]> granicus.if.org Git - php/commitdiff
adding xmlrpc extension, per Stig's request
authorDan Libby <danda@php.net>
Thu, 6 Sep 2001 04:13:30 +0000 (04:13 +0000)
committerDan Libby <danda@php.net>
Thu, 6 Sep 2001 04:13:30 +0000 (04:13 +0000)
70 files changed:
ext/rpc/xmlrpc/CREDITS [new file with mode: 0644]
ext/rpc/xmlrpc/EXPERIMENTAL [new file with mode: 0644]
ext/rpc/xmlrpc/Makefile.in [new file with mode: 0644]
ext/rpc/xmlrpc/config.m4 [new file with mode: 0644]
ext/rpc/xmlrpc/libs.mk [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/Makefile.in [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/README [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/acinclude.m4 [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/base64.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/base64.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/encodings.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/encodings.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/libs.mk [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/queue.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/queue.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/simplestring.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/simplestring.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/system_methods.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/system_methods_private.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_element.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_element.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc.m4 [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h [new file with mode: 0644]
ext/rpc/xmlrpc/php_config.h.in [new file with mode: 0644]
ext/rpc/xmlrpc/php_xmlrpc.h [new file with mode: 0644]
ext/rpc/xmlrpc/xmlrpc-epi-php.c [new file with mode: 0644]
ext/xmlrpc/CREDITS [new file with mode: 0644]
ext/xmlrpc/EXPERIMENTAL [new file with mode: 0644]
ext/xmlrpc/Makefile.in [new file with mode: 0644]
ext/xmlrpc/config.m4 [new file with mode: 0644]
ext/xmlrpc/libs.mk [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/Makefile.in [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/README [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/acinclude.m4 [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/base64.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/base64.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/encodings.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/encodings.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/libs.mk [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/queue.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/queue.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/simplestring.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/simplestring.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/system_methods.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/system_methods_private.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_element.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_element.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc.m4 [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xmlrpc_private.h [new file with mode: 0644]
ext/xmlrpc/php_config.h.in [new file with mode: 0644]
ext/xmlrpc/php_xmlrpc.h [new file with mode: 0644]
ext/xmlrpc/xmlrpc-epi-php.c [new file with mode: 0644]

diff --git a/ext/rpc/xmlrpc/CREDITS b/ext/rpc/xmlrpc/CREDITS
new file mode 100644 (file)
index 0000000..cfb14fa
--- /dev/null
@@ -0,0 +1,2 @@
+xmlrpc
+Dan Libby
diff --git a/ext/rpc/xmlrpc/EXPERIMENTAL b/ext/rpc/xmlrpc/EXPERIMENTAL
new file mode 100644 (file)
index 0000000..6443e99
--- /dev/null
@@ -0,0 +1,5 @@
+this extension is experimental,
+its functions may change their names 
+or move to extension all together 
+so do not rely to much on them 
+you have been warned!
diff --git a/ext/rpc/xmlrpc/Makefile.in b/ext/rpc/xmlrpc/Makefile.in
new file mode 100644 (file)
index 0000000..a3ba38b
--- /dev/null
@@ -0,0 +1,12 @@
+# $Id$
+
+LTLIBRARY_NAME        = libxmlrpc.la
+LTLIBRARY_SOURCES     = xmlrpc-epi-php.c
+LTLIBRARY_SHARED_NAME = xmlrpc.la
+EXTRA_INCLUDES = -I$(XMLRPC_INC_DIR)
+LTLIBRARY_LIBADD  = $(XMLRPC_LIBADD)
+LTLIBRARY_SHARED_LIBADD  = $(XMLRPC_SHARED_LIBADD)
+
+SUBDIRS = $(XMLRPC_SUBDIRS)
+
+include $(top_srcdir)/build/dynlib.mk
diff --git a/ext/rpc/xmlrpc/config.m4 b/ext/rpc/xmlrpc/config.m4
new file mode 100644 (file)
index 0000000..1880903
--- /dev/null
@@ -0,0 +1,122 @@
+
+dnl $Id$
+
+sinclude(ext/xmlrpc/libxmlrpc/acinclude.m4)
+sinclude(ext/xmlrpc/libxmlrpc/xmlrpc.m4)
+sinclude(libxmlrpc/acinclude.m4)
+sinclude(libxmlrpc/xmlrpc.m4)
+
+AC_DEFUN(XMLRPC_LIB_CHK,[
+  str="$XMLRPC_DIR/$1/libxmlrpc.*"
+  for j in `echo $str`; do
+    if test -r $j; then
+      XMLRPC_LIB_DIR=$XMLRPC_DIR/$1
+      break 2
+    fi
+  done
+])
+       
+PHP_ARG_WITH(xmlrpc, for XMLRPC-EPI support,
+[  --with-xmlrpc[=DIR]      Include XMLRPC-EPI support. DIR is the XMLRPC-EPI base
+                          directory. If unspecified, the bundled XMLRPC-EPI library
+                          will be used.], yes)
+                          
+PHP_ARG_WITH(expat-dir, libexpat dir for XMLRPC-EPI
+[  --with-expat-dir=DIR    XMLRPC-EPI: libexpat dir for XMLRPC-EPI])
+
+
+if test "$PHP_XMLRPC" != "no"; then
+  AC_DEFINE(HAVE_XMLRPC, 1, [Whether you have XMLRPC-EPI])
+  PHP_EXTENSION(xmlrpc,$ext_shared)
+
+dnl check for iconv
+  found_iconv=no
+  AC_CHECK_LIB(c, iconv_open, found_iconv=yes)
+  if test "$found_iconv" = "no"; then
+      for i in /usr /usr/local $ICONV_DIR; do
+        if test -f $i/lib/libconv.a -o -f $i/lib/libiconv.s?; then
+          PHP_ADD_LIBRARY_WITH_PATH(iconv, $i/lib, XMLRPC_SHARED_LIBADD)
+          found_iconv=yes
+        fi
+      done
+  fi
+  
+  if test "$found_iconv" = "no"; then
+    AC_MSG_ERROR(iconv not found, in order to build XMLRPC-EPI you need the iconv library)
+  fi
+  
+dnl check for expat
+  testval=no
+  for i in $PHP_EXPAT_DIR; do
+    if test -f $i/lib/libexpat.a -o -f $i/lib/libexpat.s?; then
+      AC_DEFINE(HAVE_LIBEXPAT2,1,[ ])
+      PHP_ADD_LIBRARY_WITH_PATH(expat, $i/lib, XMLRPC_SHARED_LIBADD)
+      PHP_ADD_INCLUDE($i/include)
+      testval=yes
+    fi
+  done
+
+  if test "$testval" = "no"; then
+    PHP_ADD_LIBRARY(xmlparse)
+    PHP_ADD_LIBRARY(xmltok)
+  fi
+
+fi
+
+if test "$PHP_XMLRPC" = "yes"; then
+  XMLRPC_CHECKS
+  XMLRPC_LIBADD=libxmlrpc/libxmlrpc.la
+  XMLRPC_SHARED_LIBADD=libxmlrpc/libxmlrpc.la
+  XMLRPC_SUBDIRS=libxmlrpc
+  PHP_SUBST(XMLRPC_LIBADD)
+  PHP_SUBST(XMLRPC_SUBDIRS)
+  LIB_BUILD($ext_builddir/libxmlrpc,$ext_shared,yes)
+  PHP_ADD_INCLUDE($ext_srcdir/libxmlrpc)
+  XMLRPC_MODULE_TYPE=builtin
+elif test "$PHP_XMLRPC" != "no"; then
+  for i in $PHP_XMLRPC; do
+    if test -r $i/include/xmlrpc/xmlrpc.h; then
+      XMLRPC_DIR=$i
+      XMLRPC_INC_DIR=$i/include/xmlrpc
+    elif test -r $i/include/xmlrpc.h; then
+      XMLRPC_DIR=$i
+      XMLRPC_INC_DIR=$i/include
+    fi
+  done
+
+  if test -z "$XMLRPC_DIR"; then
+    AC_MSG_ERROR(Cannot find header files under $PHP_XMLRPC)
+  fi
+
+  XMLRPC_MODULE_TYPE=external
+
+  for i in lib lib/xmlrpc; do
+    XMLRPC_LIB_CHK($i)
+  done
+
+  if test -z "$XMLRPC_LIB_DIR"; then
+    AC_MSG_ERROR(Cannot find xmlrpc library under $XMLRPC_DIR)
+  fi
+
+  if test "$PHP_ZLIB_DIR" != "no"; then
+    PHP_ADD_LIBRARY(z,, XMLRPC_SHARED_LIBADD)
+    XMLRPC_LIBS="-L$PHP_ZLIB_DIR -z"
+  fi
+
+  PHP_ADD_LIBRARY_WITH_PATH(xmlrpc, $XMLRPC_LIB_DIR, XMLRPC_SHARED_LIBADD)
+  XMLRPC_LIBS="-L$XMLRPC_LIB_DIR -lxmlrpc $XMLRPC_LIBS"
+
+  PHP_ADD_INCLUDE($XMLRPC_INC_DIR)
+  XMLRPC_INCLUDE="-I$XMLRPC_INC_DIR"
+
+else
+  XMLRPC_MODULE_TYPE=none
+fi
+
+
+PHP_SUBST(XMLRPC_SHARED_LIBADD)
+PHP_SUBST_OLD(XMLRPC_MODULE_TYPE)
+PHP_SUBST_OLD(XMLRPC_LIBS)
+PHP_SUBST_OLD(XMLRPC_INCLUDE)
+
+
diff --git a/ext/rpc/xmlrpc/libs.mk b/ext/rpc/xmlrpc/libs.mk
new file mode 100644 (file)
index 0000000..7c6e176
--- /dev/null
@@ -0,0 +1,7 @@
+include $(top_builddir)/config_vars.mk
+LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X)
+LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo)
+$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES)
+       $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD)
+
+targets = $(LTLIBRARY_NAME)
diff --git a/ext/rpc/xmlrpc/libxmlrpc/Makefile.in b/ext/rpc/xmlrpc/libxmlrpc/Makefile.in
new file mode 100644 (file)
index 0000000..44257c4
--- /dev/null
@@ -0,0 +1,9 @@
+
+LTLIBRARY_NAME    = libxmlrpc.la
+LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \
+                    xmlrpc_introspection.c encodings.c system_methods.c \
+                    xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c
+                   
+DEFS = -DVERSION="0.41"
+
+include $(top_srcdir)/build/dynlib.mk
diff --git a/ext/rpc/xmlrpc/libxmlrpc/README b/ext/rpc/xmlrpc/libxmlrpc/README
new file mode 100644 (file)
index 0000000..323edfa
--- /dev/null
@@ -0,0 +1,17 @@
+organization of this directory is moving towards this approach:
+
+<module>.h               -- public API and data types
+<module>_private.h       -- protected API and data types
+<module>.c               -- implementation and private API / types
+
+The rules are:
+.c files may include *_private.h.
+.h files may not include *_private.h
+
+This allows us to have a nicely encapsulated C api with opaque data types and private functions
+that are nonetheless shared between source files without redundant extern declarations..
+
+
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/acinclude.m4 b/ext/rpc/xmlrpc/libxmlrpc/acinclude.m4
new file mode 100644 (file)
index 0000000..9535e45
--- /dev/null
@@ -0,0 +1,38 @@
+# Local macros for automake & autoconf
+
+AC_DEFUN(XMLRPC_FUNCTION_CHECKS,[
+
+# Standard XMLRPC list
+AC_CHECK_FUNCS( \
+ strtoul strtoull snprintf \
+ strstr strpbrk strerror\
+ memcpy memmove)
+
+])
+
+AC_DEFUN(XMLRPC_HEADER_CHECKS,[
+AC_HEADER_STDC
+AC_CHECK_HEADERS(xmlparse.h xmltok.h stdlib.h strings.h string.h)
+])
+
+AC_DEFUN(XMLRPC_TYPE_CHECKS,[
+
+AC_REQUIRE([AC_C_CONST])
+AC_REQUIRE([AC_C_INLINE])
+AC_CHECK_SIZEOF(char, 1)
+
+AC_CHECK_SIZEOF(int, 4)
+AC_CHECK_SIZEOF(long, 4)
+AC_CHECK_SIZEOF(long long, 8)
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_TYPE_UID_T
+
+XMLRPC_CHECK_ULONG
+XMLRPC_CHECK_UCHAR
+XMLRPC_CHECK_UINT
+XMLRPC_CHECK_INT_8_16_32
+
+XMLRPC_TYPE_QSORT
+
+])
diff --git a/ext/rpc/xmlrpc/libxmlrpc/base64.c b/ext/rpc/xmlrpc/libxmlrpc/base64.c
new file mode 100644 (file)
index 0000000..bdbf95b
--- /dev/null
@@ -0,0 +1,192 @@
+static const char rcsid[] = "#(@) $Id$";
+
+/*
+
+          Encode or decode file as MIME base64 (RFC 1341)
+
+                           by John Walker
+                      http://www.fourmilab.ch/
+
+               This program is in the public domain.
+
+*/
+#include <stdio.h>
+
+/*  ENCODE  -- Encode binary file into base64.  */
+#include <stdlib.h>
+
+#include "base64.h"
+
+static unsigned char dtable[512];
+
+void buffer_new(struct buffer_st *b)
+{
+  b->length = 512;
+  b->data = malloc(sizeof(char)*(b->length));
+  b->data[0] = 0;
+  b->ptr = b->data;
+  b->offset = 0;
+}
+
+void buffer_add(struct buffer_st *b, char c)
+{
+  *(b->ptr++) = c;
+  b->offset++;
+  if (b->offset == b->length) {
+    b->length += 512;
+    b->data = realloc(b->data, b->length);
+    b->ptr = b->data + b->offset;
+  }
+}
+
+void buffer_delete(struct buffer_st *b)
+{
+  free(b->data);
+  b->length = 0;
+  b->offset = 0;
+  b->ptr = NULL;
+  b->data = NULL;
+}
+
+void base64_encode(struct buffer_st *b, const char *source, int length)
+{
+  int i, hiteof = 0;
+  int offset = 0;
+  int olen;
+  char *dest;
+  
+  olen = 0;
+  
+  buffer_new(b);
+  
+  /*   Fill dtable with character encodings.  */
+  
+  for (i = 0; i < 26; i++) {
+    dtable[i] = 'A' + i;
+    dtable[26 + i] = 'a' + i;
+  }
+  for (i = 0; i < 10; i++) {
+    dtable[52 + i] = '0' + i;
+  }
+  dtable[62] = '+';
+  dtable[63] = '/';
+  
+  while (!hiteof) {
+    unsigned char igroup[3], ogroup[4];
+    int c, n;
+    
+    igroup[0] = igroup[1] = igroup[2] = 0;
+    for (n = 0; n < 3; n++) {
+      c = *(source++);
+      offset++;
+      if (offset > length) {
+       hiteof = 1;
+       break;
+      }
+      igroup[n] = (unsigned char) c;
+    }
+    if (n > 0) {
+      ogroup[0] = dtable[igroup[0] >> 2];
+      ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
+      ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
+      ogroup[3] = dtable[igroup[2] & 0x3F];
+      
+      /* Replace characters in output stream with "=" pad
+        characters if fewer than three characters were
+        read from the end of the input stream. */
+      
+      if (n < 3) {
+       ogroup[3] = '=';
+       if (n < 2) {
+         ogroup[2] = '=';
+       }
+      }
+      for (i = 0; i < 4; i++) {
+       buffer_add(b, ogroup[i]);
+       if (!(b->offset % 72)) {
+         // buffer_add(b, '\r');
+         buffer_add(b, '\n');
+       }
+      }
+    }
+  }
+  // buffer_add(b, '\r');
+  buffer_add(b, '\n');
+}
+
+void base64_decode(struct buffer_st *bfr, const char *source, int length)
+{
+    int i;
+    int offset = 0;
+    int endoffile;
+    int count;
+
+    buffer_new(bfr);
+
+    for (i = 0; i < 255; i++) {
+       dtable[i] = 0x80;
+    }
+    for (i = 'A'; i <= 'Z'; i++) {
+        dtable[i] = 0 + (i - 'A');
+    }
+    for (i = 'a'; i <= 'z'; i++) {
+        dtable[i] = 26 + (i - 'a');
+    }
+    for (i = '0'; i <= '9'; i++) {
+        dtable[i] = 52 + (i - '0');
+    }
+    dtable['+'] = 62;
+    dtable['/'] = 63;
+    dtable['='] = 0;
+
+    endoffile = 0;
+
+    /*CONSTANTCONDITION*/
+    while (1) {
+       unsigned char a[4], b[4], o[3];
+
+       for (i = 0; i < 4; i++) {
+           int c;
+           while (1) {
+             c = *(source++);
+             offset++;
+             if (offset > length) endoffile = 1;
+             if (isspace(c) || c == '\n' || c == '\r') continue;
+             break;
+           }
+
+           if (endoffile) {
+             /*
+               if (i > 0) {
+                    fprintf(stderr, "Input file incomplete.\n");
+                   exit(1);
+               }
+             */
+               return;
+           }
+
+           if (dtable[c] & 0x80) {
+             /*
+             fprintf(stderr, "Offset %i length %i\n", offset, length);
+             fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]);
+             exit(1);
+             */
+             i--;
+             continue;
+           }
+           a[i] = (unsigned char) c;
+           b[i] = (unsigned char) dtable[c];
+       }
+       o[0] = (b[0] << 2) | (b[1] >> 4);
+       o[1] = (b[1] << 4) | (b[2] >> 2);
+       o[2] = (b[2] << 6) | b[3];
+        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+       count = 0;
+       while (count < i) {
+         buffer_add(bfr, o[count++]);
+       }
+       if (i < 3) {
+           return;
+       }
+    }
+}
diff --git a/ext/rpc/xmlrpc/libxmlrpc/base64.h b/ext/rpc/xmlrpc/libxmlrpc/base64.h
new file mode 100644 (file)
index 0000000..4cf156a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+
+          Encode or decode file as MIME base64 (RFC 1341)
+
+                           by John Walker
+                      http://www.fourmilab.ch/
+
+               This program is in the public domain.
+
+*/
+
+
+struct buffer_st {
+  char *data;
+  int length;
+  char *ptr;
+  int offset;
+};
+
+void buffer_new(struct buffer_st *b);
+void buffer_add(struct buffer_st *b, char c);
+void buffer_delete(struct buffer_st *b);
+
+void base64_encode(struct buffer_st *b, const char *source, int length);
+void base64_decode(struct buffer_st *b, const char *source, int length);
+
+/*
+#define DEBUG_MALLOC
+ */
+
+#ifdef DEBUG_MALLOC
+void *_malloc_real(size_t s, char *file, int line);
+void _free_real(void *p, char *file, int line);
+
+#define malloc(s)      _malloc_real(s,__FILE__,__LINE__)
+#define free(p)                _free_real(p, __FILE__,__LINE__)
+#endif
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/encodings.c b/ext/rpc/xmlrpc/libxmlrpc/encodings.c
new file mode 100644 (file)
index 0000000..544cfa2
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+#include <errno.h>
+#include <iconv.h>
+#include "encodings.h"
+
+static char* convert(const char* src, int src_len, int *new_len, const char* from_enc, const char* to_enc) {
+   char* outbuf = 0;
+
+   if(src && src_len && from_enc && to_enc) {
+      int outlenleft = src_len;
+      int outlen = src_len;
+      int inlenleft = src_len;
+      iconv_t ic = iconv_open(to_enc, from_enc);
+      char* src_ptr = (char*)src;
+      char* out_ptr = 0;
+
+      if(ic != (iconv_t)-1) {
+         size_t st;
+         outbuf = (char*)malloc(outlen + 1);
+
+         if(outbuf) {
+            out_ptr = (char*)outbuf;
+            while(inlenleft) {
+               st = iconv(ic, &src_ptr, &inlenleft, &out_ptr, &outlenleft);
+               if(st == -1) {
+                  if(errno == E2BIG) {
+                     int diff = out_ptr - outbuf;
+                     outlen += inlenleft;
+                     outlenleft += inlenleft;
+                     outbuf = (char*)realloc(outbuf, outlen + 1);
+                     if(!outbuf) {
+                        break;
+                     }
+                     out_ptr = outbuf + diff;
+                  }
+                  else {
+                     free(outbuf);
+                     outbuf = 0;
+                     break;
+                  }
+               }
+            }
+         }
+         iconv_close(ic);
+      }
+      outlen -= outlenleft;
+
+      if(new_len) {
+         *new_len = outbuf ? outlen : 0;
+      }
+      if(outbuf) {
+         outbuf[outlen] = 0;
+      }
+   }
+   return outbuf;
+}
+
+/* returns a new string that must be freed */
+char* utf8_encode(const char *s, int len, int *newlen, const char* encoding)
+{
+   return convert(s, len, newlen, encoding, "UTF-8");
+}
+
+/* returns a new string, possibly decoded */
+char* utf8_decode(const char *s, int len, int *newlen, const char* encoding)
+{
+   return convert(s, len, newlen, "UTF-8", encoding);
+}
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/encodings.h b/ext/rpc/xmlrpc/libxmlrpc/encodings.h
new file mode 100644 (file)
index 0000000..486360b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+#ifndef __ENCODINGS__H
+#define __ENCODINGS__H
+
+/* these defines are for legacy purposes. */
+#define encoding_utf_8 "UTF-8"
+typedef const char* ENCODING_ID;
+#define utf8_get_encoding_id_string(desired_enc) ((const char*)desired_enc)
+#define utf8_get_encoding_id_from_string(id_string) ((ENCODING_ID)id_string)
+
+char* utf8_encode(const char *s, int len, int *newlen, ENCODING_ID encoding);
+char* utf8_decode(const char *s, int len, int *newlen, ENCODING_ID encoding);
+
+#endif /* __ENCODINGS__H  */
diff --git a/ext/rpc/xmlrpc/libxmlrpc/libs.mk b/ext/rpc/xmlrpc/libxmlrpc/libs.mk
new file mode 100644 (file)
index 0000000..7c6e176
--- /dev/null
@@ -0,0 +1,7 @@
+include $(top_builddir)/config_vars.mk
+LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X)
+LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo)
+$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES)
+       $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD)
+
+targets = $(LTLIBRARY_NAME)
diff --git a/ext/rpc/xmlrpc/libxmlrpc/queue.c b/ext/rpc/xmlrpc/libxmlrpc/queue.c
new file mode 100644 (file)
index 0000000..25d62b9
--- /dev/null
@@ -0,0 +1,979 @@
+static const char rcsid[] = "#(@) $Id$";
+
+/* 
+ * Date last modified: Jan 2001
+ * Modifications by Dan Libby (dan@libby.com), including:
+ *  - various fixes, null checks, etc
+ *  - addition of Q_Iter funcs, macros
+ */
+
+
+/*-**************************************************************
+ *
+ *  File : q.c
+ *
+ *  Author: Peter Yard [1993.01.02] -- 02 Jan 1993
+ *
+ *  Disclaimer: This code is released to the public domain.
+ *
+ *  Description:
+ *      Generic double ended queue (Deque pronounced DEK) for handling
+ *      any data types, with sorting.
+ *
+ *      By use of various functions in this module the caller
+ *      can create stacks, queues, lists, doubly linked lists,
+ *      sorted lists, indexed lists.  All lists are dynamic.
+ *
+ *      It is the responsibility of the caller to malloc and free
+ *      memory for insertion into the queue. A pointer to the object
+ *      is used so that not only can any data be used but various kinds
+ *      of data can be pushed on the same queue if one so wished e.g.
+ *      various length string literals mixed with pointers to structures
+ *      or integers etc.
+ *
+ *  Enhancements:
+ *      A future improvement would be the option of multiple "cursors"
+ *      so that multiple locations could occur in the one queue to allow
+ *      placemarkers and additional flexibility.  Perhaps even use queue
+ *      itself to have a list of cursors.
+ *
+ * Usage:
+ *
+ *          /x init queue x/
+ *          queue  q;
+ *          Q_Init(&q);
+ *
+ *      To create a stack :
+ *
+ *          Q_PushHead(&q, &mydata1); /x push x/
+ *          Q_PushHead(&q, &mydata2);
+ *          .....
+ *          data_ptr = Q_PopHead(&q); /x pop x/
+ *          .....
+ *          data_ptr = Q_Head(&q);   /x top of stack x/
+ *
+ *      To create a FIFO:
+ *
+ *          Q_PushHead(&q, &mydata1);
+ *          .....
+ *          data_ptr = Q_PopTail(&q);
+ *
+ *      To create a double list:
+ *
+ *          data_ptr = Q_Head(&q);
+ *          ....
+ *          data_ptr = Q_Next(&q);
+ *          data_ptr = Q_Tail(&q);
+ *          if (Q_IsEmpty(&q)) ....
+ *          .....
+ *          data_ptr = Q_Previous(&q);
+ *
+ *      To create a sorted list:
+ *
+ *          Q_PushHead(&q, &mydata1); /x push x/
+ *          Q_PushHead(&q, &mydata2);
+ *          .....
+ *          if (!Q_Sort(&q, MyFunction))
+ *              .. error ..
+ *
+ *          /x fill in key field of mydata1.
+ *           * NB: Q_Find does linear search
+ *           x/
+ *
+ *          if (Q_Find(&q, &mydata1, MyFunction))
+ *          {
+ *              /x found it, queue cursor now at correct record x/
+ *              /x can retrieve with x/
+ *              data_ptr = Q_Get(&q);
+ *
+ *              /x alter data , write back with x/
+ *              Q_Put(&q, data_ptr);
+ *          }
+ *
+ *          /x Search with binary search x/
+ *          if (Q_Seek(&q, &mydata, MyFunction))
+ *              /x etc x/
+ *
+ *
+ ****************************************************************/
+
+
+#include <stdlib.h>
+#include "queue.h"
+
+
+static void QuickSort(void *list[], int low, int high,
+                      int (*Comp)(const void *, const void *));
+static int  Q_BSearch(queue *q, void *key,
+                      int (*Comp)(const void *, const void *));
+
+/* The index: a pointer to pointers */
+
+static  void        **index;
+static  datanode    **posn_index;
+
+
+/***
+ *
+ ** function    : Q_Init
+ *
+ ** purpose     : Initialise queue object and pointers.
+ *
+ ** parameters  : 'queue' pointer.
+ *
+ ** returns     : True_ if init successful else False_
+ *
+ ** comments    :
+ ***/
+
+int Q_Init(queue  *q)
+{
+   if(q) {
+      q->head = q->tail = NULL;
+      q->cursor = q->head;
+      q->size = 0;
+      q->sorted = False_;
+   }
+
+   return True_;
+}
+
+/***
+ *
+ ** function    : Q_AtHead
+ *
+ ** purpose     : tests if cursor is at head of queue
+ *
+ ** parameters  : 'queue' pointer.
+ *
+ ** returns     : boolean - True_ is at head else False_
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_AtHead(queue *q)
+{
+   return(q && q->cursor == q->head);
+}
+
+
+/***
+ *
+ ** function    : Q_AtTail
+ *
+ ** purpose     : boolean test if cursor at tail of queue
+ *
+ ** parameters  : 'queue' pointer to test.
+ *
+ ** returns     : True_ or False_
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_AtTail(queue *q)
+{
+   return(q && q->cursor == q->tail);
+}
+
+
+/***
+ *
+ ** function    : Q_IsEmpty
+ *
+ ** purpose     : test if queue has nothing in it.
+ *
+ ** parameters  : 'queue' pointer
+ *
+ ** returns     : True_ if IsEmpty queue, else False_
+ *
+ ** comments    :
+ *
+ ***/
+
+inline int Q_IsEmpty(queue *q)
+{
+   return(!q || q->size == 0);
+}
+
+/***
+ *
+ ** function    : Q_Size
+ *
+ ** purpose     : return the number of elements in the queue
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : number of elements
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_Size(queue *q)
+{
+   return q ? q->size : 0;
+}
+
+
+/***
+ *
+ ** function    : Q_Head
+ *
+ ** purpose     : position queue cursor to first element (head) of queue.
+ *
+ ** parameters  : 'queue' pointer
+ *
+ ** returns     : pointer to data at head. If queue is IsEmpty returns NULL
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_Head(queue *q)
+{
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   q->cursor = q->head;
+
+   return  q->cursor->data;
+}
+
+
+/***
+ *
+ ** function    : Q_Tail
+ *
+ ** purpose     : locate cursor at tail of queue.
+ *
+ ** parameters  : 'queue' pointer
+ *
+ ** returns     : pointer to data at tail , if queue IsEmpty returns NULL
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_Tail(queue *q)
+{
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   q->cursor = q->tail;
+
+   return  q->cursor->data;
+}
+
+
+/***
+ *
+ ** function    : Q_PushHead
+ *
+ ** purpose     : put a data pointer at the head of the queue
+ *
+ ** parameters  : 'queue' pointer, void pointer to the data.
+ *
+ ** returns     : True_ if success else False_ if unable to push data.
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_PushHead(queue *q, void *d)
+{
+   if(q && d) {
+      node    *n;
+      datanode *p;
+
+      p = malloc(sizeof(datanode));
+      if(p == NULL)
+         return False_;
+
+      n = q->head;
+
+      q->head = (node*)p;
+      q->head->prev = NULL;
+
+      if(q->size == 0) {
+         q->head->next = NULL;
+         q->tail = q->head;
+      }
+      else {
+         q->head->next = (datanode*)n;
+         n->prev = q->head;
+      }
+
+      q->head->data = d;
+      q->size++;
+
+      q->cursor = q->head;
+
+      q->sorted = False_;
+
+      return True_;
+   }
+   return False_;
+}
+
+
+
+/***
+ *
+ ** function    : Q_PushTail
+ *
+ ** purpose     : put a data element pointer at the tail of the queue
+ *
+ ** parameters  : queue pointer, pointer to the data
+ *
+ ** returns     : True_ if data pushed, False_ if data not inserted.
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_PushTail(queue *q, void *d)
+{
+   if(q && d) {
+      node        *p;
+      datanode    *n;
+
+      n = malloc(sizeof(datanode));
+      if(n == NULL)
+         return False_;
+
+      p = q->tail;
+      q->tail = (node *)n;
+
+      if(q->size == 0) {
+         q->tail->prev = NULL;
+         q->head = q->tail;
+      }
+      else {
+         q->tail->prev = (datanode *)p;
+         p->next = q->tail;
+      }
+
+      q->tail->next = NULL;
+
+      q->tail->data =  d;
+      q->cursor = q->tail;
+      q->size++;
+
+      q->sorted = False_;
+
+      return True_;
+   }
+}
+
+
+
+/***
+ *
+ ** function    : Q_PopHead
+ *
+ ** purpose     : remove and return the top element at the head of the
+ *                queue.
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : pointer to data element or NULL if queue is IsEmpty.
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_PopHead(queue *q)
+{
+   datanode    *n;
+   void        *d;
+
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   d = q->head->data;
+   n = q->head->next;
+   free(q->head);
+
+   q->size--;
+
+   if(q->size == 0)
+      q->head = q->tail = q->cursor = NULL;
+   else {
+      q->head = (node *)n;
+      q->head->prev = NULL;
+      q->cursor = q->head;
+   }
+
+   q->sorted = False_;
+
+   return d;
+}
+
+
+/***
+ *
+ ** function    : Q_PopTail
+ *
+ ** purpose     : remove element from tail of queue and return data.
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : pointer to data element that was at tail. NULL if queue
+ *                IsEmpty.
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_PopTail(queue *q)
+{
+   datanode    *p;
+   void        *d;
+
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   d = q->tail->data;
+   p = q->tail->prev;
+   free(q->tail);
+   q->size--;
+
+   if(q->size == 0)
+      q->head = q->tail = q->cursor = NULL;
+   else {
+      q->tail = (node *)p;
+      q->tail->next = NULL;
+      q->cursor = q->tail;
+   }
+
+   q->sorted = False_;
+
+   return d;
+}
+
+
+
+/***
+ *
+ ** function    : Q_Next
+ *
+ ** purpose     : Move to the next element in the queue without popping
+ *
+ ** parameters  : queue pointer.
+ *
+ ** returns     : pointer to data element of new element or NULL if end
+ *                of the queue.
+ *
+ ** comments    : This uses the cursor for the current position. Q_Next
+ *                only moves in the direction from the head of the queue
+ *                to the tail.
+ ***/
+
+void *Q_Next(queue *q)
+{
+   if(!q)
+      return NULL;
+
+   if(q->cursor->next == NULL)
+      return NULL;
+
+   q->cursor = (node *)q->cursor->next;
+
+   return  q->cursor->data ;
+}
+
+
+
+/***
+ *
+ ** function    : Q_Previous
+ *
+ ** purpose     : Opposite of Q_Next. Move to next element closer to the
+ *                head of the queue.
+ *
+ ** parameters  : pointer to queue
+ *
+ ** returns     : pointer to data of new element else NULL if queue IsEmpty
+ *
+ ** comments    : Makes cursor move towards the head of the queue.
+ *
+ ***/
+
+void *Q_Previous(queue *q)
+{
+   if(!q)
+      return NULL;
+   
+   if(q->cursor->prev == NULL)
+      return NULL;
+
+   q->cursor = (node *)q->cursor->prev;
+
+   return q->cursor->data;
+}
+
+
+void *Q_Iter_Del(queue *q, q_iter iter)
+{
+   void        *d;
+   datanode    *n, *p;
+
+   if(!q)
+      return NULL;
+
+   if(iter == NULL)
+      return NULL;
+
+   if(iter == (q_iter)q->head)
+      return Q_PopHead(q);
+
+   if(iter == (q_iter)q->tail)
+      return Q_PopTail(q);
+
+   n = ((node*)iter)->next;
+   p = ((node*)iter)->prev;
+   d = ((node*)iter)->data;
+
+   free(iter);
+
+   if(p) {
+      p->next = n;
+   }
+   if (q->cursor == (node*)iter) {
+      if (p) {
+         q->cursor = p;
+      } else {
+         q->cursor = n;
+      }
+   }
+
+
+   if (n != NULL) {
+       n->prev = p;
+   }
+
+   q->size--;
+
+   q->sorted = False_;
+
+   return d;
+}
+
+
+
+/***
+ *
+ ** function    : Q_DelCur
+ *
+ ** purpose     : Delete the current queue element as pointed to by
+ *                the cursor.
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : pointer to data element.
+ *
+ ** comments    : WARNING! It is the responsibility of the caller to
+ *                free any memory. Queue cannot distinguish between
+ *                pointers to literals and malloced memory.
+ *
+ ***/
+
+void *Q_DelCur(queue* q) {
+   if(q) {
+      return Q_Iter_Del(q, (q_iter)q->cursor);
+   }
+   return 0;
+}
+
+
+/***
+ *
+ ** function    : Q_Destroy
+ *
+ ** purpose     : Free all queue resources
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : null.
+ *
+ ** comments    : WARNING! It is the responsibility of the caller to
+ *                free any memory. Queue cannot distinguish between
+ *                pointers to literals and malloced memory.
+ *
+ ***/
+
+void Q_Destroy(queue *q)
+{
+   while(!Q_IsEmpty(q)) {
+      Q_PopHead(q);
+   }
+}
+
+
+/***
+ *
+ ** function    : Q_Get
+ *
+ ** purpose     : get the pointer to the data at the cursor location
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : data element pointer
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_Get(queue *q)
+{
+   if(!q)
+      return NULL;
+
+   if(q->cursor == NULL)
+      return NULL;
+   return q->cursor->data;
+}
+
+
+
+/***
+ *
+ ** function    : Q_Put
+ *
+ ** purpose     : replace pointer to data with new pointer to data.
+ *
+ ** parameters  : queue pointer, data pointer
+ *
+ ** returns     : boolean- True_ if successful, False_ if cursor at NULL
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_Put(queue *q, void *data)
+{
+   if(q && data) {
+      if(q->cursor == NULL)
+         return False_;
+
+      q->cursor->data = data;
+      return True_;
+   }
+   return False_;
+}
+
+
+/***
+ *
+ ** function    : Q_Find
+ *
+ ** purpose     : Linear search of queue for match with key in *data
+ *
+ ** parameters  : queue pointer q, data pointer with data containing key
+ *                comparison function here called Comp.
+ *
+ ** returns     : True_ if found , False_ if not in queue.
+ *
+ ** comments    : Useful for small queues that are constantly changing
+ *                and would otherwise need constant sorting with the
+ *                Q_Seek function.
+ *                For description of Comp see Q_Sort.
+ *                Queue cursor left on position found item else at end.
+ *
+ ***/
+
+int Q_Find(queue *q, void *data,
+           int (*Comp)(const void *, const void *))
+{
+   void *d;
+
+   if (q == NULL) {
+       return False_;
+   }
+
+   d = Q_Head(q);
+   do {
+      if(Comp(d, data) == 0)
+         return True_;
+      d = Q_Next(q);
+   } while(!Q_AtTail(q));
+
+   if(Comp(d, data) == 0)
+      return True_;
+
+   return False_;
+}
+
+/*========  Sorted Queue and Index functions   ========= */
+
+
+static void QuickSort(void *list[], int low, int high,
+                      int (*Comp)(const void *, const void *))
+{
+   int     flag = 1, i, j;
+   void    *key, *temp;
+
+   if(low < high) {
+      i = low;
+      j = high + 1;
+
+      key = list[ low ];
+
+      while(flag) {
+         i++;
+         while(Comp(list[i], key) < 0)
+            i++;
+
+         j--;
+         while(Comp(list[j], key) > 0)
+            j--;
+
+         if(i < j) {
+            temp = list[i];
+            list[i] = list[j];
+            list[j] = temp;
+         }
+         else  flag = 0;
+      }
+
+      temp = list[low];
+      list[low] = list[j];
+      list[j] = temp;
+
+      QuickSort(list, low, j-1, Comp);
+      QuickSort(list, j+1, high, Comp);
+   }
+}
+
+
+/***
+ *
+ ** function    : Q_Sort
+ *
+ ** purpose     : sort the queue and allow index style access.
+ *
+ ** parameters  : queue pointer, comparison function compatible with
+ *                with 'qsort'.
+ *
+ ** returns     : True_ if sort succeeded. False_ if error occurred.
+ *
+ ** comments    : Comp function supplied by caller must return
+ *                  -1 if data1  < data2
+ *                   0 if data1 == data2
+ *                  +1 if data1  > data2
+ *
+ *                    for Comp(data1, data2)
+ *
+ *                If queue is already sorted it frees the memory of the
+ *                old index and starts again.
+ *
+ ***/
+
+int Q_Sort(queue *q, int (*Comp)(const void *, const void *))
+{
+   int         i;
+   void        *d;
+   datanode    *dn;
+
+   /* if already sorted free memory for tag array */
+
+   if(q->sorted) {
+      free(index);
+      free(posn_index);
+      q->sorted = False_;
+   }
+
+   /* Now allocate memory of array, array of pointers */
+
+   index = malloc(q->size * sizeof(q->cursor->data));
+   if(index == NULL)
+      return False_;
+
+   posn_index = malloc(q->size * sizeof(q->cursor));
+   if(posn_index == NULL) {
+      free(index);
+      return False_;
+   }
+
+   /* Walk queue putting pointers into array */
+
+   d = Q_Head(q);
+   for(i=0; i < q->size; i++) {
+      index[i] = d;
+      posn_index[i] = q->cursor;
+      d = Q_Next(q);
+   }
+
+   /* Now sort the index */
+
+   QuickSort(index, 0, q->size - 1, Comp);
+
+   /* Rearrange the actual queue into correct order */
+
+   dn = q->head;
+   i = 0;
+   while(dn != NULL) {
+      dn->data = index[i++];
+      dn = dn->next;
+   }
+
+   /* Re-position to original element */
+
+   if(d != NULL)
+      Q_Find(q, d, Comp);
+   else  Q_Head(q);
+
+   q->sorted = True_;
+
+   return True_;
+}
+
+
+/***
+ *
+ ** function    : Q_BSearch
+ *
+ ** purpose     : binary search of queue index for node containing key
+ *
+ ** parameters  : queue pointer 'q', data pointer of key 'key',
+ *                  Comp comparison function.
+ *
+ ** returns     : integer index into array of node pointers,
+ *                or -1 if not found.
+ *
+ ** comments    : see Q_Sort for description of 'Comp' function.
+ *
+ ***/
+
+static int Q_BSearch( queue *q, void *key,
+                      int (*Comp)(const void *, const void*))
+{
+   int low, mid, hi, val;
+
+   low = 0;
+   hi = q->size - 1;
+
+   while(low <= hi) {
+      mid = (low + hi) / 2;
+      val = Comp(key, index[ mid ]);
+
+      if(val < 0)
+         hi = mid - 1;
+
+      else if(val > 0)
+         low = mid + 1;
+
+      else /* Success */
+         return mid;
+   }
+
+   /* Not Found */
+
+   return -1;
+}
+
+
+/***
+ *
+ ** function    : Q_Seek
+ *
+ ** purpose     : use index to locate data according to key in 'data'
+ *
+ ** parameters  : queue pointer 'q', data pointer 'data', Comp comparison
+ *                function.
+ *
+ ** returns     : pointer to data or NULL if could not find it or could
+ *                not sort queue.
+ *
+ ** comments    : see Q_Sort for description of 'Comp' function.
+ *
+ ***/
+
+void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *))
+{
+   int idx;
+
+   if (q == NULL) {
+       return NULL;
+   }
+
+   if(!q->sorted) {
+      if(!Q_Sort(q, Comp))
+         return NULL;
+   }
+
+   idx = Q_BSearch(q, data, Comp);
+
+   if(idx < 0)
+      return NULL;
+
+   q->cursor = posn_index[idx];
+
+   return index[idx];
+}
+
+
+
+/***
+ *
+ ** function    : Q_Insert
+ *
+ ** purpose     : Insert an element into an indexed queue
+ *
+ ** parameters  : queue pointer 'q', data pointer 'data', Comp comparison
+ *                function.
+ *
+ ** returns     : pointer to data or NULL if could not find it or could
+ *                not sort queue.
+ *
+ ** comments    : see Q_Sort for description of 'Comp' function.
+ *                WARNING! This code can be very slow since each new
+ *                element means a new Q_Sort.  Should only be used for
+ *                the insertion of the odd element ,not the piecemeal
+ *                building of an entire queue.
+ ***/
+
+int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *))
+{
+   if (q == NULL) {
+       return False_;
+   }
+
+   Q_PushHead(q, data);
+
+   if(!Q_Sort(q, Comp))
+      return False_;
+
+   return True_;
+}
+
+/* read only funcs for iterating through queue. above funcs modify queue */
+q_iter Q_Iter_Head(queue *q) {
+   return q ? (q_iter)q->head : NULL;
+}
+
+q_iter Q_Iter_Tail(queue *q) {
+   return q ? (q_iter)q->tail : NULL;
+}
+
+q_iter Q_Iter_Next(q_iter qi) {
+   return qi ? (q_iter)((node*)qi)->next : NULL;
+}
+
+q_iter Q_Iter_Prev(q_iter qi) {
+   return qi ? (q_iter)((node*)qi)->prev : NULL;
+}
+
+void * Q_Iter_Get(q_iter qi) {
+   return qi ? ((node*)qi)->data : NULL;
+}
+
+int Q_Iter_Put(q_iter qi, void* data) {
+   if(qi) {
+      ((node*)qi)->data = data;
+      return True_;
+   }
+   return False_;
+}
diff --git a/ext/rpc/xmlrpc/libxmlrpc/queue.h b/ext/rpc/xmlrpc/libxmlrpc/queue.h
new file mode 100644 (file)
index 0000000..e850b57
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+ * Date last modified: Jan 2001
+ * Modifications by Dan Libby (dan@libby.com), including:
+ *  - various fixes, null checks, etc
+ *  - addition of Q_Iter funcs, macros
+ */
+
+/*
+ *  File : q.h
+ *
+ *  Peter Yard  02 Jan 1993.
+ *
+ *  Disclaimer: This code is released to the public domain.
+ */
+
+#ifndef Q__H
+#define Q__H
+
+#ifndef False_
+   #define False_ 0
+#endif
+
+#ifndef True_
+   #define True_ 1
+#endif
+
+typedef struct nodeptr datanode;
+
+typedef struct nodeptr {
+   void        *data ;
+   datanode    *prev, *next ;
+} node ;
+
+/* For external use with Q_Iter* funcs */
+typedef struct nodeptr* q_iter;
+
+typedef struct {
+   node        *head, *tail, *cursor;
+   int         size, sorted, item_deleted;
+} queue;
+
+typedef  struct {
+   void        *dataptr;
+   node        *loc ;
+} index_elt ;
+
+
+int    Q_Init(queue  *q);
+void   Q_Destroy(queue *q);
+int    Q_IsEmpty(queue *q);
+int    Q_Size(queue *q);
+int    Q_AtHead(queue *q);
+int    Q_AtTail(queue *q);
+int    Q_PushHead(queue *q, void *d);
+int    Q_PushTail(queue *q, void *d);
+void  *Q_Head(queue *q);
+void  *Q_Tail(queue *q);
+void  *Q_PopHead(queue *q);
+void  *Q_PopTail(queue *q);
+void  *Q_Next(queue *q);
+void  *Q_Previous(queue *q);
+void  *Q_DelCur(queue *q);
+void  *Q_Get(queue *q);
+int    Q_Put(queue *q, void *data);
+int    Q_Sort(queue *q, int (*Comp)(const void *, const void *));
+int    Q_Find(queue *q, void *data,
+              int (*Comp)(const void *, const void *));
+void  *Q_Seek(queue *q, void *data,
+              int (*Comp)(const void *, const void *));
+int    Q_Insert(queue *q, void *data,
+                int (*Comp)(const void *, const void *));
+
+/* read only funcs for iterating through queue. above funcs modify queue */
+q_iter Q_Iter_Head(queue *q);
+q_iter Q_Iter_Tail(queue *q);
+q_iter Q_Iter_Next(q_iter qi);
+q_iter Q_Iter_Prev(q_iter qi);
+void*  Q_Iter_Get(q_iter qi);
+int    Q_Iter_Put(q_iter qi, void* data); // not read only! here for completeness.
+void*  Q_Iter_Del(queue *q, q_iter iter); // not read only! here for completeness.
+
+/* Fast (macro'd) versions of above */
+#define Q_Iter_Head_F(q) (q ? (q_iter)((queue*)q)->head : NULL)
+#define Q_Iter_Tail_F(q) (q ? (q_iter)((queue*)q)->tail : NULL)
+#define Q_Iter_Next_F(qi) (qi ? (q_iter)((node*)qi)->next : NULL)
+#define Q_Iter_Prev_F(qi) (qi ? (q_iter)((node*)qi)->prev : NULL)
+#define Q_Iter_Get_F(qi) (qi ? ((node*)qi)->data : NULL)
+
+#endif /* Q__H */
diff --git a/ext/rpc/xmlrpc/libxmlrpc/simplestring.c b/ext/rpc/xmlrpc/libxmlrpc/simplestring.c
new file mode 100644 (file)
index 0000000..7ce68d3
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+
+#define SIMPLESTRING_INCR 32
+
+/****h* ABOUT/simplestring
+ * NAME
+ *   simplestring
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * CREATION DATE
+ *   06/2000
+ * HISTORY
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ * PORTABILITY
+ *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
+ *   about anything with minor mods.
+ * NOTES
+ *   This code was written primarily for xmlrpc, but has found some other uses.
+ *
+ *   simplestring is, as the name implies, a simple API for dealing with C strings.
+ *   Why would I write yet another string API?  Because I couldn't find any that were
+ *   a) free / GPL, b) simple/lightweight, c) fast, not doing unneccesary strlens all
+ *   over the place.  So.  It is simple, and it seems to work, and it is pretty fast.
+ *
+ *   Oh, and it is also binary safe, ie it can handle strings with embedded NULLs,
+ *   so long as the real length is passed in.
+ *   
+ *   And the masses rejoiced.
+ *
+ * BUGS
+ *   there must be some.
+ ******/
+
+
+#include "simplestring.h"
+
+#define my_free(thing)  if(thing) {free(thing); thing = 0;}
+
+/*----------------------**
+* Begin String Functions *
+*-----------------------*/
+
+/****f* FUNC/simplestring_init
+ * NAME
+ *   simplestring_init
+ * SYNOPSIS
+ *   void simplestring_init(simplestring* string)
+ * FUNCTION
+ *   initialize string
+ * INPUTS
+ *   string - pointer to a simplestring struct that will be initialized
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   simplestring_free ()
+ *   simplestring_clear ()
+ * SOURCE
+ */
+void simplestring_init(simplestring* string) {
+   memset(string, 0, sizeof(simplestring));
+}
+/******/
+
+static void simplestring_init_str(simplestring* string) {
+   string->str = (char*)malloc(SIMPLESTRING_INCR);
+   if(string->str) {
+      string->str[0] = 0;
+      string->len = 0;
+      string->size = SIMPLESTRING_INCR;
+   }
+   else {
+      string->size = 0;
+   }
+}
+
+/****f* FUNC/simplestring_clear
+ * NAME
+ *   simplestring_clear
+ * SYNOPSIS
+ *   void simplestring_clear(simplestring* string)
+ * FUNCTION
+ *   clears contents of a string
+ * INPUTS
+ *   string - the string value to clear
+ * RESULT
+ *   void
+ * NOTES
+ *   This function is very fast as it does not de-allocate any memory.
+ * SEE ALSO
+ * 
+ * SOURCE
+ */
+void simplestring_clear(simplestring* string) {
+   if(string->str) {
+      string->str[0] = 0;
+   }
+   string->len = 0;
+}
+/******/
+
+/****f* FUNC/simplestring_free
+ * NAME
+ *   simplestring_free
+ * SYNOPSIS
+ *   void simplestring_free(simplestring* string)
+ * FUNCTION
+ *   frees contents of a string, if any. Does *not* free the simplestring struct itself.
+ * INPUTS
+ *   string - value containing string to be free'd
+ * RESULT
+ *   void
+ * NOTES
+ *   caller is responsible for allocating and freeing simplestring* struct itself.
+ * SEE ALSO
+ *   simplestring_init ()
+ * SOURCE
+ */
+void simplestring_free(simplestring* string) {
+   if(string && string->str) {
+      my_free(string->str);
+      string->len = 0;
+   }
+}
+/******/
+
+/****f* FUNC/simplestring_addn
+ * NAME
+ *   simplestring_addn
+ * SYNOPSIS
+ *   void simplestring_addn(simplestring* string, const char* add, int add_len)
+ * FUNCTION
+ *   copies n characters from source to target string
+ * INPUTS
+ *   target  - target string
+ *   source  - source string
+ *   add_len - number of characters to copy
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   simplestring_add ()
+ * SOURCE
+ */
+void simplestring_addn(simplestring* target, const char* source, int add_len) {
+   if(target && source) {
+      if(!target->str) {
+         simplestring_init_str(target);
+      }
+      if(target->len + add_len + 1 > target->size) {
+         /* newsize is current length + new length */
+         int newsize = target->len + add_len + 1;
+         int incr = target->size * 2;
+
+         /* align to SIMPLESTRING_INCR increments */
+         newsize = newsize - (newsize % incr) + incr;
+         target->str = (char*)realloc(target->str, newsize);
+
+         target->size = target->str ? newsize : 0;
+      }
+
+      if(target->str) {
+         if(add_len) {
+            memcpy(target->str + target->len, source, add_len);
+         }
+         target->len += add_len;
+         target->str[target->len] = 0; /* null terminate */
+      }
+   }
+}
+/******/
+
+/****f* FUNC/simplestring_add
+ * NAME
+ *   simplestring_add
+ * SYNOPSIS
+ *   void simplestring_add(simplestring* string, const char* add)
+ * FUNCTION
+ *   appends a string of unknown length from source to target
+ * INPUTS
+ *   target - the target string to append to
+ *   source - the source string of unknown length
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   simplestring_addn ()
+ * SOURCE
+ */
+void simplestring_add(simplestring* target, const char* source) {
+   if(target && source) {
+      simplestring_addn(target, source, strlen(source));
+   }
+}
+/******/
+
+
+/*----------------------
+* End String Functions *
+*--------------------**/
diff --git a/ext/rpc/xmlrpc/libxmlrpc/simplestring.h b/ext/rpc/xmlrpc/libxmlrpc/simplestring.h
new file mode 100644 (file)
index 0000000..a891ba6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef __SIMPLESTRING_H__
+ #define __SIMPLESTRING_H__
+
+/*-********************************
+* begin simplestring header stuff *
+**********************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+   /****s* struct/simplestring
+ * NAME
+ *  simplestring
+ * NOTES
+ *   represents a string efficiently for fast appending, etc.
+ * SOURCE
+ */
+typedef struct _simplestring {
+   char* str;         /* string buf               */
+   int len;           /* length of string/buf     */
+   int size;          /* size of allocated buffer */
+} simplestring;
+/******/
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+void simplestring_init(simplestring* string);
+void simplestring_clear(simplestring* string);
+void simplestring_free(simplestring* string);
+void simplestring_addn(simplestring* string, const char* add, int add_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*-******************************
+* end simplestring header stuff *
+********************************/
+
+#endif /* __SIMPLESTRING_H__ */
diff --git a/ext/rpc/xmlrpc/libxmlrpc/system_methods.c b/ext/rpc/xmlrpc/libxmlrpc/system_methods.c
new file mode 100644 (file)
index 0000000..742b837
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+/****h* ABOUT/system_methods
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * HISTORY
+ *   4/28/2001 -- danda -- adding system.multicall and separating out system methods.
+ * TODO
+ * NOTES
+ *******/
+
+
+#include "queue.h"
+#include "xmlrpc.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+#include "system_methods_private.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+static const char* xsm_introspection_xml =
+"<?xml version='1.0' ?>"
+
+"<introspection version='1.0'>"
+ "<typeList>"
+
+ "<typeDescription name='system.value' basetype='struct' desc='description of a value'>"
+   "<value type='string' name='name' optional='yes'>value identifier</value>"
+   "<value type='string' name='type'>value&apos;s xmlrpc or user-defined type</value>"
+   "<value type='string' name='description'>value&apos;s textual description</value> "
+   "<value type='boolean' name='optional'>true if value is optional, else it is required</value> "
+   "<value type='any' name='member' optional='yes'>a child of this element. n/a for scalar types</value> "
+ "</typeDescription>"
+
+ "<typeDescription name='system.valueList' basetype='array' desc='list of value descriptions'>"
+   "<value type='system.value'/>"
+ "</typeDescription>"
+
+ "<typeDescription name='system.stringList' basetype='array' desc='list of strings'>"
+   "<value type='string'/>"
+ "</typeDescription>"
+
+
+ "</typeList>"
+
+ "<methodList>"
+
+ "<!-- system.describeMethods -->"
+ "<methodDescription name='system.describeMethods'>"
+  "<author>Dan Libby</author>"
+  "<purpose>fully describes the methods and types implemented by this XML-RPC server.</purpose>"
+  "<version>1.1</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='array' name='methodList' optional='yes' desc='a list of methods to be described. if omitted, all are described.'>"
+      "<value type='string'>a valid method name</value>"
+     "</value>"
+    "</params>"
+    "<returns>"
+     "<value type='struct' desc='contains methods list and types list'>"
+      "<value type='array' name='methodList' desc='a list of methods'>"
+       "<value type='struct' desc='representation of a single method'>"
+        "<value type='string' name='name'>method name</value>"
+        "<value type='string' name='version' optional='yes'>method version</value>"
+        "<value type='string' name='author' optional='yes'>method author</value>"
+        "<value type='string' name='purpose' optional='yes'>method purpose</value>"
+        "<value type='array' name='signatures' desc='list of method signatures'>"
+         "<value type='struct' desc='representation of a single signature'>"
+          "<value type='system.valueList' name='params' optional='yes'>parameter list</value>"
+          "<value type='system.valueList' name='returns' optional='yes'>return value list</value>"
+         "</value>"
+        "</value>"
+        "<value type='system.stringList' name='bugs' optional='yes'>list of known bugs</value>"
+        "<value type='system.stringList' name='errors' optional='yes'>list of possible errors and error codes</value>"
+        "<value type='system.stringList' name='examples' optional='yes'>list of examples</value>"
+        "<value type='system.stringList' name='history' optional='yes'>list of modifications</value>"
+        "<value type='system.stringList' name='notes' optional='yes'>list of notes</value>"
+        "<value type='system.stringList' name='see' optional='yes'>see also.  list of related methods</value>"
+        "<value type='system.stringList' name='todo' optional='yes'>list of unimplemented features</value>"
+       "</value>"
+      "</value>"
+      "<value type='array' name='typeList' desc='a list of type descriptions. Typically used for referencing complex types'>"
+       "<value type='system.value'>a type description</value>"
+      "</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodSignature' />"
+   "<item name='system.methodHelp' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.listMethods -->"
+ "<methodDescription name='system.listMethods'>"
+  "<author>Dan Libby</author>"
+  "<purpose>enumerates the methods implemented by this XML-RPC server.</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<returns>"
+     "<value type='array' desc='an array of strings'>"
+      "<value type='string'>name of a method implemented by the server.</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.describeMethods' />"
+   "<item name='system.methodSignature' />"
+   "<item name='system.methodHelp' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.methodHelp -->"
+ "<methodDescription name='system.methodHelp'>"
+  "<author>Dan Libby</author>"
+  "<purpose>provides documentation string for a single method</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='string' name='methodName'>name of the method for which documentation is desired</value>"
+    "</params>"
+    "<returns>"
+     "<value type='string'>help text if defined for the method passed, otherwise an empty string</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodSignature' />"
+   "<item name='system.methodHelp' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.methodSignature -->"
+ "<methodDescription name='system.methodSignature'>"
+  "<author>Dan Libby</author>"
+  "<purpose>provides 1 or more signatures for a single method</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='string' name='methodName'>name of the method for which documentation is desired</value>"
+    "</params>"
+    "<returns>"
+     "<value type='array' desc='a list of arrays, each representing a signature'>"
+      "<value type='array' desc='a list of strings. the first element represents the method return value. subsequent elements represent parameters.'>"
+       "<value type='string'>a string indicating the xmlrpc type of a value. one of: string, int, double, base64, datetime, array, struct</value>"
+      "</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodHelp' />"
+   "<item name='system.describeMethods' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.multiCall -->"
+ "<methodDescription name='system.multiCall'>"
+  "<author>Dan Libby</author>"
+  "<purpose>executes multiple methods in sequence and returns the results</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='array' name='methodList' desc='an array of method call structs'>"
+      "<value type='struct' desc='a struct representing a single method call'>"
+       "<value type='string' name='methodName' desc='name of the method to be executed'/>"
+       "<value type='array' name='params' desc='an array representing the params to a method. sub-elements should match method signature'/>"
+      "</value>"
+     "</value>"
+    "</params>"
+    "<returns>"
+     "<value type='array' desc='an array of method responses'>"
+      "<value type='array' desc='an array containing a single value, which is the method&apos;s response'/>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodHelp' />"
+   "<item name='system.describeMethods' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.getCapabilities -->"
+ "<methodDescription name='system.getCapabilities'>"
+  "<author>Dan Libby</author>"
+  "<purpose>returns a list of capabilities supported by this server</purpose>"
+  "<version>1.0</version>"
+  "<notes><item>spec url: http://groups.yahoo.com/group/xml-rpc/message/2897</item></notes>"
+  "<signatures>"
+   "<signature>"
+    "<returns>"
+     "<value type='struct' desc='list of capabilities, each with a unique key defined by the capability&apos;s spec'>"
+      "<value type='struct' desc='definition of a single capability'>"
+       "<value type='string' name='specURL'>www address of the specification defining this capability</value>"
+       "<value type='int' name='specVersion'>version of the spec that this server's implementation conforms to</value>"
+      "</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodHelp' />"
+   "<item name='system.describeMethods' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "</methodList>"
+"</introspection>";
+
+
+/* forward declarations for static (non public, non api) funcs */
+static XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+
+/*-*******************
+* System Methods API *
+*********************/
+
+static void xsm_lazy_doc_methods_cb(XMLRPC_SERVER server, void* userData) {
+   XMLRPC_VALUE xDesc = XMLRPC_IntrospectionCreateDescription(xsm_introspection_xml, NULL);
+   XMLRPC_ServerAddIntrospectionData(server, xDesc);
+   XMLRPC_CleanupValue(xDesc);
+}
+
+void xsm_register(XMLRPC_SERVER server) {
+   xi_register_system_methods(server);
+
+   XMLRPC_ServerRegisterMethod(server, xsm_token_system_multicall, xsm_system_multicall_cb);
+   XMLRPC_ServerRegisterMethod(server, xsm_token_system_get_capabilities, xsm_system_get_capabilities_cb);
+
+   /* callback for documentation generation should it be requested */
+   XMLRPC_ServerRegisterIntrospectionCallback(server, xsm_lazy_doc_methods_cb);
+}
+
+XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xArray = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
+   XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_array);
+
+   if (xArray) {
+      XMLRPC_VALUE xMethodIter = XMLRPC_VectorRewind(xArray);
+
+      while (xMethodIter) {
+         XMLRPC_REQUEST request = XMLRPC_RequestNew();
+         if(request) {
+            const char* methodName = XMLRPC_VectorGetStringWithID(xMethodIter, "methodName");
+            XMLRPC_VALUE params = XMLRPC_VectorGetValueWithID(xMethodIter, "params");
+
+            if(methodName && params) {
+               XMLRPC_VALUE xRandomArray = XMLRPC_CreateVector(0, xmlrpc_vector_array);
+               XMLRPC_RequestSetMethodName(request, methodName);
+               XMLRPC_RequestSetData(request, params);
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+
+               XMLRPC_AddValueToVector(xRandomArray, 
+                                       XMLRPC_ServerCallMethod(server, request, userData));
+
+               XMLRPC_AddValueToVector(xReturn, xRandomArray);
+            }
+            XMLRPC_RequestFree(request, 1);
+         }
+         xMethodIter = XMLRPC_VectorNext(xArray);
+      }
+   }
+   return xReturn;
+}
+
+
+XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_struct);
+   XMLRPC_VALUE xFaults = XMLRPC_CreateVector("faults_interop", xmlrpc_vector_struct);
+   XMLRPC_VALUE xIntro = XMLRPC_CreateVector("introspection", xmlrpc_vector_struct);
+
+   /* support for fault spec */
+   XMLRPC_VectorAppendString(xFaults, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php", 0);
+   XMLRPC_VectorAppendInt(xFaults, "specVersion", 20010516);
+
+   /* support for introspection spec */
+   XMLRPC_VectorAppendString(xIntro, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.introspection.php", 0);
+   XMLRPC_VectorAppendInt(xIntro, "specVersion", 20010516);
+
+   XMLRPC_AddValuesToVector(xReturn,
+                            xFaults,
+                            xIntro,
+                            NULL);
+
+   return xReturn;
+                            
+}
+
+/*-***********************
+* End System Methods API *
+*************************/
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/system_methods_private.h b/ext/rpc/xmlrpc/libxmlrpc/system_methods_private.h
new file mode 100644 (file)
index 0000000..72408fd
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Dan Libby, Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* IMPORTANT!
+ *
+ * only non-public things should be in this file.  It is fine for any .c file
+ * in xmlrpc/src to include it, but users of the public API should never
+ * include it, and thus *.h files that are part of the public API should
+ * never include it, or they would break if this file is not present.
+ */
+
+
+#ifndef __SYSTEM_METHODS_PRIVATE_H
+/*
+ * Avoid include redundancy.
+ */
+#define __SYSTEM_METHODS_PRIVATE_H
+
+/*----------------------------------------------------------------------------
+ * system_methods_private.h
+ *
+ * Purpose:
+ *   define non-public system.* methods
+ * Comments:
+ *   xsm = xmlrpc system methods
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+#define xsm_token_system_multicall "system.multiCall"
+#define xsm_token_system_get_capabilities "system.getCapabilities"
+
+
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+void xsm_register(XMLRPC_SERVER server);
+int xsm_is_system_method(XMLRPC_Callback cb);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+
+#endif /* __SYSTEM_METHODS_PRIVATE_H */
+
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_element.c b/ext/rpc/xmlrpc/libxmlrpc/xml_element.c
new file mode 100644 (file)
index 0000000..255c0c5
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+
+
+/****h* ABOUT/xml_element
+ * NAME
+ *   xml_element
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * CREATION DATE
+ *   06/2000
+ * HISTORY
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ * TODO
+ *   Nicer external API. Get rid of macros.  Make opaque types, etc.
+ * PORTABILITY
+ *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
+ *   about anything with minor mods.
+ * NOTES
+ *   This code incorporates ideas from expat-ensor from http://xml.ensor.org.
+ *  
+ *   It was coded primarily to act as a go-between for expat and xmlrpc. To this
+ *   end, it stores xml elements, their sub-elements, and their attributes in an
+ *   in-memory tree.  When expat is done parsing, the tree can be walked, thus
+ *   retrieving the values.  The code can also be used to build a tree via API then
+ *   write out the tree to a buffer, thus "serializing" the xml.
+ *
+ *   It turns out this is useful for other purposes, such as parsing config files.
+ *   YMMV.
+ *
+ *   Some Features:
+ *     - output option for xml escaping data.  Choices include no escaping, entity escaping,
+ *       or CDATA sections.
+ *     - output option for character encoding.  Defaults to (none) utf-8.
+ *     - output option for verbosity/readability.  ultra-compact, newlines, pretty/level indented. 
+ *
+ * BUGS
+ *   there must be some.
+ ******/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xml_element.h"
+#include "queue.h"
+#include "expat.h"
+#include "encodings.h"
+
+#define my_free(thing)  if(thing) {free(thing); thing = 0;}
+
+#define XML_DECL_START                 "<?xml"
+#define XML_DECL_START_LEN             sizeof(XML_DECL_START) - 1
+#define XML_DECL_VERSION               "version='1.0'"
+#define XML_DECL_VERSION_LEN           sizeof(XML_DECL_VERSION) - 1
+#define XML_DECL_ENCODING_ATTR         "encoding"
+#define XML_DECL_ENCODING_ATTR_LEN     sizeof(XML_DECL_ENCODING_ATTR) - 1
+#define XML_DECL_ENCODING_DEFAULT      "utf-8"
+#define XML_DECL_ENCODING_DEFAULT_LEN  sizeof(XML_DECL_ENCODING_DEFAULT) - 1
+#define XML_DECL_END                   "?>"
+#define XML_DECL_END_LEN               sizeof(XML_DECL_END) - 1
+#define START_TOKEN_BEGIN              "<"
+#define START_TOKEN_BEGIN_LEN          sizeof(START_TOKEN_BEGIN) - 1
+#define START_TOKEN_END                ">"
+#define START_TOKEN_END_LEN            sizeof(START_TOKEN_END) - 1
+#define EMPTY_START_TOKEN_END          "/>"
+#define EMPTY_START_TOKEN_END_LEN      sizeof(EMPTY_START_TOKEN_END) - 1
+#define END_TOKEN_BEGIN                "</"
+#define END_TOKEN_BEGIN_LEN            sizeof(END_TOKEN_BEGIN) - 1
+#define END_TOKEN_END                  ">"
+#define END_TOKEN_END_LEN              sizeof(END_TOKEN_END) - 1
+#define ATTR_DELIMITER                 "\""
+#define ATTR_DELIMITER_LEN             sizeof(ATTR_DELIMITER) - 1
+#define CDATA_BEGIN                    "<![CDATA["
+#define CDATA_BEGIN_LEN                sizeof(CDATA_BEGIN) - 1
+#define CDATA_END                      "]]>"
+#define CDATA_END_LEN                  sizeof(CDATA_END) - 1
+#define EQUALS                         "="
+#define EQUALS_LEN                     sizeof(EQUALS) - 1
+#define WHITESPACE                     " "
+#define WHITESPACE_LEN                 sizeof(WHITESPACE) - 1
+#define NEWLINE                        "\n"
+#define NEWLINE_LEN                    sizeof(NEWLINE) - 1
+#define MAX_VAL_BUF                    144
+#define SCALAR_STR                     "SCALAR"
+#define SCALAR_STR_LEN                 sizeof(SCALAR_STR) - 1
+#define VECTOR_STR                     "VECTOR"
+#define VECTOR_STR_LEN                 sizeof(VECTOR_STR) - 1
+#define RESPONSE_STR                   "RESPONSE"
+#define RESPONSE_STR_LEN               sizeof(RESPONSE_STR) - 1
+
+
+/*-----------------------------
+- Begin xml_element Functions -
+-----------------------------*/
+
+/****f* xml_element/xml_elem_free_non_recurse
+ * NAME
+ *   xml_elem_free_non_recurse
+ * SYNOPSIS
+ *   void xml_elem_free_non_recurse(xml_element* root)
+ * FUNCTION
+ *   free a single xml element.  child elements will not be freed.
+ * INPUTS
+ *   root - the element to free
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_free ()
+ *   xml_elem_new ()
+ * SOURCE
+ */
+void xml_elem_free_non_recurse(xml_element* root) {
+   if(root) {
+      xml_element_attr* attrs = Q_Head(&root->attrs);
+      while(attrs) {
+         my_free(attrs->key);
+         my_free(attrs->val);
+         my_free(attrs);
+         attrs = Q_Next(&root->attrs);
+      }
+
+      Q_Destroy(&root->children);
+      Q_Destroy(&root->attrs);
+      my_free((char*)root->name);
+      simplestring_free(&root->text);
+      my_free(root);
+   }
+}
+/******/
+
+/****f* xml_element/xml_elem_free
+ * NAME
+ *   xml_elem_free
+ * SYNOPSIS
+ *   void xml_elem_free(xml_element* root)
+ * FUNCTION
+ *   free an xml element and all of its child elements
+ * INPUTS
+ *   root - the root of an xml tree you would like to free
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_free_non_recurse ()
+ *   xml_elem_new ()
+ * SOURCE
+ */
+void xml_elem_free(xml_element* root) {
+   if(root) {
+      xml_element* kids = Q_Head(&root->children);
+      while(kids) {
+         xml_elem_free(kids);
+         kids = Q_Next(&root->children);
+      }
+      xml_elem_free_non_recurse(root);
+   }
+}
+/******/
+
+/****f* xml_element/xml_elem_new
+ * NAME
+ *   xml_elem_new
+ * SYNOPSIS
+ *   xml_element* xml_elem_new()
+ * FUNCTION
+ *   allocates and initializes a new xml_element
+ * INPUTS
+ *   none
+ * RESULT
+ *   xml_element* or NULL.  NULL indicates an out-of-memory condition.
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_free ()
+ *   xml_elem_free_non_recurse ()
+ * SOURCE
+ */
+xml_element* xml_elem_new() {
+   xml_element* elem = calloc(1, sizeof(xml_element));
+   if(elem) {
+      Q_Init(&elem->children);
+      Q_Init(&elem->attrs);
+      simplestring_init(&elem->text);
+
+      /* init empty string in case we don't find any char data */
+      simplestring_addn(&elem->text, "", 0);
+   }
+   return elem;
+}
+/******/
+
+static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len)
+{
+   return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0;
+}
+
+
+
+static int create_xml_escape(char *pString, unsigned char c)
+{ 
+  int counter = 0;
+
+  pString[counter++] = '&';
+  pString[counter++] = '#';
+  if(c >= 100) {
+    pString[counter++] = c / 100 + '0';
+    c = c % 100;
+  }
+  if(c >= 10) {
+    pString[counter++] = c / 10 + '0';
+    c = c % 10;
+  }
+  pString[counter++] = c + '0';
+  pString[counter++] = ';';
+  return counter; 
+}
+
+#define non_ascii(c) (c > 127)
+#define non_print(c) (!isprint(c))
+#define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<')
+#define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */
+
+/*
+ * xml_elem_entity_escape
+ *
+ * Purpose:
+ *   escape reserved xml chars and non utf-8 chars as xml entities
+ * Comments:
+ *   The return value may be a new string, or null if no
+ *     conversion was performed. In the latter case, *newlen will
+ *     be 0.
+ * Flags (to escape)
+ *  xml_elem_no_escaping             = 0x000,
+ *  xml_elem_entity_escaping         = 0x002,   // escape xml special chars as entities
+ *  xml_elem_non_ascii_escaping      = 0x008,   // escape chars above 127
+ *  xml_elem_cdata_escaping          = 0x010,   // wrap in cdata
+ */
+static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) {
+  char *pRetval = 0;
+  int iNewBufLen=0;
+
+#define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \
+                                 ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \
+                                 ((flag & xml_elem_non_print_escaping) && non_print(c)) )
+
+  if(buf && *buf) {
+    const unsigned char *bufcopy;
+    char *NewBuffer;
+    int ToBeXmlEscaped=0;
+    int iLength;
+    bufcopy = buf;
+    iLength= old_len ? old_len : strlen(buf);
+    while(*bufcopy) {
+      if( should_escape(*bufcopy, flags) ) {
+       /* the length will increase by length of xml escape - the character length */
+       iLength += entity_length(*bufcopy);
+       ToBeXmlEscaped=1;
+      }
+      bufcopy++;
+    }
+
+    if(ToBeXmlEscaped) {
+
+      NewBuffer= malloc(iLength+1);
+      if(NewBuffer) {
+       bufcopy=buf;
+       while(*bufcopy) {
+         if(should_escape(*bufcopy, flags)) {
+           iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy);
+         }
+         else {
+           NewBuffer[iNewBufLen++]=*bufcopy;
+         }
+         bufcopy++;
+       }
+       NewBuffer[iNewBufLen] = 0;
+       pRetval = NewBuffer;
+      }
+    }
+  }
+
+  if(newlen) {
+     *newlen = iNewBufLen;
+  }
+
+  return pRetval;
+}
+
+
+static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth)
+{
+   int i;
+   static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT};
+   static char whitespace[] = "                                                                                               "
+                              "                                                                                               "
+                              "                                                                                               ";
+   depth++;
+
+   if(!el) {
+      fprintf(stderr, "Nothing to write\n");
+      return;
+   }
+   if(!options) {
+      options = &default_opts;
+   }
+
+   /* print xml declaration if at root level */
+   if(depth == 1) {
+      xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN);
+      xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+      xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN);
+      if(options->encoding && *options->encoding) {
+          xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+          xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN);
+          xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
+          xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+          xml_elem_writefunc(fptr, options->encoding, data, 0);
+          xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+      }
+      xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+      xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN);
+      if(options->verbosity != xml_elem_no_white_space) {
+         xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
+      }
+   }
+
+   if(options->verbosity == xml_elem_pretty && depth > 2) {
+         xml_elem_writefunc(fptr, whitespace, data, depth - 2);
+   }
+   /* begin element */
+   xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN);
+   if(el->name) {
+      xml_elem_writefunc(fptr, el->name, data, 0);
+
+      /* write attrs, if any */
+      if(Q_Size(&el->attrs)) {
+         xml_element_attr* iter = Q_Head(&el->attrs);
+         while( iter ) {
+            xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+            xml_elem_writefunc(fptr, iter->key, data, 0);
+            xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
+            xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+            xml_elem_writefunc(fptr, iter->val, data, 0);
+            xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+
+            iter = Q_Next(&el->attrs);
+         }
+      }
+   }
+   else {
+      xml_elem_writefunc(fptr, "None", data, 0);
+   }
+   /* if no text and no children, use abbreviated form, eg: <foo/> */
+   if(!el->text.len && !Q_Size(&el->children)) {
+       xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN);
+   }
+   /* otherwise, print element contents */
+   else {
+       xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN);
+
+       /* print text, if any */
+       if(el->text.len) {
+          char* escaped_str = el->text.str;
+          int buflen = el->text.len;
+
+          if(options->escaping && options->escaping != xml_elem_cdata_escaping) {
+             escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping );
+             if(!escaped_str) {
+                escaped_str = el->text.str;
+             }
+          }
+
+          if(options->escaping & xml_elem_cdata_escaping) {
+             xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN);
+          }
+
+          xml_elem_writefunc(fptr, escaped_str, data, buflen);
+
+          if(escaped_str != el->text.str) {
+             my_free(escaped_str);
+          }
+
+          if(options->escaping & xml_elem_cdata_escaping) {
+             xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN);
+          }
+       }
+       /* no text, so print child elems */
+       else {
+          xml_element *kids = Q_Head(&el->children);
+          i = 0;
+          while( kids ) {
+             if(i++ == 0) {
+                if(options->verbosity != xml_elem_no_white_space) {
+                   xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
+                }
+             }
+             xml_element_serialize(kids, fptr, data, options, depth);
+             kids = Q_Next(&el->children);
+          }
+          if(i) {
+             if(options->verbosity == xml_elem_pretty && depth > 2) {
+                   xml_elem_writefunc(fptr, whitespace, data, depth - 2);
+             }
+          }
+       }
+
+       xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN);
+       xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0);
+       xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN);
+   }
+   if(options->verbosity != xml_elem_no_white_space) {
+      xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
+   }
+}
+
+/* print buf to file */
+static file_out_fptr(void *f, const char *text, int size)
+{
+   fputs(text, (FILE *)f);
+}
+
+/* print buf to simplestring */
+static simplestring_out_fptr(void *f, const char *text, int size)
+{
+   simplestring* buf = (simplestring*)f;
+   if(buf) {
+      simplestring_addn(buf, text, size);
+   }
+}
+
+/****f* xml_element/xml_elem_serialize_to_string
+ * NAME
+ *   xml_elem_serialize_to_string
+ * SYNOPSIS
+ *   void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
+ * FUNCTION
+ *   writes element tree as XML into a newly allocated buffer
+ * INPUTS
+ *   el      - root element of tree
+ *   options - options determining how output is written.  see XML_ELEM_OUTPUT_OPTIONS
+ *   buf_len - length of returned buffer, if not null.
+ * RESULT
+ *   char* or NULL. Must be free'd by caller.
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_serialize_to_stream ()
+ *   xml_elem_parse_buf ()
+ * SOURCE
+ */
+char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
+{
+   simplestring buf;
+   simplestring_init(&buf);
+
+   xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0);
+
+   if(buf_len) {
+      *buf_len = buf.len;
+   }
+
+   return buf.str;
+}
+/******/
+
+/****f* xml_element/xml_elem_serialize_to_stream
+ * NAME
+ *   xml_elem_serialize_to_stream
+ * SYNOPSIS
+ *   void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
+ * FUNCTION
+ *   writes element tree as XML into a stream (typically an opened file)
+ * INPUTS
+ *   el      - root element of tree
+ *   output  - stream handle
+ *   options - options determining how output is written.  see XML_ELEM_OUTPUT_OPTIONS
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_serialize_to_string ()
+ *   xml_elem_parse_buf ()
+ * SOURCE
+ */
+void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
+{
+   xml_element_serialize(el, file_out_fptr, (void *)output, options, 0);
+}
+/******/
+
+/*--------------------------*
+* End xml_element Functions *
+*--------------------------*/
+
+
+/*----------------------
+* Begin Expat Handlers *
+*---------------------*/
+
+typedef struct _xml_elem_data {
+   xml_element*           root;
+   xml_element*           current;
+   XML_ELEM_INPUT_OPTIONS input_options;
+   int                    needs_enc_conversion;
+} xml_elem_data;
+
+
+/* expat start of element handler */
+static void startElement(void *userData, const char *name, const char **attrs)
+{
+   int i;
+   xml_element *c;
+   xml_elem_data* mydata = (xml_elem_data*)userData;
+   const char** p = attrs;
+
+   if(mydata) {
+      c = mydata->current;
+
+      mydata->current = xml_elem_new();
+      mydata->current->name = (char*)strdup(name);
+      mydata->current->parent = c;
+
+      /* init attrs */
+      while(p && *p) {
+         xml_element_attr* attr = malloc(sizeof(xml_element_attr));
+         if(attr) {
+            attr->key = strdup(*p);
+            attr->val = strdup(*(p+1));
+            Q_PushTail(&mydata->current->attrs, attr);
+
+            p += 2;
+         }
+      }
+   }
+}
+
+/* expat end of element handler */
+static void endElement(void *userData, const char *name)
+{
+   xml_elem_data* mydata = (xml_elem_data*)userData;
+
+   if(mydata && mydata->current && mydata->current->parent) {
+      Q_PushTail(&mydata->current->parent->children, mydata->current);
+
+      mydata->current = mydata->current->parent;
+   }
+}
+
+/* expat char data handler */
+static void charHandler(void *userData,
+                        const char *s,
+                        int len)
+{
+   xml_elem_data* mydata = (xml_elem_data*)userData;
+   if(mydata && mydata->current) {
+
+      /* Check if we need to decode utf-8 parser output to another encoding */
+      if(mydata->needs_enc_conversion && mydata->input_options->encoding) {
+         char* add_text = utf8_decode(s, len, &len, mydata->input_options->encoding);
+         if(add_text) {
+            simplestring_addn(&mydata->current->text, add_text, len);
+            free(add_text);
+            return;
+         }
+      }
+      simplestring_addn(&mydata->current->text, s, len);
+   }
+}
+/******/
+
+/*-------------------*
+* End Expat Handlers *
+*-------------------*/
+
+/*-------------------*
+* xml_elem_parse_buf *
+*-------------------*/
+
+/****f* xml_element/xml_elem_parse_buf
+ * NAME
+ *   xml_elem_parse_buf
+ * SYNOPSIS
+ *   xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
+ * FUNCTION
+ *   parse a buffer containing XML into an xml_element in-memory tree
+ * INPUTS
+ *   in_buf   - buffer containing XML document
+ *   len      - length of buffer
+ *   options  - input options. optional
+ *   error    - error result data. optional. check if result is null.
+ * RESULT
+ *   void
+ * NOTES
+ *   The returned data must be free'd by caller
+ * SEE ALSO
+ *   xml_elem_serialize_to_string ()
+ *   xml_elem_free ()
+ * SOURCE
+ */
+xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
+{
+   xml_element* xReturn = NULL;
+   char buf[100] = "";
+   static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8};
+
+   if(!options) {
+      options = &default_opts;
+   }
+
+   if(in_buf) {
+      XML_Parser parser;
+      xml_elem_data mydata = {0};
+
+      parser = XML_ParserCreate(NULL);
+
+      mydata.root = xml_elem_new();
+      mydata.current = mydata.root;
+      mydata.input_options = options;
+      mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8);
+
+      XML_SetElementHandler(parser, startElement, endElement);
+      XML_SetCharacterDataHandler(parser, charHandler);
+
+      /* pass the xml_elem_data struct along */
+      XML_SetUserData(parser, (void*)&mydata);
+
+      if(!len) {
+         len = strlen(in_buf);
+      }
+
+      /* parse the XML */
+      if(XML_Parse(parser, in_buf, len, 1) == 0) {
+         enum XML_Error err_code = XML_GetErrorCode(parser);
+         int line_num = XML_GetCurrentLineNumber(parser);
+         int col_num = XML_GetCurrentColumnNumber(parser);
+         long byte_idx = XML_GetCurrentByteIndex(parser);
+         int byte_total = XML_GetCurrentByteCount(parser);
+         const char * error_str = XML_ErrorString(err_code);
+         if(byte_idx >= 0) {
+             snprintf(buf, 
+                      sizeof(buf),
+                      "\n\tdata beginning %i before byte index: %s\n",
+                      byte_idx > 10  ? 10 : byte_idx,
+                      in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx));
+         }
+
+         fprintf(stderr, "expat reports error code %i\n"
+                "\tdescription: %s\n"
+                "\tline: %i\n"
+                "\tcolumn: %i\n"
+                "\tbyte index: %i\n"
+                "\ttotal bytes: %i\n%s ",
+                err_code, error_str, line_num, 
+                col_num, byte_idx, byte_total, buf);
+
+
+          /* error condition */
+          if(error) {
+              error->parser_code = (long)err_code;
+              error->line = line_num;
+              error->column = col_num;
+              error->byte_index = byte_idx;
+              error->parser_error = error_str;
+          }
+      }
+      else {
+         xReturn = (xml_element*)Q_Head(&mydata.root->children);
+      }
+
+      XML_ParserFree(parser);
+
+
+      xml_elem_free_non_recurse(mydata.root);
+   }
+
+   return xReturn;
+}
+
+/******/
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_element.h b/ext/rpc/xmlrpc/libxmlrpc/xml_element.h
new file mode 100644 (file)
index 0000000..bf4f3a8
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef __XML_ELEMENT_H__
+ #define __XML_ELEMENT_H__
+
+/* includes */
+#include <stdio.h>
+#include "queue.h"
+#include "simplestring.h"
+#include "encodings.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/****d* enum/XML_ELEM_VERBOSITY
+ * NAME
+ *   XML_ELEM_VERBOSITY
+ * NOTES
+ *   verbosity/readability options for generated xml
+ * SEE ALSO
+ *   XML_ELEM_OUTPUT_OPTIONS
+ * SOURCE
+ */
+typedef enum _xml_elem_verbosity {
+   xml_elem_no_white_space,    /* compact xml with no white space            */
+   xml_elem_newlines_only,     /* add newlines for enhanced readability      */
+   xml_elem_pretty             /* add newlines and indent accordint to depth */
+} XML_ELEM_VERBOSITY;
+/******/
+
+
+/****d* enum/XML_ELEM_ESCAPING
+ * NAME
+ *   XML_ELEM_ESCAPING
+ * NOTES
+ * xml escaping options for generated xml
+ * SEE ALSO
+ *   XML_ELEM_OUTPUT_OPTIONS
+ * SOURCE
+ */
+typedef enum _xml_elem_escaping {
+   xml_elem_no_escaping             = 0x000,
+   xml_elem_markup_escaping         = 0x002,   /* entity escape xml special chars         */
+   xml_elem_non_ascii_escaping      = 0x008,   /* entity escape chars above 127           */
+   xml_elem_non_print_escaping      = 0x010,   /* entity escape non print (illegal) chars */
+   xml_elem_cdata_escaping          = 0x020,   /* wrap in cdata section                   */
+} XML_ELEM_ESCAPING;
+/******/
+
+
+/****s* struct/XML_ELEM_OUTPUT_OPTIONS
+ * NAME
+ *   XML_ELEM_OUTPUT_OPTIONS
+ * NOTES
+ *   defines various output options
+ * SOURCE
+ */
+typedef struct _xml_output_options {
+   XML_ELEM_VERBOSITY           verbosity;      /* length/verbosity of xml        */
+   XML_ELEM_ESCAPING            escaping;       /* how to escape special chars    */
+   const char*                  encoding;       /* <?xml encoding="<encoding>" ?> */
+} STRUCT_XML_ELEM_OUTPUT_OPTIONS, *XML_ELEM_OUTPUT_OPTIONS;
+/******/
+
+/****s* struct/XML_ELEM_INPUT_OPTIONS
+ * NAME
+ *   XML_ELEM_INPUT_OPTIONS
+ * NOTES
+ *   defines various input options
+ * SOURCE
+ */
+typedef struct _xml_input_options {
+  ENCODING_ID                  encoding;       /* which encoding to use.       */
+} STRUCT_XML_ELEM_INPUT_OPTIONS, *XML_ELEM_INPUT_OPTIONS;
+/******/
+
+/****s* struct/XML_ELEM_ERROR
+ * NAME
+ *   XML_ELEM_ERROR
+ * NOTES
+ *   defines an xml parser error
+ * SOURCE
+ */
+typedef struct _xml_elem_error {
+  int parser_code;
+  const char* parser_error;
+  long line;
+  long column;
+  long byte_index;
+} STRUCT_XML_ELEM_ERROR, *XML_ELEM_ERROR;
+/******/
+
+
+/*-************************
+* begin xml element stuff *
+**************************/
+
+/****s* struct/xml_elem_attr
+ * NAME
+ *  xml_elem_attr
+ * NOTES
+ *   representation of an xml attribute, foo="bar"
+ * SOURCE
+ */
+typedef struct _xml_element_attr {
+   char* key;        /* attribute key   */
+   char* val;        /* attribute value */
+} xml_element_attr;
+/******/
+
+/****s* struct/xml_elem_attr
+ * NAME
+ *  xml_elem_attr
+ * NOTES
+ *   representation of an xml element, eg <candidate name="Harry Browne" party="Libertarian"/>
+ * SOURCE
+ */
+typedef struct _xml_element {
+   const char*   name;           /* element identifier */
+   simplestring  text;           /* text contained between element begin/end pairs */
+   struct _xml_element* parent;  /* element's parent */
+                                 
+   queue        attrs;           /* attribute list */
+   queue        children;        /* child element list */
+} xml_element;
+/******/
+
+void xml_elem_free(xml_element* root);
+void xml_elem_free_non_recurse(xml_element* root);
+xml_element* xml_elem_new();
+char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len);
+void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options);
+xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error);
+
+/*-**********************
+* end xml element stuff *
+************************/
+
+/*-**********************
+* Begin xml_element API *
+************************/
+
+/****d* VALUE/XMLRPC_MACROS
+ * NAME
+ *   Some Helpful Macros
+ * NOTES
+ *   Some macros for making life easier.  Should be self-explanatory.
+ * SEE ALSO
+ *   XMLRPC_AddValueToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+#define xml_elem_next_element(el) ((el) ? (xml_element *)Q_Next(&el->children) : NULL)
+#define xml_elem_head_element(el) ((el) ? (xml_element *)Q_Head(&el->children) : NULL)
+#define xml_elem_next_attr(el) ((el) ? (xml_element_attr *)Q_Next(&el->attrs) : NULL)
+#define xml_elem_head_attr(el) ((el) ? (xml_element_attr *)Q_Head(&el->attrs) : NULL)
+#define xml_elem_get_name(el) (char *)((el) ? el->name : NULL)
+#define xml_elem_get_val(el) (char *)((el) ? el->text.str : NULL)
+/******/
+
+
+/*-********************
+* End xml_element API *
+**********************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_ELEMENT_H__ */
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c
new file mode 100644 (file)
index 0000000..0d827a5
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include "xml_to_dandarpc.h"
+#include "base64.h"
+
+/* list of tokens used in vocab */
+#define ELEM_METHODCALL     "methodCall"
+#define ELEM_METHODNAME     "methodName"
+#define ELEM_METHODRESPONSE "methodResponse"
+#define ELEM_ROOT           "simpleRPC"
+
+#define ATTR_ARRAY          "array"
+#define ATTR_BASE64         "base64"
+#define ATTR_BOOLEAN        "boolean"
+#define ATTR_DATETIME       "dateTime.iso8601"
+#define ATTR_DOUBLE         "double"
+#define ATTR_ID             "id"
+#define ATTR_INT            "int"
+#define ATTR_MIXED          "mixed"
+#define ATTR_SCALAR         "scalar"
+#define ATTR_STRING         "string"
+#define ATTR_STRUCT         "struct"
+#define ATTR_TYPE           "type"
+#define ATTR_VECTOR         "vector"
+#define ATTR_VERSION        "version"
+
+#define VAL_VERSION_0_9     "0.9"
+
+
+XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) {
+   if(!xCurrent) {
+      xCurrent = XMLRPC_CreateValueEmpty();
+   }
+
+   if(el->name) {
+      const char* id = NULL;
+      const char* type = NULL;
+      xml_element_attr* attr_iter = Q_Head(&el->attrs);
+
+      while(attr_iter) {
+         if(!strcmp(attr_iter->key, ATTR_ID)) {
+            id = attr_iter->val;
+         }
+         if(!strcmp(attr_iter->key, ATTR_TYPE)) {
+            type = attr_iter->val;
+         }
+         attr_iter = Q_Next(&el->attrs);
+      }
+
+      if(id) {
+         XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact);
+      }
+
+      if(!strcmp(el->name, ATTR_SCALAR)) {
+         if(!type || !strcmp(type, ATTR_STRING)) {
+            XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len);
+         }
+         else if(!strcmp(type, ATTR_INT)) {
+            XMLRPC_SetValueInt(xCurrent, atoi(el->text.str));
+         }
+         else if(!strcmp(type, ATTR_BOOLEAN)) {
+            XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str));
+         }
+         else if(!strcmp(type, ATTR_DOUBLE)) {
+            XMLRPC_SetValueDouble(xCurrent, atof(el->text.str));
+         }
+         else if(!strcmp(type, ATTR_DATETIME)) {
+            XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str);
+         }
+         else if(!strcmp(type, ATTR_BASE64)) {
+            struct buffer_st buf;
+            base64_decode(&buf, el->text.str, el->text.len);
+            XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset);
+            buffer_delete(&buf);
+         }
+      }
+      else if(!strcmp(el->name, ATTR_VECTOR)) {
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+
+         if(!type || !strcmp(type, ATTR_MIXED)) {
+            XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
+         }
+         else if(!strcmp(type, ATTR_ARRAY)) {
+            XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
+         }
+         else if(!strcmp(type, ATTR_STRUCT)) {
+            XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct);
+         }
+         while( iter ) {
+            XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty();
+            xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter);
+            XMLRPC_AddValueToVector(xCurrent, xNext);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      }
+      else {
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+         while( iter ) {
+            xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+
+         if(!strcmp(el->name, ELEM_METHODCALL)) {
+            if(request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+            }
+         }
+         else if(!strcmp(el->name, ELEM_METHODRESPONSE)) {
+            if(request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
+            }
+         }
+         else if(!strcmp(el->name, ELEM_METHODNAME)) {
+            if(request) {
+               XMLRPC_RequestSetMethodName(request, el->text.str);
+            }
+         }
+      }
+   }
+   return xCurrent;
+}
+
+XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el)
+{
+   return xml_element_to_DANDARPC_REQUEST_worker(NULL, NULL, el);
+}
+
+XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
+{
+   if(request) {
+      return XMLRPC_RequestSetData(request, xml_element_to_DANDARPC_REQUEST_worker(request, NULL, el));
+   }
+   return NULL;
+}
+
+xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) {
+#define BUF_SIZE 512
+   xml_element* root = NULL;
+   if(node) {
+      char buf[BUF_SIZE];
+      const char* id = XMLRPC_GetValueID(node);
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
+      XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request);
+      int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space);
+      xml_element* elem_val = xml_elem_new();
+      const char* pAttrType = NULL;
+
+      xml_element_attr* attr_type = bNoAddType ? NULL : malloc(sizeof(xml_element_attr));
+       
+      if(attr_type) {
+         attr_type->key = strdup(ATTR_TYPE);
+         attr_type->val = 0;
+         Q_PushTail(&elem_val->attrs, attr_type);
+      }
+
+      elem_val->name = (type == xmlrpc_vector) ? strdup(ATTR_VECTOR) : strdup(ATTR_SCALAR);
+
+      if(id && *id) {
+         xml_element_attr* attr_id = malloc(sizeof(xml_element_attr));
+         if(attr_id) {
+            attr_id->key = strdup(ATTR_ID);
+            attr_id->val = strdup(id);
+            Q_PushTail(&elem_val->attrs, attr_id);
+         }
+      }
+
+      switch(type) {
+         case xmlrpc_string:
+            pAttrType = ATTR_STRING;
+            simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
+            break;
+         case xmlrpc_int:
+            pAttrType = ATTR_INT;
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_boolean:
+            pAttrType = ATTR_BOOLEAN;
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_double:
+            pAttrType = ATTR_DOUBLE;
+            snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_datetime:
+            pAttrType = ATTR_DATETIME;
+            simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
+            break;
+         case xmlrpc_base64:
+            {
+               struct buffer_st buf;
+               pAttrType = ATTR_BASE64;
+               base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
+               simplestring_addn(&elem_val->text, buf.data, buf.offset );
+               buffer_delete(&buf);
+            }
+            break;
+         case xmlrpc_vector:
+            {
+               XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
+               XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
+
+               switch(my_type) {
+                  case xmlrpc_vector_array:
+                     pAttrType = ATTR_ARRAY;
+                     break;
+                  case xmlrpc_vector_mixed:
+                     pAttrType = ATTR_MIXED;
+                     break;
+                  case xmlrpc_vector_struct:
+                     pAttrType = ATTR_STRUCT;
+                     break;
+                  default:
+                     break;
+               }
+
+               /* recurse through sub-elements */
+               while( xIter ) {
+                  xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter);
+                  if(next_el) {
+                     Q_PushTail(&elem_val->children, next_el);
+                  }
+                  xIter = XMLRPC_VectorNext(node);
+               }
+            }
+            break;
+         default:
+            break;
+      }
+      if(pAttrType && attr_type && !bNoAddType) {
+         attr_type->val = strdup(pAttrType);
+      }
+      root = elem_val;
+   }
+   return root;
+}
+
+xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
+   return DANDARPC_to_xml_element_worker(NULL, node);
+}
+
+xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
+   xml_element* wrapper = NULL;
+   xml_element* root = NULL;
+   if(request) {
+      XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
+      const char* pStr = NULL;
+      xml_element_attr* version = malloc(sizeof(xml_element_attr));
+      version->key = strdup(ATTR_VERSION);
+      version->val = strdup(VAL_VERSION_0_9);
+      
+      wrapper = xml_elem_new();
+
+      if(request_type == xmlrpc_request_response) {
+         pStr = ELEM_METHODRESPONSE;
+      }
+      else if(request_type == xmlrpc_request_call) {
+         pStr = ELEM_METHODCALL;
+      }
+      if(pStr) {
+         wrapper->name = strdup(pStr);
+      }
+
+      root = xml_elem_new();
+      root->name = strdup(ELEM_ROOT);
+      Q_PushTail(&root->attrs, version);
+      Q_PushTail(&root->children, wrapper);
+
+      pStr = XMLRPC_RequestGetMethodName(request);
+
+      if(pStr) {
+         xml_element* method = xml_elem_new();
+         method->name = strdup(ELEM_METHODNAME);
+         simplestring_add(&method->text, pStr);
+         Q_PushTail(&wrapper->children, method);
+      }
+      Q_PushTail(&wrapper->children, 
+                 DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request)));
+   }
+   return root;
+}
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.h b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.h
new file mode 100644 (file)
index 0000000..6facb55
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef XML_TO_DANDARPC_H
+ #define XML_TO_DANDARPC_H
+
+#include "time.h" 
+#include "xmlrpc.h"
+
+XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el);
+XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el);
+xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node);
+xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request);
+
+#endif /* XML_TO_DANDARPC_H */
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c
new file mode 100644 (file)
index 0000000..1cd3d9a
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+#include <string.h>
+#include <stdlib.h>
+#include "xml_to_xmlrpc.h"
+#include "base64.h"
+
+/* list of tokens used in vocab */
+#define ELEM_ARRAY          "array"
+#define ELEM_BASE64         "base64"
+#define ELEM_BOOLEAN        "boolean"
+#define ELEM_DATA           "data"
+#define ELEM_DATETIME       "dateTime.iso8601"
+#define ELEM_DOUBLE         "double"
+#define ELEM_FAULT          "fault"
+#define ELEM_FAULTCODE      "faultCode"
+#define ELEM_FAULTSTRING    "faultString"
+#define ELEM_I4             "i4"
+#define ELEM_INT            "int"
+#define ELEM_MEMBER         "member"
+#define ELEM_METHODCALL     "methodCall"
+#define ELEM_METHODNAME     "methodName"
+#define ELEM_METHODRESPONSE "methodResponse"
+#define ELEM_NAME           "name"
+#define ELEM_PARAM          "param"
+#define ELEM_PARAMS         "params"
+#define ELEM_STRING         "string"
+#define ELEM_STRUCT         "struct"
+#define ELEM_VALUE          "value"
+
+
+XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
+   if (!current_val) {
+      /* This should only be the case for the first element */
+      current_val = XMLRPC_CreateValueEmpty();
+   }
+
+   if (el->name) {
+      if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */
+          || ((!strcmp(el->name, ELEM_PARAMS)) && 
+              (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call))    /* this "PARAMS" concept is silly.  dave?! */
+          || !strcmp(el->name, ELEM_FAULT)) {  /* so is this "FAULT" nonsense. */
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+         XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
+
+         while (iter) {
+            XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
+            xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
+            XMLRPC_AddValueToVector(current_val, xNextVal);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      } else if (!strcmp(el->name, ELEM_STRUCT)) {
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+         XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
+
+         while ( iter ) {
+            XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
+            xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
+            XMLRPC_AddValueToVector(current_val, xNextVal);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      } else if (!strcmp(el->name, ELEM_STRING) || 
+                 (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
+         XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
+      } else if (!strcmp(el->name, ELEM_NAME)) {
+         XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
+      } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
+         XMLRPC_SetValueInt(current_val, atoi(el->text.str));
+      } else if (!strcmp(el->name, ELEM_BOOLEAN)) {
+         XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
+      } else if (!strcmp(el->name, ELEM_DOUBLE)) {
+         XMLRPC_SetValueDouble(current_val, atof(el->text.str));
+      } else if (!strcmp(el->name, ELEM_DATETIME)) {
+         XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
+      } else if (!strcmp(el->name, ELEM_BASE64)) {
+         struct buffer_st buf;
+         base64_decode(&buf, el->text.str, el->text.len);
+         XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
+         buffer_delete(&buf);
+      } else {
+         xml_element* iter;
+
+         if (!strcmp(el->name, ELEM_METHODCALL)) {
+            if (request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+            }
+         } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
+            if (request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
+            }
+         } else if (!strcmp(el->name, ELEM_METHODNAME)) {
+            if (request) {
+               XMLRPC_RequestSetMethodName(request, el->text.str);
+            }
+         }
+
+         iter = (xml_element*)Q_Head(&el->children);
+         while ( iter ) {
+            xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, 
+                                                 current_val, iter);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      }
+   }
+   return current_val;
+}
+
+XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
+{
+   return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
+}
+
+XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
+{
+   if (request) {
+      return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
+   }
+   return NULL;
+}
+
+xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, 
+                                          XMLRPC_REQUEST_TYPE request_type, int depth) {
+#define BUF_SIZE 512
+   xml_element* root = NULL;
+   if (node) {
+      char buf[BUF_SIZE];
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
+      XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
+      xml_element* elem_val = xml_elem_new();
+
+      /* special case for when root element is not an array */
+      if (depth == 0 && 
+          !(type == xmlrpc_vector && 
+            vtype == xmlrpc_vector_array && 
+            request_type == xmlrpc_request_call) ) {
+         int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
+
+         xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
+         if (next_el) {
+            Q_PushTail(&elem_val->children, next_el);
+         }
+         elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
+      } else {
+         switch (type) {
+         case xmlrpc_string:
+            elem_val->name = strdup(ELEM_STRING);
+            simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
+            break;
+         case xmlrpc_int:
+            elem_val->name = strdup(ELEM_INT);
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_boolean:
+            elem_val->name = strdup(ELEM_BOOLEAN);
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_double:
+            elem_val->name = strdup(ELEM_DOUBLE);
+            snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_datetime:
+            elem_val->name = strdup(ELEM_DATETIME);
+            simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
+            break;
+         case xmlrpc_base64:
+            {
+               struct buffer_st buf;
+               elem_val->name = strdup(ELEM_BASE64);
+               base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
+               simplestring_addn(&elem_val->text, buf.data, buf.offset );
+               buffer_delete(&buf);
+            }
+            break;
+         case xmlrpc_vector:
+            {
+               XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
+               XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
+               xml_element* root_vector_elem = elem_val;
+
+               switch (my_type) {
+               case xmlrpc_vector_array:
+                  {
+                      if(depth == 0) {
+                         elem_val->name = strdup(ELEM_PARAMS);
+                      }
+                      else {
+                         /* Hi my name is Dave and I like to make things as confusing
+                          * as possible, thus I will throw in this 'data' element
+                          * where it absolutely does not belong just so that people
+                          * cannot code arrays and structs in a similar and straight
+                          * forward manner. Have a good day.
+                          *
+                          * GRRRRRRRRR!
+                          */
+                         xml_element* data = xml_elem_new();
+                         data->name = strdup(ELEM_DATA);
+    
+                         elem_val->name = strdup(ELEM_ARRAY);
+                         Q_PushTail(&elem_val->children, data);
+                         root_vector_elem = data;
+                      }
+                  }
+                  break;
+               case xmlrpc_vector_mixed:       /* not officially supported */
+               case xmlrpc_vector_struct:
+                  elem_val->name = strdup(ELEM_STRUCT);
+                  break;
+               default:
+                  break;
+               }
+
+               /* recurse through sub-elements */
+               while ( xIter ) {
+                  xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
+                  if (next_el) {
+                     Q_PushTail(&root_vector_elem->children, next_el);
+                  }
+                  xIter = XMLRPC_VectorNext(node);
+               }
+            }
+            break;
+         default:
+            break;
+         }
+      }
+
+      {
+         XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
+
+         if (depth == 1) {
+            xml_element* value = xml_elem_new();
+            value->name = strdup(ELEM_VALUE);
+
+            /* yet another hack for the "fault" crap */
+            if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
+               root = value;
+            } else {
+               xml_element* param = xml_elem_new();
+               param->name = strdup(ELEM_PARAM);
+
+               Q_PushTail(&param->children, value);
+
+               root = param;
+            }
+            Q_PushTail(&value->children, elem_val);
+         } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
+            xml_element* member = xml_elem_new();
+            xml_element* name = xml_elem_new();
+            xml_element* value = xml_elem_new();
+
+            member->name = strdup(ELEM_MEMBER);
+            name->name = strdup(ELEM_NAME);
+            value->name = strdup(ELEM_VALUE);
+
+            simplestring_add(&name->text, XMLRPC_GetValueID(node));
+
+            Q_PushTail(&member->children, name);
+            Q_PushTail(&member->children, value);
+            Q_PushTail(&value->children, elem_val);
+
+            root = member;
+         } else if (vtype == xmlrpc_vector_array) {
+            xml_element* value = xml_elem_new();
+
+            value->name = strdup(ELEM_VALUE);
+
+            Q_PushTail(&value->children, elem_val);
+
+            root = value;
+         } else if (vtype == xmlrpc_vector_none) {
+            /* no parent.  non-op */
+            root = elem_val;
+         } else {
+            xml_element* value = xml_elem_new();
+
+            value->name = strdup(ELEM_VALUE);
+
+            Q_PushTail(&value->children, elem_val);
+
+            root = value;
+         }
+      }
+   }
+   return root;
+}
+
+xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
+   return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
+}
+
+xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
+   xml_element* wrapper = NULL;
+   if (request) {
+      const char* pStr = NULL;
+      XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
+      XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
+
+      wrapper = xml_elem_new();
+
+      if (request_type == xmlrpc_request_call) {
+         pStr = ELEM_METHODCALL;
+      } else if (request_type == xmlrpc_request_response) {
+         pStr = ELEM_METHODRESPONSE;
+      }
+      if (pStr) {
+         wrapper->name = strdup(pStr);
+      }
+
+      pStr = XMLRPC_RequestGetMethodName(request);
+
+      if (pStr) {
+         xml_element* method = xml_elem_new();
+         method->name = strdup(ELEM_METHODNAME);
+         simplestring_add(&method->text, pStr);
+         Q_PushTail(&wrapper->children, method);
+      }
+      if (xParams) {
+         Q_PushTail(&wrapper->children, 
+                    XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
+      } else {
+         /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
+         xml_element* params = xml_elem_new();
+         params->name = strdup(ELEM_PARAMS);
+         Q_PushTail(&wrapper->children, params);
+      }
+   }
+   return wrapper;
+}
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.h b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.h
new file mode 100644 (file)
index 0000000..234a153
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+#ifndef XML_TO_XMLRPC_H
+ #define XML_TO_XMLRPC_H
+
+#include "time.h" 
+#include "xmlrpc.h"
+
+XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el);
+XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el);
+xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node);
+xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request);
+
+#endif /* XML_TO_XMLRPC_H */
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c
new file mode 100644 (file)
index 0000000..f95ac94
--- /dev/null
@@ -0,0 +1,2471 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+
+/****h* ABOUT/xmlrpc
+ * NAME
+ *   XMLRPC_VALUE
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * CREATION DATE
+ *   9/1999 - 10/2000
+ * HISTORY
+ *   09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only.
+ *   06/2000 -- danda -- played with expat-ensor from www.ensor.org.  Cool, but some flaws.
+ *   07/2000 -- danda -- wrote new implementation to be compatible with xmlrpc standard and
+ *                       incorporated some ideas from ensor, most notably the separation of
+ *                       xml dom from xmlrpc api.
+ *   08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc
+ *   08/2000 -- danda -- PHP C extension that uses XMLRPC                     
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ * TODO
+ *   Server method introspection. (Enumerate available methods, describe I/O)
+ * PORTABILITY
+ *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
+ *   about anything with minor mods.
+ * NOTES
+ *   Welcome to XMLRPC.  For more info on the specification and history, see
+ *   http://www.xmlrpc.org.
+ *
+ *   This code aims to be a full-featured C implementation of XMLRPC.  It does not
+ *   have any networking code.  Rather, it is intended to be plugged into apps
+ *   or libraries with existing networking facilities, eg PHP, apache, perl, mozilla, 
+ *   home-brew application servers, etc.
+ *
+ *   Usage Paradigm:
+ *     The user of this library will typically be implementing either an XMLRPC server,
+ *     an XMLRPC client, or both.  The client will use the library to build an in-memory
+ *     representation of a request, and then serialize (encode) that request into XML. The
+ *     client will then send the XML to the server via external mechanism.  The server will
+ *     de-serialize the XML back into an binary representation, call the appropriate registered
+ *     method -- thereby generating a response.  The response will be serialized into XML and
+ *     sent back to the client.  The client will de-serialize it into memory, and can
+ *     iterate through the results via API.
+ *
+ *     Both the request and the response may consist of arbitrarily long, arbitrarily nested
+ *     values.  The values may be one of several types, as defined by XMLRPC_VALUE_TYPE.
+ *
+ *   Features and Architecture:
+ *     - The XML parsing (xml_element.c) is completely independent of the XMLRPC api. In fact,
+ *       it can be used as a standalone dom implementation.
+ *     - Because of this, the same XMLRPC data can be serialized into multiple xml vocabularies.
+ *       It is simply a matter of writing a transport.  So far, two transports have been defined.
+ *       The default xmlrpc vocab (xml_to_xmlrpc.c), and simple-rpc (xml_to_dandarpc.c) which is 
+ *       proprietary, but imho more readable, and nice for proprietary legacy reasons.
+ *     - Various output options, including: xml escaping via CDATA or entity, case folding,
+ *       vocab version, and character encoding.
+ *     - One to One mapping between C structures and actual values, unlike ensor which forces
+ *       one to understand the arcana of the xmlrpc vocab.
+ *     - support for mixed indexed/keyed vector types, making it more compatible with 
+ *       languages such as PHP.
+ *     - quite speedy compared to implementations written in interpreted languages. Also, uses
+ *       intelligent string handling, so not many strlen() calls, etc.
+ *     - comprehensive API for manipulation of values
+ *******/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "queue.h"
+#include "xmlrpc.h"
+#include "expat.h"
+#include "base64.h"
+
+#include "xml_to_xmlrpc.h"
+#include "xml_to_dandarpc.h"
+#include "xml_element.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+
+
+
+/*-*********************
+* Begin Time Functions *
+***********************/
+
+static int date_from_ISO8601(const char *text, time_t *value)
+{
+   struct tm tm;
+   int n;
+   int i;
+   time_t t;
+
+   tm.tm_isdst = -1;
+
+   if(strlen(text) < 17) {
+      return -1;
+   }
+
+   n = 1000;
+   tm.tm_year = 0;
+   for(i = 0; i < 4; i++) {
+      tm.tm_year += (text[i]-'0')*n;
+      n /= 10;
+   }
+   n = 10;
+   tm.tm_mon = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_mon += (text[i+4]-'0')*n;
+      n /= 10;
+   }
+   tm.tm_mon --;
+
+   n = 10;
+   tm.tm_mday = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_mday += (text[i+6]-'0')*n;
+      n /= 10;
+   }
+
+   n = 10;
+   tm.tm_hour = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_hour += (text[i+9]-'0')*n;
+      n /= 10;
+   }
+
+   n = 10;
+   tm.tm_min = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_min += (text[i+12]-'0')*n;
+      n /= 10;
+   }
+
+   n = 10;
+   tm.tm_sec = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_sec += (text[i+15]-'0')*n;
+      n /= 10;
+   }
+
+   tm.tm_year -= 1900;
+
+   *value = mktime(&tm);
+
+   return 0;
+
+}
+
+static int date_to_ISO8601(time_t value, char *buf, int length)
+{
+   struct tm *tm;
+   tm = localtime(&value);
+
+   return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm);
+}
+
+/*-*******************
+* End Time Functions *
+*********************/
+
+
+/*-***************************
+* Begin XMLRPC_REQUEST funcs *
+*****************************/
+
+/****f* REQUEST/XMLRPC_RequestNew
+ * NAME
+ *   XMLRPC_RequestNew
+ * SYNOPSIS
+ *   XMLRPC_REQUEST XMLRPC_RequestNew()
+ * FUNCTION
+ *   Creates a new XMLRPC_Request data struct
+ * INPUTS
+ *   none
+ * SEE ALSO
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+XMLRPC_REQUEST XMLRPC_RequestNew() {
+   XMLRPC_REQUEST xRequest = calloc(1, sizeof(STRUCT_XMLRPC_REQUEST));
+   if(xRequest) {
+      simplestring_init(&xRequest->methodName);
+   }
+   return xRequest;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestFree
+ * NAME
+ *   XMLRPC_RequestFree
+ * SYNOPSIS
+ *   void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO)
+ * FUNCTION
+ *   Free XMLRPC Request and all sub-values
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   bFreeIO -- 1 = also free request value data, if any, 0 = ignore.
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) {
+   if(request) {
+      simplestring_free(&request->methodName);
+
+      if(request->io && bFreeIO) {
+         XMLRPC_CleanupValue(request->io);
+      }
+      if(request->error) {
+         XMLRPC_CleanupValue(request->error);
+      }
+      my_free(request);
+   }
+}
+/*******/
+
+/* Set Method Name to call */
+/****f* REQUEST/XMLRPC_RequestSetMethodName
+ * NAME
+ *   XMLRPC_RequestSetMethodName
+ * SYNOPSIS
+ *   const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName)
+ * FUNCTION
+ *   Set name of method to call with this request.
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   methodName -- name of method
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetMethodName ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) {
+   if(request) {
+      simplestring_clear(&request->methodName);
+      simplestring_add(&request->methodName, methodName);
+      return request->methodName.str;
+   }
+   return NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetMethodName
+ * NAME
+ *   XMLRPC_RequestGetMethodName
+ * SYNOPSIS
+ *   const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Get name of method called by this request
+ * INPUTS
+ *   request -- previously allocated request struct
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetMethodName ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) {
+   return request ? request->methodName.str : NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestSetRequestType
+ * NAME
+ *   XMLRPC_RequestSetRequestType
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type)
+ * FUNCTION
+ *   A request struct may be allocated by a caller or by xmlrpc
+ *   in response to a request.  This allows setting the
+ *   request type.
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   type    -- request type [xmlrpc_method_call | xmlrpc_method_response]
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetRequestType ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) {
+   if(request) {
+      request->request_type = type;
+      return request->request_type;
+   }
+   return xmlrpc_request_none;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetRequestType
+ * NAME
+ *   XMLRPC_RequestGetRequestType
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   A request struct may be allocated by a caller or by xmlrpc
+ *   in response to a request.  This allows setting the
+ *   request type.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   type    -- request type [xmlrpc_method_call | xmlrpc_method_response]
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetRequestType ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) {
+   return request ? request->request_type : xmlrpc_request_none;
+}
+/*******/
+
+
+/****f* REQUEST/XMLRPC_RequestSetData
+ * NAME
+ *   XMLRPC_RequestSetData
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data)
+ * FUNCTION
+ *   Associates a block of xmlrpc data with the request.  The
+ *   data is *not* copied.  A pointer is kept.  The caller
+ *   should be careful not to doubly free the data value,
+ *   which may optionally be free'd by XMLRPC_RequestFree().
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   data    -- previously allocated data struct
+ * RESULT
+ *   XMLRPC_VALUE -- pointer to value stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetData ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) {
+   if(request && data) {
+      request->io = XMLRPC_CopyValue(data);
+      return request->io;
+   }
+   return NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetData
+ * NAME
+ *   XMLRPC_RequestGetData
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Returns data associated with request, if any.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   XMLRPC_VALUE -- pointer to value stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetData ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) {
+   return request ? request->io : NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestSetOutputOptions
+ * NAME
+ *   XMLRPC_RequestSetOutputOptions
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output)
+ * FUNCTION
+ *   Sets output options used for generating XML. The output struct
+ *   is copied, and may be freed by the caller.
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   output  -- output options struct initialized by caller
+ * RESULT
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to value stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetOutputOptions ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * SOURCE
+ */
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) {
+   if(request && output) {
+      memcpy(&request->output, output, sizeof(STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS));
+      return &request->output;
+   }
+   return NULL;
+}
+/*******/
+
+
+/****f* REQUEST/XMLRPC_RequestGetOutputOptions
+ * NAME
+ *   XMLRPC_RequestGetOutputOptions
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Gets a pointer to output options used for generating XML.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to options stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetOutputOptions ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * SOURCE
+ */
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) {
+   return request ? &request->output : NULL;
+}
+/*******/
+
+/*-*************************
+* End XMLRPC_REQUEST funcs *
+***************************/
+
+
+/*-***************************
+* Begin Serializiation funcs *
+*****************************/
+
+/****f* SERIALIZE/XMLRPC_VALUE_ToXML
+ * NAME
+ *   XMLRPC_VALUE_ToXML
+ * SYNOPSIS
+ *   char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val)
+ * FUNCTION
+ *   encode XMLRPC_VALUE into XML buffer.  Note that the generated
+ *   buffer will not contain a methodCall.
+ * INPUTS
+ *   val -- previously allocated XMLRPC_VALUE
+ *   buf_len -- length of returned buffer, if not null
+ * RESULT
+ *   char* -- newly allocated buffer containing XML. 
+ *   It is the caller's responsibility to free it.
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ToXML ()
+ *   XMLRPC_VALUE_FromXML ()
+ *   XMLRPC_Free ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) {
+   xml_element *root_elem = XMLRPC_VALUE_to_xml_element(val);
+   char* pRet = NULL;
+
+   if(root_elem) {
+      pRet = xml_elem_serialize_to_string(root_elem, NULL, buf_len);
+      xml_elem_free(root_elem);
+   }
+   return pRet;
+}
+/*******/
+
+/****f* SERIALIZE/XMLRPC_REQUEST_ToXML
+ * NAME
+ *   XMLRPC_REQUEST_ToXML
+ * SYNOPSIS
+ *   char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   encode XMLRPC_REQUEST into XML buffer
+ * INPUTS
+ *   request -- previously allocated XMLRPC_REQUEST
+ *   buf_len -- size of returned buf, if not null
+ * RESULT
+ *   char* -- newly allocated buffer containing XML. 
+ *   It is the caller's responsibility to free it.
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ToXML ()
+ *   XMLRPC_REQUEST_FromXML ()
+ *   XMLRPC_Free ()
+ *   XMLRPC_VALUE_ToXML ()
+ *   XMLRPC_REQUEST
+ * SOURCE
+ */
+char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) {
+   if(request) {
+      xml_element *root_elem = (request->output.version == xmlrpc_version_simple) ? 
+                                        DANDARPC_REQUEST_to_xml_element(request) :
+                                        XMLRPC_REQUEST_to_xml_element(request);
+      char* pRet = NULL;
+
+      if(root_elem) {
+         pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len);
+         xml_elem_free(root_elem);
+      }
+      return pRet;
+   }
+}
+/*******/
+
+/****f* SERIALIZE/XMLRPC_VALUE_FromXML
+ * NAME
+ *   XMLRPC_VALUE_FromXML
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int le
+ * FUNCTION
+ *   Retrieve XMLRPC_VALUE from XML buffer. Note that this will
+ *   ignore any methodCall.  See XMLRPC_REQUEST_FromXML
+ * INPUTS
+ *   in_buf -- character buffer containing XML
+ *   len    -- length of buffer
+ * RESULT
+ *   XMLRPC_VALUE -- newly allocated data, or NULL if error. Should
+ *   be free'd by caller.
+ * SEE ALSO
+ *   XMLRPC_VALUE_ToXML ()
+ *   XMLRPC_REQUEST_FromXML ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options)
+{
+   XMLRPC_VALUE xResponse = NULL;
+   XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options);
+
+   if(req) {
+      xResponse = req->io;
+      XMLRPC_RequestFree(req, 0);
+   }
+   return xResponse;
+}
+/*******/
+
+/* map parser errors to standard xml-rpc errors */
+static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(error) {
+      XMLRPC_ERROR_CODE code;
+      char buf[1024];
+      snprintf(buf, sizeof(buf), 
+               "error occurred at line %i, column %i, byte index %i", 
+               error->line, error->column,
+               error->byte_index);
+
+      /* expat specific errors */
+      switch(error->parser_code) {
+      case XML_ERROR_UNKNOWN_ENCODING:
+         code = xmlrpc_error_parse_unknown_encoding;
+         break;
+      case XML_ERROR_INCORRECT_ENCODING:
+         code = xmlrpc_error_parse_bad_encoding;
+         break;
+      default:
+         code = xmlrpc_error_parse_xml_syntax;
+         break;
+      }
+      xReturn = XMLRPC_UtilityCreateFault(code, buf);
+   }
+   return xReturn;
+}
+
+/****f* SERIALIZE/XMLRPC_REQUEST_FromXML
+ * NAME
+ *   XMLRPC_REQUEST_FromXML
+ * SYNOPSIS
+ *   XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int le
+ * FUNCTION
+ *   Retrieve XMLRPC_REQUEST from XML buffer
+ * INPUTS
+ *   in_buf -- character buffer containing XML
+ *   len    -- length of buffer
+ * RESULT
+ *   XMLRPC_REQUEST -- newly allocated data, or NULL if error. Should
+ *   be free'd by caller.
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ToXML ()
+ *   XMLRPC_VALUE_FromXML ()
+ *   XMLRPC_REQUEST
+ * SOURCE
+ */
+XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options)
+{
+   XMLRPC_REQUEST request = XMLRPC_RequestNew();
+   STRUCT_XML_ELEM_ERROR error = {0};
+
+   if(request) {
+      xml_element *root_elem = xml_elem_parse_buf(in_buf, len, (in_options ? &in_options->xml_elem_opts : NULL), &error);
+
+      if(root_elem) {
+         if(!strcmp(root_elem->name, "simpleRPC")) {
+            request->output.version = xmlrpc_version_simple;
+            xml_element_to_DANDARPC_REQUEST(request, root_elem);
+         }
+         else {
+            request->output.version = xmlrpc_version_1_0;
+            xml_element_to_XMLRPC_REQUEST(request, root_elem);
+         }
+         xml_elem_free(root_elem);
+      }
+      else {
+         if(error.parser_error) {
+            request->error = map_expat_errors(&error);
+         }
+      }
+   }
+
+   return request;
+}
+/*******/
+
+/*-************************
+* End Serialization Funcs *
+**************************/
+
+
+
+/****f* VALUE/XMLRPC_CreateValueEmpty
+ * NAME
+ *   XMLRPC_CreateValueEmpty
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueEmpty ()
+ * FUNCTION
+ *   Create an XML value to be used/modified elsewhere.
+ * INPUTS
+ * RESULT
+ *   XMLRPC_VALUE.  The new value, or NULL on failure.
+ * SEE ALSO
+ *   XMLRPC_CleanupValue ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueEmpty() {
+   XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE));
+   if(v) {
+      v->type = xmlrpc_empty;
+      simplestring_init(&v->id);
+      simplestring_init(&v->str);
+   }
+   return v;
+}
+
+static const char* get_string(const char* buf, int bDup) {
+   if(bDup) {
+      return strdup(buf);
+   }
+   return buf;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueID_Case
+ * NAME
+ *   XMLRPC_SetValueID_Case
+ * SYNOPSIS
+ *   const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case)
+ * FUNCTION
+ *   Assign an ID (key) to an XMLRPC value.
+ * INPUTS
+ *   value     The xml value who's ID we will set.
+ *   id        The desired new id.
+ *   len       length of id string if known, or 0 if unknown.
+ *   id_case   one of XMLRPC_CASE
+ * RESULT
+ *   const char*  pointer to the newly allocated id string, or NULL
+ * SEE ALSO
+ *   XMLRPC_SetValueID ()
+ *   XMLRPC_GetValueID ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_CASE
+ * SOURCE
+ */
+const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) {
+   const char* pRetval = NULL;
+   if(value) {
+      if(id) {
+         simplestring_clear(&value->id);
+         (len > 0) ? simplestring_addn(&value->id, id, len) :
+                     simplestring_add(&value->id, id);
+
+         /* upper or lower case string in place if required. could be a seperate func. */
+         if(id_case == xmlrpc_case_lower || id_case == xmlrpc_case_upper) {
+            int i;
+            for(i = 0; i < value->id.len; i++) {
+               value->id.str[i] = (id_case == xmlrpc_case_lower) ? tolower(value->id.str[i]) : toupper(value->id.str[i]);
+            }
+         }
+
+         pRetval = value->id.str;
+
+#ifdef XMLRPC_DEBUG_REFCOUNT
+         printf("set value id: %s\n", pRetval);
+#endif 
+      }
+   }
+
+   return pRetval;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueString
+ * NAME
+ *   XMLRPC_SetValueString
+ * SYNOPSIS
+ *   const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len)
+ * FUNCTION
+ *   Assign a string value to an XMLRPC_VALUE, and set it to type xmlrpc_string
+ * INPUTS
+ *   value     The xml value who's ID we will set.
+ *   val        The desired new string val.
+ *   len       length of val string if known, or 0 if unknown.
+ * RESULT
+ *   const char*  pointer to the newly allocated value string, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueString ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) {
+   char *pRetval = NULL;
+   if(value && val) {
+      simplestring_clear(&value->str);
+      (len > 0) ? simplestring_addn(&value->str, val, len) :
+                  simplestring_add(&value->str, val);
+      value->type = xmlrpc_string;
+      pRetval = (char *)value->str.str;
+   }
+
+   return pRetval;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueInt
+ * NAME
+ *   XMLRPC_SetValueInt
+ * SYNOPSIS
+ *   void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val)
+ * FUNCTION
+ *   Assign an int value to an XMLRPC_VALUE, and set it to type xmlrpc_int
+ * INPUTS
+ *   value     The xml value who's ID we will set.
+ *   val        The desired new integer value
+ * RESULT
+ * SEE ALSO
+ *   XMLRPC_GetValueInt ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) {
+   if(value) {
+      value->type = xmlrpc_int;
+      value->i = val;
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueBoolean
+ * NAME
+ *   XMLRPC_SetValueBoolean
+ * SYNOPSIS
+ *   void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val)
+ * FUNCTION
+ *   Assign a boolean value to an XMLRPC_VALUE, and set it to type xmlrpc_boolean
+ * INPUTS
+ *   value     The xml value who's value we will set.
+ *   val        The desired new boolean value. [0 | 1]
+ * RESULT
+ * SEE ALSO
+ *   XMLRPC_GetValueBoolean ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) {
+   if(value) {
+      value->type = xmlrpc_boolean;
+      value->i = val ? 1 : 0;
+   }
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_SetIsVector
+ * NAME
+ *   XMLRPC_SetIsVector
+ * SYNOPSIS
+ *   int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type)
+ * FUNCTION
+ *   Set the XMLRPC_VALUE to be a vector (list) type.  The vector may be one of
+ *   [xmlrpc_array | xmlrpc_struct | xmlrpc_mixed].  An array has only index values.
+ *   A struct has key/val pairs.  Mixed allows both index and key/val combinations. 
+ * INPUTS
+ *   value     The xml value who's vector type we will set
+ *   type      New type of vector as enumerated by XMLRPC_VECTOR_TYPE
+ * RESULT
+ *   int       1 if successful, 0 otherwise
+ * SEE ALSO
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_GetVectorType ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VECTOR_TYPE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) {
+   int bSuccess = 0;
+
+   if(value && value->type != xmlrpc_vector) {
+      value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR));
+      if(value->v) {
+         value->v->q = (queue*)malloc(sizeof(queue));
+         if(value->v->q) {
+            Q_Init(value->v->q);
+            value->v->type = type;
+            value->type = xmlrpc_vector;
+            bSuccess = 1;
+         }
+      }
+   }
+
+   return bSuccess;
+}
+/*******/
+
+/****f* VECTOR/XMLRPC_CreateVector
+ * NAME
+ *   XMLRPC_CreateVector
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type)
+ * FUNCTION
+ *   Create a new vector and optionally set an id.
+ * INPUTS
+ *   id        The id of the vector, or NULL
+ *   type      New type of vector as enumerated by XMLRPC_VECTOR_TYPE
+ * RESULT
+ *   XMLRPC_VALUE  The new vector, or NULL on failure.
+ * SEE ALSO
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_SetIsVector ()
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_GetVectorType ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VECTOR_TYPE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) {
+   XMLRPC_VALUE val = NULL;
+
+   val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_VECTOR *pSIV = NULL;
+
+      if(XMLRPC_SetIsVector(val, type)) {
+         if(id) {
+            const char *pSVI = NULL;
+
+            pSVI = XMLRPC_SetValueID(val, id, 0);
+            if(NULL == pSVI) {
+               val = NULL;
+            }
+         }
+      }
+      else {
+         val = NULL;
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/* Not yet implemented.
+ *
+ * This should use a hash to determine if a given target id has already
+ * been appended.  
+ *
+ * Alternately, it could walk the entire vector, but that could be quite
+ * slow for very large lists.
+ */
+static int isDuplicateEntry(XMLRPC_VALUE target, XMLRPC_VALUE source) {
+   return 0;
+}
+
+/****f* VECTOR/XMLRPC_AddValueToVector
+ * NAME
+ *   XMLRPC_AddValueToVector
+ * SYNOPSIS
+ *   int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source)
+ * FUNCTION
+ *   Add (append) an existing XMLRPC_VALUE to a vector.
+ * INPUTS
+ *   target    The target vector
+ *   source    The source value to append
+ * RESULT
+ *   int       1 if successful, else 0
+ * SEE ALSO
+ *   XMLRPC_AddValuesToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * NOTES
+ *   The function will fail and return 0 if an attempt is made to add
+ *   a value with an ID into a vector of type xmlrpc_vector_array. Such
+ *   values can only be added to xmlrpc_vector_struct.
+ * SOURCE
+ */
+int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) {
+   if(target && source) {
+      if(target->type == xmlrpc_vector && target->v && 
+         target->v->q && target->v->type != xmlrpc_vector_none) {
+
+         /* guard against putting value of unknown type into vector */
+         switch(source->type) {
+            case xmlrpc_empty:
+            case xmlrpc_base64:
+            case xmlrpc_boolean:
+            case xmlrpc_datetime:
+            case xmlrpc_double:
+            case xmlrpc_int:
+            case xmlrpc_string:
+            case xmlrpc_vector:
+               /* Guard against putting a key/val pair into an array vector */
+               if( !(source->id.len && target->v->type == xmlrpc_vector_array) ) {
+                  if(isDuplicateEntry(target, source) || Q_PushTail(target->v->q, XMLRPC_CopyValue(source))) {
+                     return 1;
+                  }
+               }
+               else {
+                  fprintf(stderr, "xmlrpc: attempted to add key/val pair to vector of type array\n");
+               }
+               break;
+            default:
+               fprintf(stderr, "xmlrpc: attempted to add value of unknown type to vector\n");
+               break;
+         }
+      }
+   }
+   return 0;
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_AddValuesToVector
+ * NAME
+ *   XMLRPC_AddValuesToVector
+ * SYNOPSIS
+ *   XMLRPC_AddValuesToVector ( target, val1, val2, val3, val(n), 0 )
+ *   XMLRPC_AddValuesToVector( XMLRPC_VALUE, ... )
+ * FUNCTION
+ *   Add (append) a series of existing XMLRPC_VALUE to a vector.
+ * INPUTS
+ *   target    The target vector
+ *   ...       The source value(s) to append.  The last item *must* be 0.
+ * RESULT
+ *   int       1 if successful, else 0
+ * SEE ALSO
+ *   XMLRPC_AddValuesToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * NOTES
+ *   This function may actually return failure after it has already modified
+ *     or added items to target.  You can not trust the state of target
+ *     if this function returns failure.
+ * SOURCE
+ */
+int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) {
+   int iRetval = 0;
+
+   if(target) {
+      if(target->type == xmlrpc_vector) {
+         XMLRPC_VALUE v = NULL;
+         va_list vl;
+
+         va_start(vl, target);
+
+         do {
+            v = va_arg(vl, XMLRPC_VALUE);
+            if(v) {
+               if(!XMLRPC_AddValueToVector(target, v)) {
+                  iRetval = 0;
+                  break;
+               }
+            }
+         } while(v);
+
+         va_end(vl);
+
+         if(NULL == v) {
+            iRetval = 1;
+         }
+      }
+   }
+   return iRetval;
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_VectorGetValueWithID_Case
+ * NAME
+ *   XMLRPC_VectorGetValueWithID_Case
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case)
+ * FUNCTION
+ *   Get value from vector matching id (key)
+ * INPUTS
+ *   vector    The source vector
+ *   id        The key to find
+ *   id_case   Rule for how to match key
+ * RESULT
+ *   int       1 if successful, else 0
+ * SEE ALSO
+ *   XMLRPC_SetValueID_Case ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_CASE_COMPARISON
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) {
+   if(vector && vector->v && vector->v->q) {
+       q_iter qi = Q_Iter_Head_F(vector->v->q);
+
+       while(qi) {
+          XMLRPC_VALUE xIter = Q_Iter_Get_F(qi);
+          if(xIter && xIter->id.str) {
+             if(id_case == xmlrpc_case_sensitive) {
+                if(!strcmp(xIter->id.str, id)) {
+                   return xIter;
+                }
+             }
+             else if(id_case == xmlrpc_case_insensitive) {
+                if(!strcasecmp(xIter->id.str, id)) {
+                   return xIter;
+                }
+             }
+          }
+          qi = Q_Iter_Next_F(qi);
+       }
+   }
+   return NULL;
+}
+/*******/
+
+
+int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value) {
+   if(vector && vector->v && vector->v->q && value) {
+       q_iter qi = Q_Iter_Head_F(vector->v->q);
+
+       while(qi) {
+          XMLRPC_VALUE xIter = Q_Iter_Get_F(qi);
+          if(xIter == value) {
+             XMLRPC_CleanupValue(xIter);
+             Q_Iter_Del(vector->v->q, qi);
+             return 1;
+          }
+          qi = Q_Iter_Next_F(qi);
+       }
+   }
+   return 0;
+}
+
+
+/****f* VALUE/XMLRPC_CreateValueString
+ * NAME
+ *   XMLRPC_CreateValueString
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len)
+ * FUNCTION
+ *   Create an XMLRPC_VALUE, and assign a string to it
+ * INPUTS
+ *   id        The id of the value, or NULL
+ *   val       The desired new string val.
+ *   len       length of val string if known, or 0 if unknown.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueString ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) {
+   XMLRPC_VALUE value = NULL;
+   if(val) {
+      value = XMLRPC_CreateValueEmpty();
+      if(value) {
+         XMLRPC_SetValueString(value, val, len);
+         if(id) {
+            XMLRPC_SetValueID(value, id, 0);
+         }
+      }
+   }
+   return value;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueInt
+ * NAME
+ *   XMLRPC_CreateValueInt
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i)
+ * FUNCTION
+ *   Create an XMLRPC_VALUE, and assign an int to it
+ * INPUTS
+ *   id        The id of the value, or NULL
+ *   i         The desired new int val.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueInt ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueInt(val, i);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueBoolean
+ * NAME
+ *   XMLRPC_CreateValueBoolean
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i)
+ * FUNCTION
+ *   Create an XMLRPC_VALUE, and assign an int to it
+ * INPUTS
+ *   id        The id of the value, or NULL
+ *   i         The desired new int val.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueBoolean ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueBoolean(val, i);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_CleanupValue
+ * NAME
+ *   XMLRPC_CleanupValue
+ * SYNOPSIS
+ *   void XMLRPC_CleanupValue(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Frees all memory allocated for an XMLRPC_VALUE and any of its children (if a vector)
+ * INPUTS
+ *   value     The id of the value to be cleaned up.
+ * RESULT
+ *   void
+ * NOTES
+ *   Normally this function will be called for the topmost vector, thus free-ing
+ *   all children.  If a child of a vector is free'd first, results are undefined.
+ *   Failure to call this function *will* cause memory leaks.
+ *
+ *   Also, this function is implemented using reference counting.  Thus a value
+ *   may be added and freed from multiple parents so long as a reference is added
+ *   first using XMLRPC_CopyValue()
+ * SEE ALSO
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_CopyValue()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_CleanupValue(XMLRPC_VALUE value) {
+   if(value) {
+      if(value->iRefCount > 0) {
+         value->iRefCount --;
+      }
+
+#ifdef XMLRPC_DEBUG_REFCOUNT
+      if(value->id.str) {
+         printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount);
+      }
+      else {
+         printf("decremented refcount of 0x%x, now %i\n", value, value->iRefCount);
+      }
+#endif
+
+      if(value->type == xmlrpc_vector) {
+         if(value->v) {
+            if(value->iRefCount == 0) {
+               XMLRPC_VALUE cur = (XMLRPC_VALUE)Q_Head(value->v->q);
+               while( cur ) {
+                  XMLRPC_CleanupValue(cur);
+   
+                  /* Make sure some idiot didn't include a vector as a child of itself
+                   * and thus it would have already free'd these.
+                   */
+                  if(value->v && value->v->q) {
+                     cur = Q_Next(value->v->q);
+                  }
+                  else {
+                     break;
+                  }
+               }
+
+               Q_Destroy(value->v->q);
+               my_free(value->v->q);
+               my_free(value->v);
+            }
+         }
+      }
+
+
+      if(value->iRefCount == 0) {
+
+         /* guard against freeing invalid types */
+         switch(value->type) {
+            case xmlrpc_empty:
+            case xmlrpc_base64:
+            case xmlrpc_boolean:
+            case xmlrpc_datetime:
+            case xmlrpc_double:
+            case xmlrpc_int:
+            case xmlrpc_string:
+            case xmlrpc_vector:
+#ifdef XMLRPC_DEBUG_REFCOUNT
+               if(value->id.str) {
+                  printf("free'd %s\n", value->id.str);
+               }
+               else {
+                  printf("free'd 0x%x\n", value);
+               }
+#endif 
+               simplestring_free(&value->id);
+               simplestring_free(&value->str);
+
+               memset(value, 0, sizeof(STRUCT_XMLRPC_VALUE));
+               my_free(value);
+               break;
+            default:
+               fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n");
+               break;
+         }
+      }
+   }
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueDateTime
+ * NAME
+ *   XMLRPC_SetValueDateTime
+ * SYNOPSIS
+ *   void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time)
+ * FUNCTION
+ *   Assign time value to XMLRPC_VALUE
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   time      The desired new unix time value (time_t)
+ * RESULT
+ *   void
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime ()
+ *   XMLRPC_SetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) {
+   if(value) {
+      char timeBuf[30];
+      value->type = xmlrpc_datetime;
+      value->i = time;
+
+      timeBuf[0] = 0;
+
+      date_to_ISO8601(time, timeBuf, sizeof(timeBuf));
+
+      if(timeBuf[0]) {
+         simplestring_clear(&value->str);
+         simplestring_add(&value->str, timeBuf);
+      }
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CopyValue
+ * NAME
+ *   XMLRPC_CopyValue
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Make a copy of an XMLRPC_VALUE
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ * RESULT
+ *   XMLRPC_VALUE -- address of the copy
+ * SEE ALSO
+ *   XMLRPC_CleanupValue ()
+ * NOTES
+ *   This function is implemented via reference counting, so the
+ *   returned value is going to be the same as the passed in value.
+ *   The value must be freed the same number of times it is copied
+ *   or there will be a memory leak.
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) {
+   if(value) {
+      value->iRefCount ++;
+#ifdef XMLRPC_DEBUG_REFCOUNT
+      if(value->id.str) {
+         printf("incremented refcount of %s\n", value->id.str);
+      }
+#endif
+   }
+   return value;
+}
+/*******/
+
+
+
+/****f* VALUE/XMLRPC_CreateValueDateTime
+ * NAME
+ *   XMLRPC_CreateValueDateTime
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time)
+ * FUNCTION
+ *   Create new datetime value from time_t
+ * INPUTS
+ *   id        id of the new value, or NULL
+ *   time      The desired unix time value (time_t)
+ * RESULT
+ *   void
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime ()
+ *   XMLRPC_SetValueDateTime ()
+ *   XMLRPC_CreateValueDateTime_ISO8601 ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueDateTime(val, time);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueDateTime_ISO8601
+ * NAME
+ *   XMLRPC_SetValueDateTime_ISO8601
+ * SYNOPSIS
+ *   void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s)
+ * FUNCTION
+ *   Set datetime value from IS08601 encoded string
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   s         The desired new time value
+ * RESULT
+ *   void                                
+ * BUGS
+ *   This function currently attempts to convert the time string to a valid unix time
+ *   value before passing it. Behavior when the string is invalid or out of range
+ *   is not well defined, but will probably result in Jan 1, 1970 (0) being passed.
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) {
+   if(value) {
+      time_t time_val = 0;
+      if(s) {
+         date_from_ISO8601(s, &time_val);
+         XMLRPC_SetValueDateTime(value, time_val);
+      }
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601
+ * NAME
+ *   XMLRPC_CreateValueDateTime_ISO8601
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s)
+ * FUNCTION
+ *   Create datetime value from IS08601 encoded string
+ * INPUTS
+ *   id        The id of the new value, or NULL
+ *   s         The desired new time value
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL if no value created.                                
+ * BUGS
+ *   See XMLRPC_SetValueDateTime_ISO8601 ()
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime_ISO8601 ()
+ *   XMLRPC_SetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueDateTime_ISO8601(val, s);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueBase64
+ * NAME
+ *   XMLRPC_SetValueBase64
+ * SYNOPSIS
+ *   void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len)
+ * FUNCTION
+ *   Set base64 value.  Base64 is useful for transferring binary data, such as an image.
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   s         The desired new binary value
+ *   len       The length of s, or NULL. If buffer is not null terminated, len *must* be passed.
+ * RESULT
+ *   void                                
+ * NOTES
+ *   Data is set/stored/retrieved as passed in, but is base64 encoded for XML transfer, and
+ *   decoded on the other side.  This is transparent to the caller.
+ * SEE ALSO
+ *   XMLRPC_GetValueBase64 ()
+ *   XMLRPC_CreateValueBase64 ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) {
+   if(value && s) {
+      simplestring_clear(&value->str);
+      (len > 0) ? simplestring_addn(&value->str, s, len) :
+                  simplestring_add(&value->str, s);
+      value->type = xmlrpc_base64;
+   }
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_CreateValueBase64
+ * NAME
+ *   XMLRPC_CreateValueBase64
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len)
+ * FUNCTION
+ *   Create base64 value.  Base64 is useful for transferring binary data, such as an image.
+ * INPUTS
+ *   id        id of the new value, or NULL
+ *   s         The desired new binary value
+ *   len       The length of s, or NULL. If buffer is not null terminated, len *must* be passed.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL if error
+ * NOTES
+ *   See XMLRPC_SetValueBase64 ()
+ * SEE ALSO
+ *   XMLRPC_GetValueBase64 ()
+ *   XMLRPC_SetValueBase64 ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueBase64(val, s, len);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueDouble
+ * NAME
+ *   XMLRPC_SetValueDouble
+ * SYNOPSIS
+ *   void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val)
+ * FUNCTION
+ *   Set double (floating point) value.
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   val       The desired new double value
+ * RESULT
+ *   void                                
+ * SEE ALSO
+ *   XMLRPC_GetValueDouble ()
+ *   XMLRPC_CreateValueDouble ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) {
+   if(value) {
+      value->type = xmlrpc_double;
+      value->d = val;
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueDouble
+ * NAME
+ *   XMLRPC_CreateValueDouble
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d)
+ * FUNCTION
+ *   Create double (floating point) value.
+ * INPUTS
+ *   id        id of the newly created value, or NULL
+ *   d         The desired new double value
+ * RESULT
+ *   void                                
+ * SEE ALSO
+ *   XMLRPC_GetValueDouble ()
+ *   XMLRPC_CreateValueDouble ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueDouble(val, d);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueString
+ * NAME
+ *   XMLRPC_GetValueString
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueString(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve string value
+ * INPUTS
+ *   value     source XMLRPC_VALUE of type xmlrpc_string
+ * RESULT
+ *   void                                
+ * SEE ALSO
+ *   XMLRPC_SetValueString ()
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+const char* XMLRPC_GetValueString(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_string) ? value->str.str : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueStringLen
+ * NAME
+ *   XMLRPC_GetValueStringLen
+ * SYNOPSIS
+ *   int XMLRPC_GetValueStringLen(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine length of string value
+ * INPUTS
+ *   value     XMLRPC_VALUE of type xmlrpc_string 
+ * RESULT
+ *   length of string, or 0
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_SetValueString ()
+ *   XMLRPC_GetValueString ()
+ * SOURCE
+ */
+int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) {
+    return ((value) ? value->str.len : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueInt
+ * NAME
+ *   XMLRPC_GetValueInt
+ * SYNOPSIS
+ *   int XMLRPC_GetValueInt(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve integer value.
+ * INPUTS
+ *   value     XMLRPC_VALUE of type xmlrpc_int 
+ * RESULT
+ *   integer value or 0 if value is not valid int
+ * NOTES
+ *   use XMLRPC_GetValueType () to be sure if 0 is real return value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueInt ()
+ *   XMLRPC_CreateValueInt ()
+ * SOURCE
+ */
+int XMLRPC_GetValueInt(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_int) ? value->i : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueBoolean
+ * NAME
+ *   XMLRPC_GetValueBoolean
+ * SYNOPSIS
+ *   int XMLRPC_GetValueBoolean(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve boolean value.
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_boolean
+ * RESULT
+ *   boolean value or 0 if value is not valid boolean
+ * NOTES
+ *   use XMLRPC_GetValueType() to be sure if 0 is real value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueBoolean ()
+ *   XMLRPC_CreateValueBoolean ()
+ * SOURCE
+ */
+int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_boolean) ? value->i : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueDouble
+ * NAME
+ *   XMLRPC_GetValueDouble
+ * SYNOPSIS
+ *   double XMLRPC_GetValueDouble(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve double value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_double
+ * RESULT
+ *   double value or 0 if value is not valid double.
+ * NOTES
+ *   use XMLRPC_GetValueType() to be sure if 0 is real value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueDouble ()
+ *   XMLRPC_CreateValueDouble ()
+ * SOURCE
+ */
+double XMLRPC_GetValueDouble(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_double) ? value->d : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueBase64
+ * NAME
+ *   XMLRPC_GetValueBase64
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve binary value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_base64
+ * RESULT
+ *   pointer to binary value or 0 if value is not valid.
+ * SEE ALSO
+ *   XMLRPC_SetValueBase64 ()
+ *   XMLRPC_CreateValueBase64 ()
+ * NOTES
+ *   Call XMLRPC_GetValueStringLen() to retrieve real length of binary data.  strlen()
+ *   will not be accurate, as returned data may contain embedded nulls.
+ * SOURCE
+ */
+const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_base64) ? value->str.str : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueDateTime
+ * NAME
+ *   XMLRPC_GetValueDateTime
+ * SYNOPSIS
+ *   time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve time_t value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_datetime
+ * RESULT
+ *   time_t value or 0 if value is not valid datetime.
+ * NOTES
+ *   use XMLRPC_GetValueType() to be sure if 0 is real value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueDateTime ()
+ *   XMLRPC_GetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ * SOURCE
+ */
+time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) {
+    return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueDateTime_IOS8601
+ * NAME
+ *   XMLRPC_GetValueDateTime_IOS8601
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueDateTime_IOS8601(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve ISO8601 formatted time value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_datetime
+ * RESULT
+ *   const char* value or 0 if value is not valid datetime.
+ * SEE ALSO
+ *   XMLRPC_SetValueDateTime_IOS8601 ()
+ *   XMLRPC_GetValueDateTime ()
+ *   XMLRPC_CreateValueDateTime_IOS8601 ()
+ * SOURCE
+ */
+const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_datetime) ? value->str.str : 0);
+}
+/*******/
+
+/* Get ID (key) of value or NULL */
+/****f* VALUE/XMLRPC_GetValueID
+ * NAME
+ *   XMLRPC_GetValueID
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueID(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve id (key) of value
+ * INPUTS
+ *   XMLRPC_VALUE of any type
+ * RESULT
+ *   const char* pointer to id of value, or NULL
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_SetValueID()
+ *   XMLRPC_CreateValueEmpty()
+ * SOURCE
+ */
+const char* XMLRPC_GetValueID(XMLRPC_VALUE value) {
+    return (const char*)((value && value->id.len) ? value->id.str : 0);
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_VectorSize
+ * NAME
+ *   XMLRPC_VectorSize
+ * SYNOPSIS
+ *   int XMLRPC_VectorSize(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve size of vector
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   count of items in vector
+ * NOTES
+ *   This is a cheap operation even on large vectors.  Vector size is 
+ *   maintained by queue during add/remove ops.
+ * SEE ALSO
+ *   XMLRPC_AddValueToVector ()
+ * SOURCE
+ */
+int XMLRPC_VectorSize(XMLRPC_VALUE value) {
+   int size = 0;
+   if(value && value->type == xmlrpc_vector && value->v) {
+      size = Q_Size(value->v->q);
+   }
+   return size;
+}
+/*******/
+
+/****f* VECTOR/XMLRPC_VectorRewind
+ * NAME
+ *   XMLRPC_VectorRewind
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value)
+ * FUNCTION
+ *   reset vector to first item
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   first XMLRPC_VALUE in list, or NULL if empty or error.
+ * NOTES
+ *   Be careful to rewind any vector passed in to you if you expect to
+ *   iterate through the entire list.
+ * SEE ALSO
+ *   XMLRPC_VectorNext ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(value && value->type == xmlrpc_vector && value->v) {
+      xReturn = (XMLRPC_VALUE)Q_Head(value->v->q);
+   }
+   return xReturn;
+}
+/*******/
+
+/****f* VECTOR/XMLRPC_VectorNext
+ * NAME
+ *   XMLRPC_VectorNext
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Iterate vector to next item in list.
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   Next XMLRPC_VALUE in vector, or NULL if at end.
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_VectorRewind ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(value && value->type == xmlrpc_vector && value->v) {
+      xReturn = (XMLRPC_VALUE)Q_Next(value->v->q);
+   }
+   return xReturn;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueType
+ * NAME
+ *   XMLRPC_GetValueType
+ * SYNOPSIS
+ *   XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine data type of the XMLRPC_VALUE
+ * INPUTS
+ *   XMLRPC_VALUE target of query
+ * RESULT
+ *   data type of value as enumerated by XMLRPC_VALUE_TYPE
+ * NOTES
+ *   all values are of type xmlrpc_empty until set.
+ * SEE ALSO
+ *   XMLRPC_SetValue*
+ *   XMLRPC_CreateValue*
+ *   XMLRPC_Append*
+ * SOURCE
+ */
+XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) {
+   return value ? value->type : xmlrpc_empty;
+}
+/*******/
+
+/* Vector type accessor */
+/****f* VALUE/XMLRPC_GetVectorType
+ * NAME
+ *   XMLRPC_GetVectorType
+ * SYNOPSIS
+ *   XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine vector type of the XMLRPC_VALUE
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   vector type of value as enumerated by XMLRPC_VECTOR_TYPE
+ * NOTES
+ *   xmlrpc_none is returned if value is not a vector
+ * SEE ALSO
+ *   XMLRPC_SetIsVector ()
+ *   XMLRPC_GetValueType ()
+ * SOURCE
+ */
+XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) {
+   return(value && value->v) ? value->v->type : xmlrpc_none;
+}
+/*******/
+
+
+/*-*******************
+* Begin Server Funcs *
+*********************/
+
+
+/****f* VALUE/XMLRPC_ServerCreate
+ * NAME
+ *   XMLRPC_ServerCreate
+ * SYNOPSIS
+ *   XMLRPC_SERVER XMLRPC_ServerCreate()
+ * FUNCTION
+ *   Allocate/Init XMLRPC Server Resources.
+ * INPUTS
+ *   none
+ * RESULT
+ *   newly allocated XMLRPC_SERVER
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_ServerDestroy ()
+ *   XMLRPC_GetGlobalServer ()
+ * SOURCE
+ */
+XMLRPC_SERVER XMLRPC_ServerCreate() {
+   XMLRPC_SERVER server = calloc(1, sizeof(STRUCT_XMLRPC_SERVER));
+   if(server) {
+      Q_Init(&server->methodlist);
+      Q_Init(&server->docslist);
+
+      /* register system methods */
+      xsm_register(server);
+   }
+   return server;
+}
+/*******/
+
+/* Return global server.  Not locking! Not Thread Safe! */
+/****f* VALUE/XMLRPC_GetGlobalServer
+ * NAME
+ *   XMLRPC_GetGlobalServer
+ * SYNOPSIS
+ *   XMLRPC_SERVER XMLRPC_GetGlobalServer()
+ * FUNCTION
+ *   Allocates a global (process-wide) server, or returns pointer if pre-existing.
+ * INPUTS
+ *   none
+ * RESULT
+ *   pointer to global server, or 0 if error.
+ * NOTES
+ *   ***WARNING*** This function is not thread safe.  It is included only for the very lazy.
+ *   Multi-threaded programs that use this may experience problems.
+ * BUGS
+ *   There is currently no way to cleanup the global server gracefully.
+ * SEE ALSO
+ *   XMLRPC_ServerCreate ()
+ * SOURCE
+ */
+XMLRPC_SERVER XMLRPC_GetGlobalServer() {
+   static XMLRPC_SERVER xsServer = 0;
+   if(!xsServer) {
+      xsServer = XMLRPC_ServerCreate();
+   }
+   return xsServer;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_ServerDestroy
+ * NAME
+ *   XMLRPC_ServerDestroy
+ * SYNOPSIS
+ *   void XMLRPC_ServerDestroy(XMLRPC_SERVER server)
+ * FUNCTION
+ *   Free Server Resources
+ * INPUTS
+ *   server     The server to be free'd
+ * RESULT
+ *   void
+ * NOTES
+ *   This frees the server struct and any methods that have been added.
+ * SEE ALSO
+ *   XMLRPC_ServerCreate ()
+ * SOURCE
+ */
+void XMLRPC_ServerDestroy(XMLRPC_SERVER server) {
+   if(server) {
+      doc_method* dm = Q_Head(&server->docslist);
+      server_method* sm = Q_Head(&server->methodlist);
+      while( dm ) {
+         my_free(dm);
+         dm = Q_Next(&server->docslist);
+      }
+      while( sm ) {
+         if(sm->name) {
+            my_free(sm->name);
+         }
+         if(sm->desc) {
+            XMLRPC_CleanupValue(sm->desc);
+         }
+         my_free(sm);
+         sm = Q_Next(&server->methodlist);
+      }
+      if(server->xIntrospection) {
+         XMLRPC_CleanupValue(server->xIntrospection);
+      }
+
+      Q_Destroy(&server->methodlist);
+      Q_Destroy(&server->docslist);
+      my_free(server);
+   }
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_ServerRegisterMethod
+ * NAME
+ *   XMLRPC_ServerRegisterMethod
+ * SYNOPSIS
+ *   void XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb)
+ * FUNCTION
+ *   Register new XMLRPC method with server
+ * INPUTS
+ *   server     The XMLRPC_SERVER to register the method with
+ *   name       public name of the method
+ *   cb         C function that implements the method
+ * RESULT
+ *   int  - 1 if success, else 0
+ * NOTES
+ *   A C function must be registered for every "method" that the server recognizes.  The
+ *   method name is equivalent to <methodCall><name> method name </name></methodCall> in the
+ *   XML syntax.
+ * SEE ALSO
+ *   XMLRPC_ServerFindMethod ()
+ *   XMLRPC_ServerCallMethod ()
+ * SOURCE
+ */
+int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) {
+   if(server && name && cb) {
+
+      server_method* sm = malloc(sizeof(server_method));
+      
+      if(sm) {
+         sm->name = strdup(name);
+         sm->method = cb;
+         sm->desc = NULL;
+
+         return Q_PushTail(&server->methodlist, sm);
+      }
+   }
+   return 0;
+}
+/*******/
+
+inline server_method* find_method(XMLRPC_SERVER server, const char* name) {
+   server_method* sm;
+
+   q_iter qi = Q_Iter_Head_F(&server->methodlist);
+
+   while( qi ) {
+      sm = Q_Iter_Get_F(qi);
+      if(sm && !strcmp(sm->name, name)) {
+         return sm;
+      }
+      qi = Q_Iter_Next_F(qi);
+   }
+   return NULL;
+}
+
+
+const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) {
+    switch(type) {
+       case xmlrpc_none:
+          return "none";
+       case xmlrpc_empty:
+          return "empty";
+       case xmlrpc_base64:
+          return "base64";
+       case xmlrpc_boolean:
+          return "boolean";
+       case xmlrpc_datetime:
+          return "datetime";
+       case xmlrpc_double:
+          return "double";
+       case xmlrpc_int:
+          return "int";
+       case xmlrpc_string:
+          return "string";
+       case xmlrpc_vector:
+          switch(vtype) {
+             case xmlrpc_vector_none:
+                return "none";
+             case xmlrpc_vector_array:
+                return "array";
+             case xmlrpc_vector_mixed:
+                return "mixed vector (struct)";
+             case xmlrpc_vector_struct:
+                return "struct";
+          }
+    }
+}
+
+/****f* VALUE/XMLRPC_ServerFindMethod
+ * NAME
+ *   XMLRPC_ServerFindMethod
+ * SYNOPSIS
+ *   XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName)
+ * FUNCTION
+ *   retrieve C callback associated with a given method name.
+ * INPUTS       
+ *   server     The XMLRPC_SERVER the method is registered with
+ *   callName   the method to find
+ * RESULT
+ *   previously registered XMLRPC_Callback, or NULL
+ * NOTES
+ *   Typically, this is used to determine if a requested method exists, without actually calling it.
+ * SEE ALSO
+ *   XMLRPC_ServerCallMethod ()
+ *   XMLRPC_ServerRegisterMethod ()
+ * SOURCE
+ */
+XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) {
+   if(server && callName) {
+      q_iter qi = Q_Iter_Head_F(&server->methodlist);
+      while( qi ) {
+         server_method* sm = Q_Iter_Get_F(qi);
+         if(sm && !strcmp(sm->name, callName)) {
+            return sm->method;
+         }
+         qi = Q_Iter_Next_F(qi);
+      }
+   }
+   return NULL;
+}
+/*******/
+
+
+/* Call method specified in request */
+/****f* VALUE/XMLRPC_ServerCallMethod
+ * NAME
+ *   XMLRPC_ServerCallMethod
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData)
+ * FUNCTION
+ *
+ * INPUTS
+ *   server     The XMLRPC_SERVER the method is registered with
+ *   request    the request to handle
+ *   userData   any additional data to pass to the C callback, or NULL
+ * RESULT
+ *   XMLRPC_VALUE allocated by the callback, or NULL
+ * NOTES
+ *   It is typically the caller's responsibility to free the returned value.
+ *
+ *   Often the caller will want to serialize the result as XML, via 
+ *   XMLRPC_VALUE_To_XML () or XMLRPC_REQUEST_To_XML ()
+ * SEE ALSO
+ *   XMLRPC_ServerFindMethod ()
+ *   XMLRPC_ServerRegisterMethod ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) {
+   XMLRPC_VALUE xReturn = NULL;
+
+   /* check for error set during request parsing / generation */
+   if(request && request->error) {
+      xReturn = XMLRPC_CopyValue(request->error);
+   }
+   else if(server && request && request->methodName.str) {
+      XMLRPC_Callback cb = XMLRPC_ServerFindMethod(server, request->methodName.str);
+      if(cb) {
+         xReturn = cb(server, request, userData);
+      }
+      else {
+         xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str);
+      }
+   }
+   return xReturn;
+}
+/*******/
+
+/*-*****************
+* End server funcs *
+*******************/
+
+
+/*-***********************************
+* Begin XMLRPC General Options funcs *
+*************************************/
+
+/* For options used by XMLRPC_VALUE funcs that otherwise do not have
+ * parameters for options.  Kind of gross.  :(
+ */
+typedef struct _xmlrpc_options {
+   XMLRPC_CASE id_case;
+   XMLRPC_CASE_COMPARISON id_case_compare;
+} STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS;
+
+static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() {
+   static STRUCT_XMLRPC_OPTIONS options = {
+      xmlrpc_case_exact,
+      xmlrpc_case_sensitive
+   };
+   return &options;
+}
+
+/****f* VALUE/XMLRPC_GetDefaultIdCase
+ * NAME
+ *   XMLRPC_GetDefaultIdCase
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_GetDefaultIdCase()
+ * FUNCTION
+ *   Gets default case options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   none
+ * RESULT
+ *   XMLRPC_CASE
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_SetDefaultIdCase ()
+ * SOURCE
+ */
+XMLRPC_CASE XMLRPC_GetDefaultIdCase() {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   return options->id_case;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetDefaultIdCase
+ * NAME
+ *   XMLRPC_SetDefaultIdCase
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case)
+ * FUNCTION
+ *   Sets default case options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   id_case   case options as enumerated by XMLRPC_CASE
+ * RESULT
+ *   XMLRPC_CASE -- newly set option
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_GetDefaultIdCase ()
+ * SOURCE
+ */
+XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   options->id_case = id_case;
+   return options->id_case;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetDefaultIdCaseComparison
+ * NAME
+ *   XMLRPC_GetDefaultIdCaseComparison
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_GetDefaultIdCaseComparison( )
+ * FUNCTION
+ *   Gets default case comparison options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   none
+ * RESULT
+ *   XMLRPC_CASE_COMPARISON default
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_SetDefaultIdCaseComparison ()
+ * SOURCE
+ */
+XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   return options->id_case_compare;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetDefaultIdCaseComparison
+ * NAME
+ *   XMLRPC_SetDefaultIdCaseComparison
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_SetDefaultIdCaseComparison( XMLRPC_CASE_COMPARISON id_case_compare )
+ * FUNCTION
+ *   Gets default case comparison options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   id_case_compare  case comparison rule to set as default
+ * RESULT
+ *   XMLRPC_CASE_COMPARISON newly set default
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_GetDefaultIdCaseComparison ()
+ * SOURCE
+ */
+XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case_compare) {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   options->id_case_compare = id_case_compare;
+   return options->id_case_compare;
+}
+/*******/
+
+/*-*********************************
+* End XMLRPC General Options funcs *
+***********************************/
+
+
+/*-******************
+* Utility API funcs *
+********************/
+
+/****f* UTILITY/XMLRPC_UtilityCreateFault
+ * NAME
+ *   XMLRPC_UtilityCreateFault
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_UtilityCreateFault( int fault_code, const char* fault_string )
+ * FUNCTION
+ *   generates a struct containing a string member with id "faultString" and an int member
+ *   with id "faultCode". When using the xmlrpc xml serialization, these will be translated
+ *   to <fault><value><struct>... format.
+ * INPUTS
+ *   fault_code     application specific error code. can be 0.
+ *   fault_string   application specific error string.  cannot be null.
+ * RESULT
+ *   XMLRPC_VALUE a newly created struct vector representing the error, or null on error.
+ * NOTES
+ *   This is a utility function. xmlrpc "faults" are not directly represented in this xmlrpc
+ *   API or data structures. It is the author's view, that this API is intended for simple
+ *   data types, and a "fault" is a complex data type consisting of multiple simple data
+ *   types.  This function is provided for convenience only, the same result could be
+ *   achieved directly by the application.
+ *
+ *   This function now supports some "standardized" fault codes, as specified at.
+ *   http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php.
+ *   If one of these fault codes is received, the description string will automatically
+ *   be prefixed with a standard error string and 2 newlines.  
+ *
+ *   The actual transformation between this complex type and the xml "<fault>" element takes
+ *   place in the xmlrpc to xml serialization layer.  This step is not performed when using the
+ *   simplerpc serialization, meaning that there will be no "<fault>" element in that
+ *   serialization. There will simply be a standard struct with 2 child elements.  
+ *   imho, the "<fault>" element is unnecessary and/or out of place as part of the standard API.
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) {
+   XMLRPC_VALUE xOutput = NULL;
+
+   char* string = NULL;
+   simplestring description;
+   simplestring_init(&description);
+
+   switch (fault_code) {
+   case xmlrpc_error_parse_xml_syntax: string = xmlrpc_error_parse_xml_syntax_str; break;
+   case xmlrpc_error_parse_unknown_encoding: string = xmlrpc_error_parse_unknown_encoding_str; break;
+   case xmlrpc_error_parse_bad_encoding: string = xmlrpc_error_parse_bad_encoding_str; break;
+   case xmlrpc_error_invalid_xmlrpc: string = xmlrpc_error_invalid_xmlrpc_str; break;
+   case xmlrpc_error_unknown_method: string = xmlrpc_error_unknown_method_str; break;
+   case xmlrpc_error_invalid_params: string = xmlrpc_error_invalid_params_str; break;
+   case xmlrpc_error_internal_server: string = xmlrpc_error_internal_server_str; break;
+   case xmlrpc_error_application: string = xmlrpc_error_application_str; break;
+   case xmlrpc_error_system: string = xmlrpc_error_system_str; break;
+   case xmlrpc_error_transport: string = xmlrpc_error_transport_str; break;
+   }
+
+   simplestring_add(&description, string);
+
+   if(string && fault_string) {
+      simplestring_add(&description, "\n\n");
+   }
+   simplestring_add(&description, fault_string);
+
+
+   if(description.len) {
+      xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+
+      XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len);
+      XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code);
+   }
+
+   simplestring_free(&description);
+
+   return xOutput;
+}
+/*******/
+
+
+/****f* UTILITY/XMLRPC_Free
+ * NAME
+ *   XMLRPC_Free
+ * SYNOPSIS
+ *   void XMLRPC_Free(void* mem)
+ * FUNCTION
+ *   frees a block of memory allocated by xmlrpc. 
+ * INPUTS
+ *   mem    memory to free
+ * RESULT
+ *   void
+ * NOTES
+ *   Useful for OS's where memory must be free'd
+ *   in the same library in which it is allocated.
+ * SOURCE
+ */
+void XMLRPC_Free(void* mem) {
+   my_free(mem);
+}
+/*******/
+
+
+/****f* UTILITY/XMLRPC_GetVersionString
+ * NAME
+ *   XMLRPC_GetVersionString
+ * SYNOPSIS
+ *   const char* XMLRPC_GetVersionString()
+ * FUNCTION
+ *   returns library version string
+ * INPUTS
+ *   
+ * RESULT
+ *   const char* 
+ * NOTES
+ * SOURCE
+ */
+const char*  XMLRPC_GetVersionString() {
+   return XMLRPC_VERSION_STR;
+}
+/*******/
+
+
+/*-**********************
+* End Utility API funcs *
+************************/
+
+
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h
new file mode 100644 (file)
index 0000000..2cfe7cb
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef XMLRPC_ALREADY_INCLUDED
+#define XMLRPC_ALREADY_INCLUDED 1
+
+/* includes */
+#include "xml_element.h"
+#include <time.h> /* for time_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* this number, representing the date, must be increased each time the API changes */
+#define XMLRPC_API_NO 20010721
+
+/* this string should be changed with each packaged release */
+#define XMLRPC_VERSION_STR "xmlrpc-epi v. " VERSION
+
+/* where to find more info. shouldn't need to change much */
+#define XMLRPC_HOME_PAGE_STR "http://xmlprc-epi.sourceforge.net/"
+
+
+/****d* VALUE/XMLRPC_VALUE_TYPE
+ * NAME
+ *   XMLRPC_VALUE_TYPE
+ * NOTES
+ *   Defines data types for XMLRPC_VALUE
+ * SEE ALSO
+ *   XMLRPC_VECTOR_TYPE
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+typedef enum _XMLRPC_VALUE_TYPE {
+   xmlrpc_none,                   /* not a value                    */
+   xmlrpc_empty,                  /* empty value, eg NULL           */
+   xmlrpc_base64,                 /* base64 value, eg binary data   */
+   xmlrpc_boolean,                /* boolean  [0 | 1]               */
+   xmlrpc_datetime,               /* datetime [ISO8601 | time_t]    */
+   xmlrpc_double,                 /* double / floating point        */
+   xmlrpc_int,                    /* integer                        */
+   xmlrpc_string,                 /* string                         */
+   xmlrpc_vector                  /* vector, aka list, array        */
+} XMLRPC_VALUE_TYPE;
+/*******/
+
+/****d* VALUE/XMLRPC_VECTOR_TYPE
+ * NAME
+ *   XMLRPC_VECTOR_TYPE
+ * NOTES
+ *   Defines data types for XMLRPC_VECTOR
+ * SEE ALSO
+ *   XMLRPC_VALUE_TYPE
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+typedef enum _XMLRPC_VECTOR_TYPE {
+   xmlrpc_vector_none,            /* not an array                   */
+   xmlrpc_vector_array,           /* no values may have key names   */
+   xmlrpc_vector_mixed,           /* some values may have key names */
+   xmlrpc_vector_struct           /* all values must have key names */
+} XMLRPC_VECTOR_TYPE;
+/*******/
+
+/****d* VALUE/XMLRPC_REQUEST_TYPE
+ * NAME
+ *   XMLRPC_REQUEST_TYPE
+ * NOTES
+ *   Defines data types for XMLRPC_REQUEST
+ * SEE ALSO
+ *   XMLRPC_VALUE_TYPE
+ *   XMLRPC_VECTOR_TYPE
+ * SOURCE
+ */
+typedef enum _xmlrpc_request_type {
+   xmlrpc_request_none,          /* not a valid request            */
+   xmlrpc_request_call,          /* calling/invoking a method      */
+   xmlrpc_request_response,      /* responding to a method call    */
+} XMLRPC_REQUEST_TYPE;
+/*******/
+
+/****d* VALUE/XMLRPC_ERROR_CODE
+ * NAME
+ *   XMLRPC_ERROR_CODE
+ * NOTES
+ *   All existing error codes
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ERROR
+ * SOURCE
+ */
+typedef enum _xmlrpc_error_code {
+   xmlrpc_error_none                      = 0,              /* not an error                                      */
+   xmlrpc_error_parse_xml_syntax          = -32700,
+   xmlrpc_error_parse_unknown_encoding    = -32701,
+   xmlrpc_error_parse_bad_encoding        = -32702,
+   xmlrpc_error_invalid_xmlrpc            = -32600,
+   xmlrpc_error_unknown_method            = -32601,
+   xmlrpc_error_invalid_params            = -32602,
+   xmlrpc_error_internal_server           = -32603,
+   xmlrpc_error_application               = -32500,
+   xmlrpc_error_system                    = -32400,
+   xmlrpc_error_transport                 = -32300
+} XMLRPC_ERROR_CODE;
+/******/
+
+#define xmlrpc_error_parse_xml_syntax_str       "parse error. not well formed."
+#define xmlrpc_error_parse_unknown_encoding_str "parse error. unknown encoding"
+#define xmlrpc_error_parse_bad_encoding_str     "parse error. invalid character for encoding"
+#define xmlrpc_error_invalid_xmlrpc_str         "server error. xml-rpc not conforming to spec"
+#define xmlrpc_error_unknown_method_str         "server error. method not found."
+#define xmlrpc_error_invalid_params_str         "server error. invalid method parameters"
+#define xmlrpc_error_internal_server_str        "server error. internal xmlrpc library error"
+#define xmlrpc_error_application_str            "application error."
+#define xmlrpc_error_system_str                 "system error."
+#define xmlrpc_error_transport_str              "transport error."
+
+
+
+/****d* VALUE/XMLRPC_VERSION
+ * NAME
+ *   XMLRPC_VERSION
+ * NOTES
+ *   Defines xml vocabulary used for generated xml
+ * SEE ALSO
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ *   XMLRPC_REQUEST_To_XML ()
+ * SOURCE
+ */
+typedef enum _xmlrpc_version {
+   xmlrpc_version_none,          /* not a recognized vocabulary    */ 
+   xmlrpc_version_1_0,           /* xmlrpc 1.0 standard vocab      */ 
+   xmlrpc_version_simple = 2,    /* alt more readable vocab        */ 
+   xmlrpc_version_danda = 2      /* same as simple. legacy         */
+} XMLRPC_VERSION;
+/******/
+
+/****s* VALUE/XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * NAME
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * NOTES
+ *   Defines output options for generated xml
+ * SEE ALSO
+ *   XMLRPC_VERSION
+ *   XML_ELEM_OUTPUT_OPTIONS
+ *   XMLRPC_REQUEST_To_XML ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_request_output_options {
+   STRUCT_XML_ELEM_OUTPUT_OPTIONS xml_elem_opts;  /* xml_element specific output options */
+   XMLRPC_VERSION                 version;        /* xml vocabulary to use               */
+} STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS, *XMLRPC_REQUEST_OUTPUT_OPTIONS;
+/******/
+
+/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS
+ * NAME
+ *   XMLRPC_REQUEST_INPUT_OPTIONS
+ * NOTES
+ *   Defines options for reading in xml data
+ * SEE ALSO
+ *   XMLRPC_VERSION
+ *   XML_ELEM_INPUT_OPTIONS
+ *   XMLRPC_REQUEST_From_XML ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_request_input_options {
+   STRUCT_XML_ELEM_INPUT_OPTIONS  xml_elem_opts;  /* xml_element specific output options */
+} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS;
+/******/
+
+/****s* VALUE/XMLRPC_ERROR
+ * NAME
+ *   XMLRPC_ERROR
+ * NOTES
+ *   For the reporting and handling of errors
+ * SOURCE
+ */
+typedef struct _xmlrpc_error {
+   XMLRPC_ERROR_CODE      code;
+   STRUCT_XML_ELEM_ERROR  xml_elem_error;  /* xml_element errors (parser errors) */
+} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR;
+/******/
+
+
+/****d* VALUE/XMLRPC_CASE_COMPARISON
+ * NAME
+ *   XMLRPC_CASE_COMPARISON
+ * NOTES
+ *   Defines case comparison options for XMLRPC_VALUE/VECTOR API's
+ * SEE ALSO
+ *   XMLRPC_CASE
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+typedef enum _xmlrpc_case_comparison {
+   xmlrpc_case_insensitive,      /* use case-insensitive compare */
+   xmlrpc_case_sensitive         /* use case-sensitive compare   */
+} XMLRPC_CASE_COMPARISON;
+/******/
+
+/****d* VALUE/XMLRPC_CASE
+ * NAME
+ *   XMLRPC_CASE
+ * NOTES
+ *   Defines case behavior when setting IDs in XMLRPC_VALUE API's
+ * SEE ALSO
+ *   XMLRPC_CASE_COMPARISON
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+typedef enum _xmlrpc_case {
+   xmlrpc_case_exact,            /* leave case alone             */
+   xmlrpc_case_lower,            /* lower-case id                */
+   xmlrpc_case_upper             /* upper-case id                */
+} XMLRPC_CASE;
+/******/
+
+/* if you don't like these defaults, you can set them with XMLRPC_SetDefaultIdCase*() */
+#define XMLRPC_DEFAULT_ID_CASE              XMLRPC_GetDefaultIdCase()
+#define XMLRPC_DEFAULT_ID_CASE_SENSITIVITY  XMLRPC_GetDefaultIdCaseComparison()
+
+/* opaque (non-public) types. defined locally in xmlrpc.c */
+typedef struct _xmlrpc_request* XMLRPC_REQUEST;
+typedef struct _xmlrpc_server* XMLRPC_SERVER;
+typedef struct _xmlrpc_value* XMLRPC_VALUE;
+
+/****d* VALUE/XMLRPC_Callback
+ * NAME
+ *   XMLRPC_Callback
+ * NOTES
+ *   Function prototype for user defined method handlers (callbacks).
+ * SEE ALSO
+ *   XMLRPC_ServerRegisterMethod ()
+ *   XMLRPC_ServerCallMethod ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+typedef XMLRPC_VALUE (*XMLRPC_Callback)(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+/******/
+
+/* ID Case Defaults */
+XMLRPC_CASE XMLRPC_GetDefaultIdCase();
+XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case);
+XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison();
+XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case);
+
+/* Vector manipulation */
+int XMLRPC_VectorSize(XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value);
+int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type);
+int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source);
+int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...);
+int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case);
+
+
+/* Create values */
+XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int truth);
+XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len);
+XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time);
+XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s);
+XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double f);
+XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i);
+XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* s, int len);
+XMLRPC_VALUE XMLRPC_CreateValueEmpty();
+XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type);
+
+/* Cleanup values */
+void XMLRPC_CleanupValue(XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value);
+
+/* Set Values */
+void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time);
+void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s);
+void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val);
+void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val);
+void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val);
+const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* s, int len);
+void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len);
+const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case);
+#define XMLRPC_SetValueID(value, id, len) XMLRPC_SetValueID_Case(value, id, len, XMLRPC_DEFAULT_ID_CASE)
+
+/* Get Values */
+const char* XMLRPC_GetValueString(XMLRPC_VALUE value);
+int XMLRPC_GetValueStringLen(XMLRPC_VALUE value);
+int XMLRPC_GetValueInt(XMLRPC_VALUE value);
+int XMLRPC_GetValueBoolean(XMLRPC_VALUE value);
+double XMLRPC_GetValueDouble(XMLRPC_VALUE value);
+const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value);
+time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value);
+const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value);
+const char* XMLRPC_GetValueID(XMLRPC_VALUE value);
+
+/* Type introspection */
+XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v);
+XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v);
+
+/* Parsing and Creating XML */
+XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options);
+XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options);
+char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int *buf_len);
+char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len);
+
+/* Request manipulation funcs */
+const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName);
+const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request);
+XMLRPC_REQUEST XMLRPC_RequestNew();
+void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO);
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output);
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request);
+XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data);
+XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request);
+XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type);
+XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request);
+
+/* Server Creation/Destruction; Method Registration and Invocation */
+XMLRPC_SERVER XMLRPC_ServerCreate();
+XMLRPC_SERVER XMLRPC_GetGlobalServer();   /* better to use XMLRPC_ServerCreate if you can */
+void XMLRPC_ServerDestroy(XMLRPC_SERVER server);
+int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb);
+XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName);
+XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData);
+
+#include "xmlrpc_introspection.h"
+
+/* Public Utility funcs */
+XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string);
+void XMLRPC_Free(void* mem);
+const char*  XMLRPC_GetVersionString();
+
+/****d* VALUE/XMLRPC_MACROS
+ * NAME
+ *   Some Helpful Macros
+ * NOTES
+ *   Some macros for making life easier.  Should be self-explanatory.
+ * SEE ALSO
+ *   XMLRPC_AddValueToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+
+/* Append values to vector */
+#define XMLRPC_VectorAppendString(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id, s, len))
+#define XMLRPC_VectorAppendBase64(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id, s, len))
+#define XMLRPC_VectorAppendDateTime(vector, id, time) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time))
+#define XMLRPC_VectorAppendDateTime_ISO8601(vector, id, s) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id, s))
+#define XMLRPC_VectorAppendDouble(vector, id, f) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id, f))
+#define XMLRPC_VectorAppendInt(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id, i))
+#define XMLRPC_VectorAppendBoolean(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id, i))
+
+/* Get named values from vector */
+#define XMLRPC_VectorGetValueWithID(vector, id) XMLRPC_VectorGetValueWithID_Case(vector, id, XMLRPC_DEFAULT_ID_CASE_SENSITIVITY)
+#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id))
+
+/******/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XMLRPC_ALREADY_INCLUDED */
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.m4 b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.m4
new file mode 100644 (file)
index 0000000..18d3606
--- /dev/null
@@ -0,0 +1,16 @@
+CPPFLAGS="$CPPFLAGS -DVERSION=\\\"0.42\\\""
+
+AC_DEFUN(XMLRPC_CHECKS,[       
+
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_LN_S])
+AC_REQUIRE([AC_PROG_RANLIB])
+
+AC_DEFINE(UNDEF_THREADS_HACK,,[ ])
+
+XMLRPC_HEADER_CHECKS
+XMLRPC_TYPE_CHECKS
+XMLRPC_FUNCTION_CHECKS
+
+PHP_FAST_OUTPUT($ext_builddir/libxmlrpc/Makefile)
+])
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.c b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.c
new file mode 100644 (file)
index 0000000..5b15d4b
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+/****h* ABOUT/xmlrpc_introspection
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * HISTORY
+ *   4/10/2001 -- danda -- initial introspection support
+ * TODO
+ * NOTES
+ *******/
+
+
+#include "queue.h"
+#include "xmlrpc.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+/* forward declarations for static (non public, non api) funcs */
+static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+
+
+/*-**********************************
+* Introspection Callbacks (methods) *
+************************************/
+
+/* iterates through a list of structs and finds the one with key "name" matching
+ * needle.  slow, would benefit from a struct key hash.
+ */
+inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) {
+   XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list);
+   while(xIter) {
+      const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name);
+      if(name && !strcmp(name, needle)) {
+         return xIter;
+      }
+      xIter = XMLRPC_VectorNext(list);
+   }
+   return NULL;
+}
+
+
+/* iterates through docs callbacks and calls any that have not yet been called */
+static void check_docs_loaded(XMLRPC_SERVER server, void* userData) {
+   if(server) {
+      q_iter qi = Q_Iter_Head_F(&server->docslist);
+      while( qi ) {
+         doc_method* dm = Q_Iter_Get_F(qi);
+         if(dm && !dm->b_called) {
+            dm->method(server, userData);
+            dm->b_called = 1;
+         }
+         qi = Q_Iter_Next_F(qi);
+      }
+   }
+}
+
+
+/* utility function for xi_system_describe_methods_cb */
+inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) {
+   if(method) {
+      server_method* sm = find_method(server, method);
+      if(sm) {
+         XMLRPC_AddValueToVector(vector, sm->desc);
+      }
+   }
+}
+
+
+
+/* system.describeMethods() callback */
+static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
+   XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+   XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array);
+   XMLRPC_VALUE xTypeList = NULL;
+   int bAll = 1;
+
+   /* lazy loading of introspection data */
+   check_docs_loaded(server, userData);
+
+   xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
+
+   XMLRPC_AddValueToVector(xResponse, xTypeList);
+   XMLRPC_AddValueToVector(xResponse, xMethodList);
+
+   /* check if we have any param */
+   if(xParams) {
+      /* check if string or vector (1 or n) */
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams);
+      if(type == xmlrpc_string) {
+         /* just one.  spit it out. */
+         describe_method(server, xMethodList, XMLRPC_GetValueString(xParams));
+         bAll = 0;
+      }
+      else if(type == xmlrpc_vector) {
+         /* multiple.  spit all out */
+         XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams);
+         while(xIter) {
+            describe_method(server, xMethodList, XMLRPC_GetValueString(xIter));
+            xIter = XMLRPC_VectorNext(xParams);
+         }
+         bAll = 0;
+      }
+   }
+
+   /* otherwise, default to sending all methods */
+   if(bAll) {
+      q_iter qi = Q_Iter_Head_F(&server->methodlist);
+      while( qi ) {
+         server_method* sm = Q_Iter_Get_F(qi);
+         if(sm) {
+            XMLRPC_AddValueToVector(xMethodList, sm->desc);
+         }
+         qi = Q_Iter_Next_F(qi);
+      }
+   }
+   
+   return xResponse;
+}
+
+/* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */
+static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
+
+   q_iter qi = Q_Iter_Head_F(&server->methodlist);
+   while( qi ) {
+      server_method* sm = Q_Iter_Get_F(qi);
+      if(sm) {
+         XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0);
+      }
+      qi = Q_Iter_Next_F(qi);
+   }
+   return xResponse;
+}
+
+/* this complies with system.methodSignature as defined at 
+ * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html 
+ */
+static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
+   XMLRPC_VALUE xResponse = NULL;
+
+   /* lazy loading of introspection data */
+   check_docs_loaded(server, userData);
+
+   if(method) {
+      server_method* sm = find_method(server, method);
+      if(sm && sm->desc) {
+         XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
+         XMLRPC_VALUE xIter, xParams, xSig, xSigIter;
+         const char* type;
+
+         /* array of possible signatures.  */
+         xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
+
+         /* find first signature */
+         xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures);
+         xSigIter = XMLRPC_VectorRewind( xSig );
+
+         /* iterate through sigs */
+         while(xSigIter) {
+            /* first type is the return value */
+            type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind(
+                                                 XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), 
+                                                xi_token_type);
+            XMLRPC_AddValueToVector(xTypesArray, 
+                                    XMLRPC_CreateValueString(NULL, 
+                                                             type ? type : type_to_str(xmlrpc_none, 0), 
+                                    0));
+
+            /* the rest are parameters */
+            xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params);
+            xIter = XMLRPC_VectorRewind(xParams);
+
+            /* iter through params, adding to types array */
+            while(xIter) {
+               XMLRPC_AddValueToVector(xTypesArray,
+                                       XMLRPC_CreateValueString(NULL, 
+                                                                XMLRPC_VectorGetStringWithID(xIter, xi_token_type),
+                                                                0));
+               xIter = XMLRPC_VectorNext(xParams);
+            }
+
+            /* add types for this signature */
+            XMLRPC_AddValueToVector(xResponse, xTypesArray);
+
+            xSigIter = XMLRPC_VectorNext( xSig );
+         }
+      }
+   }
+
+   return xResponse;
+}
+
+/* this complies with system.methodHelp as defined at 
+ * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html 
+ */
+static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
+   XMLRPC_VALUE xResponse = NULL;
+
+   /* lazy loading of introspection data */
+   check_docs_loaded(server, userData);
+
+   if(method) {
+      server_method* sm = find_method(server, method);
+      if(sm && sm->desc) {
+         const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose);
+
+         /* returns a documentation string, or empty string */
+         xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0);
+      }
+   }
+
+   return xResponse;
+}
+
+/*-**************************************
+* End Introspection Callbacks (methods) *
+****************************************/
+
+
+/*-************************
+* Introspection Utilities *
+**************************/
+
+/* performs registration of introspection methods */
+void xi_register_system_methods(XMLRPC_SERVER server) {
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb);
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb);
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb);
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb);
+}
+
+/* describe a value (param, return, type) */
+static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) {
+   XMLRPC_VALUE xParam = NULL;
+   if(id || desc) {
+      xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+      XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0);
+      XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0);
+      XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0);
+      if(optional != 2) {
+         XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional);
+      }
+      if(optional == 1 && default_val) {
+         XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0);
+      }
+      XMLRPC_AddValueToVector(xParam, sub_params);
+   }
+   return xParam;
+}
+
+
+/* convert an xml tree conforming to spec <url tbd> to  XMLRPC_VALUE
+ * suitable for use with XMLRPC_ServerAddIntrospectionData
+ */
+XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) {
+   XMLRPC_VALUE xReturn = NULL;
+
+   if(el->name) {
+      const char* name = NULL;
+      const char* type = NULL;
+      const char* basetype = NULL;
+      const char* desc = NULL;
+      const char* def = NULL;
+      int optional = 0;
+      xml_element_attr* attr_iter = Q_Head(&el->attrs);
+
+      /* grab element attributes up front to save redundant while loops */
+      while(attr_iter) {
+         if(!strcmp(attr_iter->key, "name")) {
+            name = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "type")) {
+            type = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "basetype")) {
+            basetype = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "desc")) {
+            desc = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "optional")) {
+            if(attr_iter->val && !strcmp(attr_iter->val, "yes")) {
+               optional = 1;
+            }
+         }
+         else if(!strcmp(attr_iter->key, "default")) {
+            def = attr_iter->val;
+         }
+         attr_iter = Q_Next(&el->attrs);
+      }
+
+      /* value and typeDescription behave about the same */
+      if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) {
+         XMLRPC_VALUE xSubList = NULL;
+         const char* ptype = !strcmp(el->name, "value") ? type : basetype;
+         if(ptype) {
+            if(Q_Size(&el->children) &&
+               !strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed")) {
+               xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array);
+
+               if(xSubList) {
+                  xml_element* elem_iter = Q_Head(&el->children);
+                  while(elem_iter) {
+                     XMLRPC_AddValueToVector(xSubList, 
+                                             xml_element_to_method_description(elem_iter, err));
+                     elem_iter = Q_Next(&el->children);
+                  }
+               }
+            }
+            xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList);
+         }
+      }
+
+      /* these three kids are about equivalent */
+      else if(!strcmp(el->name, "params") || 
+              !strcmp(el->name, "returns") || 
+              !strcmp(el->name, "signature")) {
+         if(Q_Size(&el->children)) {
+            xml_element* elem_iter = Q_Head(&el->children);
+            xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct);
+
+
+            while(elem_iter) {
+               XMLRPC_AddValueToVector(xReturn, 
+                                       xml_element_to_method_description(elem_iter, err));
+               elem_iter = Q_Next(&el->children);
+            }
+         }
+      }
+
+
+      else if(!strcmp(el->name, "methodDescription")) {
+         xml_element* elem_iter = Q_Head(&el->children);
+         xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+
+         XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0);
+
+         while(elem_iter) {
+            XMLRPC_AddValueToVector(xReturn, 
+                                    xml_element_to_method_description(elem_iter, err));
+            elem_iter = Q_Next(&el->children);
+         }
+      }
+
+      /* items are slightly special */
+      else if(!strcmp(el->name, "item")) {
+         xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len);
+      }
+
+      /* sure.  we'll let any ol element with children through */
+      else if(Q_Size(&el->children)) {
+         xml_element* elem_iter = Q_Head(&el->children);
+         xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed);
+
+         while(elem_iter) {
+            XMLRPC_AddValueToVector(xReturn, 
+                                    xml_element_to_method_description(elem_iter, err));
+            elem_iter = Q_Next(&el->children);
+         }
+      }
+
+      /* or anything at all really, so long as its got some text. 
+       * no reason being all snotty about a spec, right? 
+       */
+      else if(el->name && el->text.len) {
+         xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len);
+      }
+   }
+
+   return xReturn;
+}
+
+/*-****************************
+* End Introspection Utilities *
+******************************/
+
+
+
+/*-******************
+* Introspection API *
+********************/
+
+
+/****f* VALUE/XMLRPC_IntrospectionCreateDescription
+ * NAME
+ *   XMLRPC_IntrospectionCreateDescription
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err)
+ * FUNCTION
+ *   converts raw xml describing types and methods into an
+ *   XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData()
+ * INPUTS
+ *   xml - xml data conforming to introspection spec at <url tbd>
+ *   err - optional pointer to error struct. filled in if error occurs and not NULL.
+ * RESULT
+ *   XMLRPC_VALUE - newly created value, or NULL if fatal error.
+ * BUGS
+ *   Currently does little or no validation of xml.
+ *   Only parse errors are currently reported in err, not structural errors.
+ * SEE ALSO
+ *   XMLRPC_ServerAddIntrospectionData ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) {
+   XMLRPC_VALUE xReturn = NULL;
+   xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL);
+
+   if(root) {
+      xReturn = xml_element_to_method_description(root, err);
+
+      xml_elem_free(root);
+   }
+
+   return xReturn;
+
+}
+/*******/
+
+
+/****f* SERVER/XMLRPC_ServerAddIntrospectionData
+ * NAME
+ *   XMLRPC_ServerAddIntrospectionData
+ * SYNOPSIS
+ *   int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc)
+ * FUNCTION
+ *   updates server with additional introspection data
+ * INPUTS
+ *   server - target server
+ *   desc - introspection data, should be a struct generated by 
+ *          XMLRPC_IntrospectionCreateDescription ()
+ * RESULT
+ *   int - 1 if success, else 0
+ * NOTES
+ *  - function will fail if neither typeList nor methodList key is present in struct.
+ *  - if method or type already exists, it will be replaced.
+ *  - desc is never freed by the server.  caller is responsible for cleanup.
+ * BUGS
+ *   - horribly slow lookups. prime candidate for hash improvements.
+ *   - uglier and more complex than I like to see for API functions.
+ * SEE ALSO
+ *   XMLRPC_ServerAddIntrospectionData ()
+ *   XMLRPC_ServerRegisterIntrospectionCallback ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) {
+   int bSuccess = 0;
+   if(server && desc) {
+      XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList");
+      XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList");
+      XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
+
+      if(xNewMethods) {
+         XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods);
+
+         while(xMethod) {
+            const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name);
+            server_method* sm = find_method(server, name);
+
+            if(sm) {
+               if(sm->desc) {
+                  XMLRPC_CleanupValue(sm->desc);
+               }
+               sm->desc = XMLRPC_CopyValue(xMethod);
+               bSuccess = 1;
+            }
+
+            xMethod = XMLRPC_VectorNext(xNewMethods);
+         }
+      }
+      if(xNewTypes) {
+         if(!xServerTypes) {
+            if(!server->xIntrospection) {
+               server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+            }
+
+            XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes);
+            bSuccess = 1;
+         }
+         else {
+            XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes);
+            while(xIter) {
+               /* get rid of old values */
+               XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name));
+               if(xPrev) {
+                  XMLRPC_VectorRemoveValue(xServerTypes, xPrev);
+               }
+               XMLRPC_AddValueToVector(xServerTypes, xIter);
+               bSuccess = 1;
+               xIter = XMLRPC_VectorNext(xNewTypes);
+            }
+         }
+      }
+   }
+   return bSuccess;
+}
+/*******/
+
+
+/****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback
+ * NAME
+ *   XMLRPC_ServerRegisterIntrospectionCallback
+ * SYNOPSIS
+ *   int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb)
+ * FUNCTION
+ *   registers a callback for lazy generation of introspection data
+ * INPUTS
+ *   server - target server
+ *   cb - callback that will generate introspection data
+ * RESULT
+ *   int - 1 if success, else 0
+ * NOTES
+ *   parsing xml and generating introspection data is fairly expensive, thus a
+ *   server may wish to wait until this data is actually requested before generating
+ *   it. Any number of callbacks may be registered at any time.  A given callback
+ *   will only ever be called once, the first time an introspection request is
+ *   processed after the time of callback registration.
+ * SEE ALSO
+ *   XMLRPC_ServerAddIntrospectionData ()
+ *   XMLRPC_IntrospectionCreateDescription ()
+ * SOURCE
+ */
+int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) {
+   int bSuccess = 0;
+   if(server && cb) {
+
+      doc_method* dm = calloc(1, sizeof(doc_method));
+      
+      if(dm) {
+         dm->method = cb;
+         dm->b_called = 0;
+
+         if(Q_PushTail(&server->docslist, dm)) {
+            bSuccess = 1;
+         }
+         else {
+            my_free(dm);
+         }
+      }
+   }
+   return 0;
+}
+/*******/
+
+/*-**********************
+* End Introspection API *
+************************/
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.h
new file mode 100644 (file)
index 0000000..656e441
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* IMPORTANT!
+ *
+ * only public (official API) things should be in this file. Anything else
+ * should go in <group>_private.h, or in the appropriate .c file.
+ */
+
+
+#ifndef __XI_INTROSPECTION_H
+/*
+ * Avoid include redundancy.
+ */
+#define __XI_INTROSPECTION_H
+
+/*----------------------------------------------------------------------------
+ * xmlrpc_introspection.h
+ *
+ * Purpose:
+ *   define public introspection API
+ * Comments:
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+ #define xi_token_params "params"
+ #define xi_token_returns "returns"
+ #define xi_token_related "related"
+ #define xi_token_sub "sub"
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+ /****d* VALUE/XMLRPC_IntrospectionCallback
+ * NAME
+ *   XMLRPC_IntrospectionCallback
+ * NOTES
+ *   Function prototype for lazy documentation generation (not generated until requested).
+ * SOURCE
+ */
+typedef void (*XMLRPC_IntrospectionCallback)(XMLRPC_SERVER server, void* userData);
+/******/
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR error);
+int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc);
+int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+
+
+#endif /* __XI_INTROSPECTION_H */
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h
new file mode 100644 (file)
index 0000000..7b97fa7
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Dan Libby, Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* IMPORTANT!
+ *
+ * only non-public things should be in this file.  It is fine for any .c file
+ * in xmlrpc/src to include it, but users of the public API should never
+ * include it, and thus *.h files that are part of the public API should
+ * never include it, or they would break if this file is not present.
+ */
+
+
+#ifndef __XI_INTROSPECTION_PRIVATE_H
+/*
+ * Avoid include redundancy.
+ */
+#define __XI_INTROSPECTION_PRIVATE_H
+
+/*----------------------------------------------------------------------------
+ * xmlrpc_introspection_private.h
+ *
+ * Purpose:
+ *   define non-public introspection routines
+ * Comments:
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+#define xi_token_default                    "default"
+#define xi_token_description                "description"
+#define xi_token_name                       "name"
+#define xi_token_optional                   "optional"
+#define xi_token_params                     "params"
+#define xi_token_purpose                    "purpose"
+#define xi_token_returns                    "returns"
+#define xi_token_signatures                 "signatures"
+#define xi_token_type                       "type"
+#define xi_token_version                    "version"
+#define xi_token_empty                      ""
+#define xi_token_system_describe_methods    "system.describeMethods"
+#define xi_token_system_list_methods        "system.listMethods"
+#define xi_token_system_method_help         "system.methodHelp"
+#define xi_token_system_method_signature    "system.methodSignature"
+
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+typedef struct _doc_method {
+   XMLRPC_IntrospectionCallback         method;
+   int                                  b_called;
+} doc_method; 
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+void xi_register_system_methods(XMLRPC_SERVER server);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+
+#endif /* __XI_INTROSPECTION_PRIVATE_H */
+
+
+
+
diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h
new file mode 100644 (file)
index 0000000..0b4a078
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* only non-public things should be in this file.  It is fine for any .c file
+ * in xmlrpc/src to include it, but users of the public API should never
+ * include it, and thus *.h files that are part of the public API should
+ * never include it, or they would break if this file is not present.
+ */
+
+#ifndef XMLRPC_PRIVATE_ALREADY_INCLUDED
+/*
+ * Avoid include redundancy.
+ */
+#define XMLRPC_PRIVATE_ALREADY_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*----------------------------------------------------------------------------
+ * xmlrpc_private.h
+ *
+ * Purpose:
+ *   define non-public intra-library routines & data
+ * Comments:
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+
+
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+/* Some of these are typedef'd in xmlrpc.h for public use */
+
+typedef struct _xmlrpc_vector* XMLRPC_VECTOR;
+
+/****s* VALUE/XMLRPC_VALUE
+ * NAME
+ *   XMLRPC_VALUE
+ * NOTES
+ *   A value of variable data type. The most important object in this API.  :)
+ *
+ *  This struct is opaque to callers and should be accessed only via accessor functions.
+ * SEE ALSO
+ *   XMLRPC_REQUEST
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_value {
+   XMLRPC_VALUE_TYPE type; /* data type of this value                        */
+   XMLRPC_VECTOR v;        /* vector type specific info                      */
+   simplestring str;       /* string value buffer                            */
+   simplestring id;        /* id of this value.  possibly empty.             */
+   int i;                  /* integer value.                                 */
+   double d;               /* double value                                   */
+   int iRefCount;          /* So we know when we can delete the value      . */
+} STRUCT_XMLRPC_VALUE;
+/******/
+
+/****s* VALUE/XMLRPC_REQUEST
+ * NAME
+ *   XMLRPC_REQUEST
+ * NOTES
+ *   Internal representation of an XML request.
+ *
+ *  This struct is opaque to callers and should be accessed only via accessor functions.
+ *  
+ * SEE ALSO
+ *   XMLRPC_VALUE
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_request {
+   XMLRPC_VALUE                         io;           /* data associated with this request */
+   simplestring                         methodName;   /* name of method being called       */
+   XMLRPC_REQUEST_TYPE                  request_type; /* type of request                   */
+   STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS output;       /* xml output options                */
+   XMLRPC_VALUE                         error;        /* error codes                       */
+} STRUCT_XMLRPC_REQUEST;
+/******/
+
+/* Vector type. Used by XMLRPC_VALUE.  Never visible to users of the API. */
+typedef struct _xmlrpc_vector {
+   XMLRPC_VECTOR_TYPE type;                           /* vector type                       */
+   const char* id;                                    /* ??? unused?                       */
+   queue *q;                                          /* list of child values              */
+} STRUCT_XMLRPC_VECTOR;
+/******/
+
+/****s* VALUE/XMLRPC_SERVER
+ * NAME
+ *   XMLRPC_SERVER
+ * NOTES
+ *   internal representation of an xmlrpc server
+ *
+ *  This struct is opaque to callers and should be accessed only via accessor functions.
+ *  
+ * SEE ALSO
+ *   XMLRPC_ServerCreate ()
+ *   XMLRPC_ServerDestroy ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_server {
+   queue methodlist;                                  /* list of callback methods          */
+   queue docslist;                                    /* list of introspection callbacks   */
+   XMLRPC_VALUE xIntrospection;
+} STRUCT_XMLRPC_SERVER;
+/******/
+
+typedef struct _server_method {
+   char*                   name;
+   XMLRPC_VALUE            desc;
+   XMLRPC_Callback         method;
+} server_method;
+
+
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+server_method* find_method(XMLRPC_SERVER server, const char* name);
+const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+#define my_free(thing)  if(thing) {free(thing); thing = 0;}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* XMLRPC_PRIVATE_ALREADY_INCLUDED */
+
diff --git a/ext/rpc/xmlrpc/php_config.h.in b/ext/rpc/xmlrpc/php_config.h.in
new file mode 100644 (file)
index 0000000..2c8a1bf
--- /dev/null
@@ -0,0 +1,11 @@
+/* php_config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if your C compiler doesn't accept -c and -o together.  */
+#undef NO_MINUS_C_MINUS_O
+
+/* Whether you have XMLRPC */
+#undef HAVE_XMLRPC
+
+/* Whether to build xmlrpc as dynamic module */
+#undef COMPILE_DL_XMLRPC
+
diff --git a/ext/rpc/xmlrpc/php_xmlrpc.h b/ext/rpc/xmlrpc/php_xmlrpc.h
new file mode 100644 (file)
index 0000000..77fbd57
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+  This file is part of, or distributed with, libXMLRPC - a C library for 
+  xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* auto-generated portions of this file are also subject to the php license */
+
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors:                                                             |
+   |                                                                      |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef _PHP_XMLRPC_H
+#define _PHP_XMLRPC_H
+
+/* You should tweak config.m4 so this symbol (or some else suitable)
+   gets defined.
+*/
+#if 1 /* HAVE_XMLRPC */
+
+extern zend_module_entry xmlrpc_module_entry;
+#define phpext_xmlrpc_ptr &xmlrpc_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_XMLRPC_API __declspec(dllexport)
+#else
+#define PHP_XMLRPC_API
+#endif
+
+PHP_MINIT_FUNCTION(xmlrpc);
+PHP_MSHUTDOWN_FUNCTION(xmlrpc);
+PHP_RINIT_FUNCTION(xmlrpc);
+PHP_RSHUTDOWN_FUNCTION(xmlrpc);
+PHP_MINFO_FUNCTION(xmlrpc);
+
+PHP_FUNCTION(xmlrpc_encode);
+PHP_FUNCTION(xmlrpc_decode);
+PHP_FUNCTION(xmlrpc_decode_request);
+PHP_FUNCTION(xmlrpc_encode_request);
+PHP_FUNCTION(xmlrpc_get_type);
+PHP_FUNCTION(xmlrpc_set_type);
+PHP_FUNCTION(xmlrpc_server_create);
+PHP_FUNCTION(xmlrpc_server_destroy);
+PHP_FUNCTION(xmlrpc_server_register_method);
+PHP_FUNCTION(xmlrpc_server_call_method);
+PHP_FUNCTION(xmlrpc_parse_method_descriptions);
+PHP_FUNCTION(xmlrpc_server_add_introspection_data);
+PHP_FUNCTION(xmlrpc_server_register_introspection_callback);
+
+/* Fill in this structure and use entries in it
+   for thread safety instead of using true globals.
+*/
+typedef struct {
+       /* You can use the next one as type if your module registers any
+          resources. Oh, you can of course rename it to something more
+          suitable, add list entry types or remove it if it not needed.
+          It's just an example.
+       */
+        int le_xmlrpc_server;
+} php_xmlrpc_globals;
+
+/* In every function that needs to use variables in php_xmlrpc_globals,
+   do call XMLRPCLS_FETCH(); after declaring other variables used by
+   that function, and always refer to them as XMLRPCG(variable).
+   You are encouraged to rename these macros something shorter, see
+   examples in any other php module directory.
+*/
+
+#ifdef ZTS
+#define XMLRPCG(v) (xmlrpc_globals->v)
+#define XMLRPCLS_FETCH() php_xmlrpc_globals *xmlrpc_globals = ts_resource(gd_xmlrpc_id)
+#else
+#define XMLRPCG(v) (xmlrpc_globals.v)
+#define XMLRPCLS_FETCH()
+#endif
+
+#else
+
+#define phpext_xmlrpc_ptr NULL
+
+#endif
+
+#endif /* _PHP_XMLRPC_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/ext/rpc/xmlrpc/xmlrpc-epi-php.c b/ext/rpc/xmlrpc/xmlrpc-epi-php.c
new file mode 100644 (file)
index 0000000..589adaa
--- /dev/null
@@ -0,0 +1,1421 @@
+/*
+  This file is part of, or distributed with, libXMLRPC - a C library for 
+  xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* auto-generated portions of this file are also subject to the php license */
+
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors:                                                             |
+   |                                                                      |
+   +----------------------------------------------------------------------+
+ */
+
+#include "php.h"
+#include "php_ini.h"
+#include "php_xmlrpc.h"
+#include "xmlrpc.h"
+
+/* You should tweak config.m4 so this symbol (or some else suitable)
+   gets defined.
+*/
+
+#ifdef ZTS
+int xmlrpc_globals_id;
+#else
+php_xmlrpc_globals xmlrpc_globals;
+#endif
+
+
+/* Every user visible function must have an entry in xmlrpc_functions[].
+*/
+function_entry xmlrpc_functions[] = {
+   PHP_FE(xmlrpc_encode,    NULL) 
+   PHP_FE(xmlrpc_decode,    NULL)
+   PHP_FE(xmlrpc_decode_request, NULL)
+   PHP_FE(xmlrpc_encode_request, NULL)
+   PHP_FE(xmlrpc_get_type,    NULL)
+   PHP_FE(xmlrpc_set_type,    NULL)
+   PHP_FE(xmlrpc_server_create, NULL)
+   PHP_FE(xmlrpc_server_destroy, NULL)
+   PHP_FE(xmlrpc_server_register_method, NULL)
+   PHP_FE(xmlrpc_server_call_method, NULL)
+   PHP_FE(xmlrpc_parse_method_descriptions, NULL)
+   PHP_FE(xmlrpc_server_add_introspection_data, NULL)
+   PHP_FE(xmlrpc_server_register_introspection_callback, NULL)
+   {NULL, NULL, NULL}      /* Must be the last line in xmlrpc_functions[] */
+};
+
+zend_module_entry xmlrpc_module_entry = {
+   "xmlrpc",
+   xmlrpc_functions,
+   PHP_MINIT(xmlrpc),
+   PHP_MSHUTDOWN(xmlrpc),
+   PHP_RINIT(xmlrpc),      /* Replace with NULL if there's nothing to do at request start */
+   PHP_RSHUTDOWN(xmlrpc),  /* Replace with NULL if there's nothing to do at request end */
+   PHP_MINFO(xmlrpc),
+   STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_XMLRPC
+ZEND_GET_MODULE(xmlrpc)
+#endif
+
+/* Remove comments and fill if you need to have entries in php.ini
+PHP_INI_BEGIN()
+PHP_INI_END()
+*/
+
+/*******************************
+* local structures and defines *
+*******************************/
+
+// per server data
+typedef struct _xmlrpc_server_data {
+   pval* method_map;
+   pval* introspection_map;
+   XMLRPC_SERVER server_ptr;
+} xmlrpc_server_data;
+
+
+// how to format output
+typedef struct _php_output_options {
+   int b_php_out;
+   STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
+} php_output_options;
+
+// data passed to C callback
+typedef struct _xmlrpc_callback_data {
+   pval* xmlrpc_method;
+   pval* php_function;
+   pval* caller_params;
+   pval* return_data;
+   xmlrpc_server_data* server;
+   char php_executed;
+} xmlrpc_callback_data;
+
+#define PHP_EXT_VERSION "0.41"
+
+// output options
+#define OUTPUT_TYPE_KEY "output_type"
+#define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
+#define OUTPUT_TYPE_VALUE_PHP "php"
+#define OUTPUT_TYPE_VALUE_XML "xml"
+
+#define VERBOSITY_KEY "verbosity"
+#define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
+#define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
+#define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
+#define VERBOSITY_VALUE_PRETTY "pretty"
+
+#define ESCAPING_KEY "escaping"
+#define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
+#define ESCAPING_VALUE_CDATA "cdata"
+#define ESCAPING_VALUE_NON_ASCII "non-ascii"
+#define ESCAPING_VALUE_NON_PRINT "non-print"
+#define ESCAPING_VALUE_MARKUP "markup"
+
+#define VERSION_KEY "version"
+#define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
+#define VERSION_VALUE_SIMPLE "simple"
+#define VERSION_VALUE_XMLRPC "xmlrpc"
+
+#define ENCODING_KEY "encoding"
+#define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
+#define ENCODING_DEFAULT "iso-8859-1"
+
+// value types
+#define OBJECT_TYPE_ATTR "xmlrpc_type"
+#define OBJECT_VALUE_ATTR "scalar"
+
+
+
+/***********************
+* forward declarations *
+***********************/
+XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue);
+static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
+
+/*********************
+* startup / shutdown *
+*********************/
+
+static void destroy_server_data(xmlrpc_server_data *server) {
+   if(server) {
+      XMLRPC_ServerDestroy(server->server_ptr);
+
+      zval_dtor(server->method_map);
+      FREE_ZVAL(server->method_map);
+
+      zval_dtor(server->introspection_map);
+      FREE_ZVAL(server->introspection_map);
+
+      efree(server);
+   }
+}
+
+/* called when server is being destructed. either when xmlrpc_server_destroy
+ * is called, or when request ends.
+ */
+static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc) {
+   if(rsrc && rsrc->ptr) {
+      destroy_server_data((xmlrpc_server_data*)rsrc->ptr);
+   }
+}
+
+/* module init */
+PHP_MINIT_FUNCTION(xmlrpc)
+{
+/* Remove comments if you have entries in php.ini
+        REGISTER_INI_ENTRIES();
+*/
+   XMLRPCG(le_xmlrpc_server) = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
+
+   return SUCCESS;
+}
+
+/* module shutdown */
+PHP_MSHUTDOWN_FUNCTION(xmlrpc)
+{
+/* Remove comments if you have entries in php.ini
+        UNREGISTER_INI_ENTRIES();
+*/
+   return SUCCESS;
+}
+
+/* Remove if there's nothing to do at request start */
+PHP_RINIT_FUNCTION(xmlrpc)
+{
+   return SUCCESS;
+}
+
+/* Remove if there's nothing to do at request end */
+PHP_RSHUTDOWN_FUNCTION(xmlrpc)
+{
+   return SUCCESS;
+}
+
+/* display info in phpinfo() */
+PHP_MINFO_FUNCTION(xmlrpc)
+{
+   php_info_print_table_start();
+   php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
+   php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
+   php_info_print_table_row(2, "author", "Dan Libby");
+   php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
+   php_info_print_table_row(2, "open sourced by", "Epinions.com");
+   php_info_print_table_end();
+   
+       /*
+   DISPLAY_INI_ENTRIES();
+       */
+}
+
+/*******************
+* random utilities *
+*******************/
+
+/* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
+ * Could easily be further generalized to work with objects.
+ */
+static int add_long(pval* list, char* id, int num) {
+   if(id) return add_assoc_long(list, id, num);
+   else   return add_next_index_long(list, num);
+}
+
+static int add_double(pval* list, char* id, double num) {
+   if(id) return add_assoc_double(list, id, num);
+   else   return add_next_index_double(list, num);
+}
+
+static int add_string(pval* list, char* id, char* string, int duplicate) {
+   if(id) return add_assoc_string(list, id, string, duplicate);
+   else   return add_next_index_string(list, string, duplicate);
+}
+
+static int add_stringl(pval* list, char* id, char* string, uint length, int duplicate) {
+   if(id) return add_assoc_stringl(list, id, string, length, duplicate);
+   else   return add_next_index_stringl(list, string, length, duplicate);
+}
+
+static int add_pval(pval* list, const char* id, pval** val) {
+   if(list && val) {
+      if(id) return zend_hash_update(list->value.ht, (char*)id, strlen(id)+1, (void *) val, sizeof(pval **), NULL);
+      else   return zend_hash_next_index_insert(list->value.ht, (void *) val, sizeof(pval **), NULL); 
+   }
+}
+
+#if ZEND_MODULE_API_NO >= 20001222
+#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
+#else
+#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index)
+#endif 
+
+
+/*************************
+* input / output options *
+*************************/
+
+/* parse an array (user input) into output options suitable for use by xmlrpc engine
+ * and determine whether to return data as xml or php vars
+ */
+static void set_output_options(php_output_options* options, pval* output_opts) {
+
+   if(options) {
+
+      /* defaults */
+      options->b_php_out = 0;
+      options->xmlrpc_out.version = xmlrpc_version_1_0;
+      options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
+      options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
+      options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
+
+     if(output_opts && output_opts->type == IS_ARRAY) {
+        pval** val;
+
+        /* verbosity of generated xml */
+        if(zend_hash_find(output_opts->value.ht, 
+                          OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_PHP)) {
+                 options->b_php_out = 1;
+              }
+              else if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_XML)) {
+                 options->b_php_out = 0;
+              }
+           }
+        }
+
+        /* verbosity of generated xml */
+        if(zend_hash_find(output_opts->value.ht, 
+                          VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NO_WHITE_SPACE)) {
+                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
+              }
+              else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NEWLINES_ONLY)) {
+                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
+              }
+              else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_PRETTY)) {
+                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
+              }
+           }
+        }
+
+        /* version of xml to output */
+        if(zend_hash_find(output_opts->value.ht, 
+                          VERSION_KEY, VERSION_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, VERSION_VALUE_XMLRPC)) {
+                 options->xmlrpc_out.version = xmlrpc_version_1_0;
+              }
+              else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SIMPLE)) {
+                 options->xmlrpc_out.version = xmlrpc_version_simple;
+              }
+           }
+        }
+
+        /* encoding code set */
+        if(zend_hash_find(output_opts->value.ht, 
+                          ENCODING_KEY, ENCODING_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              options->xmlrpc_out.xml_elem_opts.encoding = estrdup((*val)->value.str.val);
+           }
+        }
+
+        /* escaping options */
+        if(zend_hash_find(output_opts->value.ht, 
+                          ESCAPING_KEY, ESCAPING_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           /* multiple values allowed.  check if array */
+           if((*val)->type == IS_ARRAY) {
+              pval** iter_val;
+              zend_hash_internal_pointer_reset((*val)->value.ht);
+              options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
+              while(1) {
+                 if(zend_hash_get_current_data((*val)->value.ht, (void**)&iter_val) == SUCCESS) {
+                    if((*iter_val)->type == IS_STRING && (*iter_val)->value.str.val) {
+                       if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_CDATA)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
+                       }
+                       else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
+                       }
+                       else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
+                       }
+                       else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_MARKUP)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
+                       }
+                    }
+                 }
+                 else {
+                    break;
+                 }
+
+                 zend_hash_move_forward((*val)->value.ht);
+              }
+           }
+           /* else, check for single value */
+           else if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_CDATA)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
+              }
+              else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
+              }
+              else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
+              }
+              else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_MARKUP)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
+              }
+           }
+        }
+     }
+   }
+}
+
+
+/******************
+* encode / decode *
+******************/
+
+/* php arrays have no distinction between array and struct types.
+ * they even allow mixed.  Thus, we determine the type by iterating
+ * through the entire array and figuring out each element.
+ * room for some optimation here if we stop after a specific # of elements.
+ */
+static XMLRPC_VECTOR_TYPE determine_vector_type(HashTable *ht) {
+    int bArray = 0, bStruct = 0, bMixed = 0;
+    unsigned long num_index;
+    char* my_key;
+
+    zend_hash_internal_pointer_reset(ht);
+    while(1) {
+       int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
+       if(res == HASH_KEY_IS_LONG) {
+           if(bStruct) {
+               bMixed = 1;
+               break;
+           }
+           bArray = 1;
+       }
+       else if(res == HASH_KEY_NON_EXISTANT) {
+          break;
+       }
+       else if(res == HASH_KEY_IS_STRING) {
+           if(bArray) {
+               bMixed = 1;
+               break;
+           }
+           bStruct = 1;
+       }
+
+       zend_hash_move_forward(ht);
+    }
+    return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
+}
+
+/* recursively convert php values into xmlrpc values */
+static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int depth) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(in_val) {
+      pval* val = NULL;
+      XMLRPC_VALUE_TYPE type = get_pval_xmlrpc_type(in_val, &val);
+      if(val) {
+         switch(type) {
+            case xmlrpc_base64:
+               if(val->type == IS_NULL) {
+                  xReturn = XMLRPC_CreateValueBase64(key, "", 1);
+               }
+               else {
+                  xReturn = XMLRPC_CreateValueBase64(key, val->value.str.val, val->value.str.len);
+               }
+               break;
+            case xmlrpc_datetime:
+               convert_to_string(val);
+               xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, val->value.str.val);
+               break;
+            case xmlrpc_boolean:
+               convert_to_boolean(val);
+               xReturn = XMLRPC_CreateValueBoolean(key, val->value.lval);
+               break;
+            case xmlrpc_int:
+               convert_to_long(val);
+               xReturn = XMLRPC_CreateValueInt(key, val->value.lval);
+               break;
+            case xmlrpc_double:
+               convert_to_double(val);
+               xReturn = XMLRPC_CreateValueDouble(key, val->value.dval);
+               break;
+            case xmlrpc_string:
+               convert_to_string(val);
+               xReturn = XMLRPC_CreateValueString(key, val->value.str.val, val->value.str.len);
+               break;
+            case xmlrpc_vector:
+               {
+                  unsigned long num_index;
+                  pval** pIter;
+                  char* my_key;
+
+                  convert_to_array(val);
+
+                  xReturn = XMLRPC_CreateVector(key, determine_vector_type(val->value.ht));
+
+                  zend_hash_internal_pointer_reset(val->value.ht);
+                  while(1) {
+                     int res = my_zend_hash_get_current_key(val->value.ht, &my_key, &num_index);
+                     if(res == HASH_KEY_IS_LONG) {
+                        if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) {
+                           XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++));
+                        }
+                     }
+                     else if(res == HASH_KEY_NON_EXISTANT) {
+                        break;
+                     }
+                     else if(res == HASH_KEY_IS_STRING) {
+                        if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) {
+                           XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++));
+                        }
+                     }
+
+                     zend_hash_move_forward(val->value.ht);
+                  }
+               }
+               break;
+            default:
+               break;
+         }
+      }
+   }
+   return xReturn;
+}
+
+static XMLRPC_VALUE PHP_to_XMLRPC(pval* root_val) {
+   return PHP_to_XMLRPC_worker(NULL, root_val, 0);
+}
+
+/* recursively convert xmlrpc values into php values */
+static pval* XMLRPC_to_PHP(XMLRPC_VALUE el) {
+   pval* elem = NULL;
+   char* pBuf;
+   const char* pStr;
+
+   if(el) {
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
+
+      MAKE_STD_ZVAL(elem); /* init. very important.  spent a frustrating day finding this out. */
+
+      switch(type) {
+         case xmlrpc_empty:
+            elem->type = IS_NULL;
+            break;
+         case xmlrpc_string:
+            pStr = XMLRPC_GetValueString(el);
+            if(pStr) {
+               elem->value.str.len = XMLRPC_GetValueStringLen(el);
+               elem->value.str.val = estrndup(pStr, elem->value.str.len);
+               elem->type = IS_STRING;
+            }
+            break;
+         case xmlrpc_int:
+            elem->value.lval = XMLRPC_GetValueInt(el);
+            elem->type = IS_LONG;
+            break;
+         case xmlrpc_boolean:
+            elem->value.lval = XMLRPC_GetValueBoolean(el);
+            elem->type = IS_BOOL;
+            break;
+         case xmlrpc_double:
+            elem->value.dval = XMLRPC_GetValueDouble(el);
+            elem->type = IS_DOUBLE;
+            break;
+         case xmlrpc_datetime:
+            elem->value.str.len = XMLRPC_GetValueStringLen(el);
+            elem->value.str.val = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), elem->value.str.len);
+            elem->type = IS_STRING;
+            break;
+         case xmlrpc_base64:
+            pStr = XMLRPC_GetValueBase64(el);
+            if(pStr) {
+               elem->value.str.len = XMLRPC_GetValueStringLen(el);
+               elem->value.str.val = estrndup(pStr, elem->value.str.len);
+               elem->type = IS_STRING;
+            }
+            break;
+         case xmlrpc_vector:
+            if(array_init(elem) == SUCCESS) {
+               XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
+
+               while( xIter ) {
+                  pval* val = XMLRPC_to_PHP(xIter);
+                  if(val) {
+                     add_pval(elem, XMLRPC_GetValueID(xIter), &val);
+                  }
+                  xIter = XMLRPC_VectorNext(el);
+               }
+            }
+            break;
+         default:
+            break;
+      }
+      set_pval_xmlrpc_type(elem, type);
+   }
+   return elem;
+}
+
+/* {{{ proto string xmlrpc_encode_request(string method, mixed params)
+   generate xml for a method request */
+PHP_FUNCTION(xmlrpc_encode_request) {
+   XMLRPC_REQUEST xRequest = NULL;
+   pval* method, *vals, *out_opts;
+   char* outBuf;
+   php_output_options out;
+
+   if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || 
+       getParameters(ht, ARG_COUNT(ht), &method, &vals, &out_opts) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0);
+
+   if(return_value_used) {
+      xRequest = XMLRPC_RequestNew();
+
+      if(xRequest) {
+         XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
+         if(method->type == IS_NULL) {
+            XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
+         }
+         else {
+            XMLRPC_RequestSetMethodName(xRequest, method->value.str.val);
+            XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
+         }
+         if(vals->type != IS_NULL) {
+             XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals));
+         }
+
+         outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
+         if(outBuf) {
+            RETVAL_STRING(outBuf, 1);
+            free(outBuf);
+         }
+         XMLRPC_RequestFree(xRequest, 1);
+      }
+   }
+}
+
+/* {{{ proto string xmlrpc_encode(mixed value)
+   generate xml for a PHP value */
+PHP_FUNCTION(xmlrpc_encode)
+{
+   XMLRPC_VALUE xOut = NULL;
+   pval* arg1, *out_opts;
+   php_output_options out;
+   char* outBuf;
+
+   if( !(ARG_COUNT(ht) == 1)  || 
+       getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   if( return_value_used ) {
+      /* convert native php type to xmlrpc type */
+      xOut = PHP_to_XMLRPC(arg1);
+
+      /* generate raw xml from xmlrpc data */
+      outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
+
+      if(xOut) {
+         if(outBuf) {
+            RETVAL_STRING(outBuf, 1);
+            free(outBuf);
+         }
+         /* cleanup */
+         XMLRPC_CleanupValue(xOut);
+      }
+   }
+}
+
+
+/* }}} */
+
+pval* decode_request_worker(pval* xml_in, pval* encoding_in, pval* method_name_out) {
+   pval* retval = NULL;
+   XMLRPC_REQUEST response;
+   STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {0};
+   opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in->value.str.val) : ENCODING_DEFAULT;
+
+   /* generate XMLRPC_REQUEST from raw xml */
+   response = XMLRPC_REQUEST_FromXML(xml_in->value.str.val, xml_in->value.str.len, &opts);
+   if(response) {
+      /* convert xmlrpc data to native php types */
+      retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
+
+      if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
+         if(method_name_out) {
+            convert_to_string(method_name_out);
+            method_name_out->type = IS_STRING;
+            method_name_out->value.str.val = estrdup(XMLRPC_RequestGetMethodName(response));
+            method_name_out->value.str.len = strlen(method_name_out->value.str.val);
+         }
+      }
+
+      /* dust, sweep, and mop */
+      XMLRPC_RequestFree(response, 1);
+   }
+   return retval;
+}
+
+/* {{{ proto array xmlrpc_decode_request(string xml, string& method, [string encoding])
+   decode xml into native php types */
+PHP_FUNCTION(xmlrpc_decode_request)
+{
+   pval* xml, *method, *encoding = NULL;
+
+   if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &xml, &method, &encoding) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+#if ZEND_MODULE_API_NO < 20010901
+   if (!ParameterPassedByReference(ht,2)) {
+       zend_error(E_WARNING,"second argument to xmlrpc_decode_request() passed by value, expecting reference");
+   }
+#endif
+
+   convert_to_string(xml);
+   convert_to_string(method);
+   if(ARG_COUNT(ht) == 3) {
+      convert_to_string(encoding);
+   }
+
+   if(return_value_used) {
+      pval* retval = decode_request_worker(xml, encoding, method);
+      if(retval) {
+         *return_value = *retval;
+         zval_copy_ctor(return_value);
+      }
+   }
+}
+/* }}} */
+
+
+/* {{{ proto array xmlrpc_decode(string xml, [string encoding])
+   decode xml into native php types */
+PHP_FUNCTION(xmlrpc_decode)
+{
+   pval* arg1, *arg2 = NULL;
+
+   if( !(ARG_COUNT(ht) == 1 || ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg1, &arg2) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   convert_to_string(arg1);
+   if(ARG_COUNT(ht) == 2) {
+      convert_to_string(arg2);
+   }
+
+   if(return_value_used) {
+      pval* retval = decode_request_worker(arg1, arg2, NULL);
+      if(retval) {
+         *return_value = *retval;
+         zval_copy_ctor(return_value);
+      }
+   }
+}
+/* }}} */
+
+
+/*************************
+* server related methods *
+*************************/
+
+/* {{{ proto handle xmlrpc_server_create()
+   create an xmlrpc server */
+PHP_FUNCTION(xmlrpc_server_create) {
+   if(ARG_COUNT(ht) != 0) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   if(return_value_used) {
+      pval *method_map, *introspection_map;
+      MAKE_STD_ZVAL(method_map);
+      MAKE_STD_ZVAL(introspection_map);
+
+      if(array_init(method_map) == SUCCESS && array_init(introspection_map) == SUCCESS) {
+         /* allocate server data.  free'd in destroy_server_data() */
+         xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
+
+         if(server) {
+            server->method_map = method_map;
+            server->introspection_map = introspection_map;
+            server->server_ptr = XMLRPC_ServerCreate();
+
+            XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
+
+            /* store for later use */
+            ZEND_REGISTER_RESOURCE(return_value,server, XMLRPCG(le_xmlrpc_server));
+         }
+      }
+   }
+}
+
+/* {{{ proto void xmlrpc_server_destroy(handle server)
+   destroy server resources */
+PHP_FUNCTION(xmlrpc_server_destroy) {
+   pval* arg1;
+   int bSuccess = FAILURE;
+
+   if(ARG_COUNT(ht) != 1 || getParameters(ht, 1, &arg1) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   if(arg1->type == IS_RESOURCE) {
+      int type;
+
+      xmlrpc_server_data *server = zend_list_find(arg1->value.lval, &type);
+
+      if(server && type == XMLRPCG(le_xmlrpc_server)) {
+         bSuccess = zend_list_delete(arg1->value.lval);
+
+         /* called by hashtable destructor
+          * destroy_server_data(server);
+          */
+      }
+   }
+   RETVAL_LONG(bSuccess == SUCCESS);
+}
+
+           
+/* called by xmlrpc C engine as method handler for all registered methods.
+ * it then calls the corresponding PHP function to handle the method.
+ */
+static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) {
+   pval *retval_ptr;
+   xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
+   pval* xmlrpc_params;
+   pval* callback_params[3];
+
+   /* convert xmlrpc to native php types */
+   xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
+
+   /* setup data hoojum */
+   callback_params[0] = pData->xmlrpc_method;
+   callback_params[1] = xmlrpc_params;
+   callback_params[2] = pData->caller_params;
+
+   /* Use same C function for all methods */
+
+   /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
+   call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params);
+
+   pData->php_executed = 1;
+}
+
+/* called by the C server when it first receives an introspection request.  We pass this on to
+ * our PHP listeners, if any
+ */
+static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) {
+   pval *retval_ptr, **php_function;
+   pval* callback_params[1];
+   xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
+
+   MAKE_STD_ZVAL(retval_ptr);
+   retval_ptr->type = IS_NULL;
+
+   /* setup data hoojum */
+   callback_params[0] = pData->caller_params;
+
+   /* loop through and call all registered callbacks */
+   zend_hash_internal_pointer_reset(pData->server->introspection_map->value.ht);
+   while(1) {
+      if(zend_hash_get_current_data(pData->server->introspection_map->value.ht, 
+                                    (void**)&php_function) == SUCCESS) {
+
+         /* php func prototype: function string user_func($user_params) */
+         if(call_user_function(CG(function_table), NULL, *php_function, 
+                               retval_ptr, 1, callback_params) == SUCCESS) {
+            XMLRPC_VALUE xData;
+            STRUCT_XMLRPC_ERROR err = {0};
+
+            /* return value should be a string */
+            convert_to_string(retval_ptr);
+
+            xData = XMLRPC_IntrospectionCreateDescription(retval_ptr->value.str.val, &err);
+
+            if(xData) {
+               if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
+                  zend_error(E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", (*php_function)->value.str.val);
+               }
+               XMLRPC_CleanupValue(xData);
+            }
+            else {
+               /* could not create description */
+               if(err.xml_elem_error.parser_code) {
+                  zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to add introspection data returned from %s()", 
+                             err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, (*php_function)->value.str.val);
+               }
+               else {
+                  zend_error(E_WARNING, "Unable to add introspection data returned from %s()", 
+                             (*php_function)->value.str.val);
+               }
+            }
+         }
+         else {
+            /* user func failed */
+            zend_error(E_WARNING, "Error calling user introspection callback: %s()", (*php_function)->value.str.val);
+         }
+      }
+      else {
+         break;
+      }
+
+      zend_hash_move_forward(pData->server->introspection_map->value.ht);
+   }
+
+   /* so we don't call the same callbacks ever again */
+   zend_hash_clean(pData->server->introspection_map->value.ht);
+}
+
+/* {{{ proto boolean xmlrpc_server_register_method(handle server, string method_name, string function)
+   register a php function to handle method matching method_name */
+PHP_FUNCTION(xmlrpc_server_register_method) {
+
+   pval* method_key, *method_name, *handle, *method_name_save;
+   int type;
+   xmlrpc_server_data* server;
+
+   /* get some params.  should be 3 */
+   if( !(ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &handle, &method_key, &method_name) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if(type == XMLRPCG(le_xmlrpc_server)) {
+      /* register with C engine. every method just calls our standard callback, 
+       * and it then dispatches to php as necessary
+       */
+      if(XMLRPC_ServerRegisterMethod(server->server_ptr, method_key->value.str.val, php_xmlrpc_callback)) {
+         /* save for later use */
+         MAKE_STD_ZVAL(method_name_save);
+         *method_name_save = *method_name;
+         zval_copy_ctor(method_name_save);
+
+         /* register our php method */
+         add_pval(server->method_map, method_key->value.str.val, &method_name_save);
+
+         RETURN_BOOL(1);
+      }
+   }
+   RETURN_BOOL(0);
+}
+
+
+/* {{{ proto boolean xmlrpc_server_register_introspection_callback(handle server, string function)
+   register a php function to generate documentation */
+PHP_FUNCTION(xmlrpc_server_register_introspection_callback) {
+
+   pval* method_key, *method_name, *handle, *method_name_save;
+   int type;
+   xmlrpc_server_data* server;
+
+   /* get some params.  should be 2 */
+   if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &method_name) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if(type == XMLRPCG(le_xmlrpc_server)) {
+      {
+         /* save for later use */
+         MAKE_STD_ZVAL(method_name_save);
+         *method_name_save = *method_name;
+         zval_copy_ctor(method_name_save);
+
+         /* register our php method */
+         add_pval(server->introspection_map, NULL, &method_name_save);
+
+         RETURN_BOOL(1);
+      }
+   }
+   RETURN_BOOL(0);
+}
+
+
+/* this function is itchin for a re-write */
+
+/* {{{ proto mixed xmlrpc_server_call_method(handle server, string xml, mixed user_data, [array output_options])
+   parse xml request and call method */
+PHP_FUNCTION(xmlrpc_server_call_method) {
+   xmlrpc_callback_data data = {0};
+   XMLRPC_REQUEST xRequest;
+   STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
+   xmlrpc_server_data* server;
+   pval *rawxml, *caller_params, *handle, *output_opts;
+   int type;
+   php_output_options out;
+
+   /* get params. 3 or 4 params ok */
+   if(ARG_COUNT(ht) == 4) {
+      if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params, &output_opts) != SUCCESS) {
+         WRONG_PARAM_COUNT;
+      }
+
+      /* user output options */
+      set_output_options(&out, output_opts);
+   }
+   else if(ARG_COUNT(ht) == 3) {
+      if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params) != SUCCESS) {
+         WRONG_PARAM_COUNT;
+      }
+      /* user output options */
+      set_output_options(&out, NULL);
+   }
+   else {
+      WRONG_PARAM_COUNT;
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if(type == XMLRPCG(le_xmlrpc_server)) {
+      /* HACK: use output encoding for now */
+      input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
+
+      /* generate an XMLRPC_REQUEST from the raw xml input */
+      xRequest = XMLRPC_REQUEST_FromXML(rawxml->value.str.val, rawxml->value.str.len, &input_opts);
+
+      if(xRequest) {
+
+         /* check if we have a method name -- indicating success and all manner of good things */
+         if(XMLRPC_RequestGetMethodName(xRequest)) {
+            pval** php_function, *returned = NULL;
+            XMLRPC_VALUE xAnswer = NULL;
+            MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important.  spent a frustrating day finding this out. */
+            MAKE_STD_ZVAL(data.return_data);
+            data.return_data->type = IS_NULL;  /* in case value is never init'd, we don't dtor to think it is a string or something */
+            data.xmlrpc_method->type = IS_NULL;
+
+            /* setup some data to pass to the callback function */
+            data.xmlrpc_method->value.str.val = estrdup(XMLRPC_RequestGetMethodName(xRequest));
+            data.xmlrpc_method->value.str.len = strlen(data.xmlrpc_method->value.str.val);
+            data.xmlrpc_method->type = IS_STRING;
+            data.caller_params = caller_params;
+            data.php_executed = 0;
+            data.server = server;
+
+            /* check if the called method has been previous registered */
+            if(zend_hash_find(server->method_map->value.ht, 
+                              data.xmlrpc_method->value.str.val, 
+                              data.xmlrpc_method->value.str.len + 1, 
+                              (void**)&php_function) == SUCCESS) {
+
+               data.php_function = *php_function;
+            }
+
+           /* We could just call the php method directly ourselves at this point, but we do this 
+            * with a C callback in case the xmlrpc library ever implements some cool usage stats,
+            * or somesuch.
+            */
+           xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
+           if(xAnswer) {
+               if(out.b_php_out) {
+                  zval_dtor(data.return_data);
+                  FREE_ZVAL(data.return_data);
+                  data.return_data = XMLRPC_to_PHP(xAnswer);
+               }
+           }
+           else if(data.php_executed) {
+               if(!out.b_php_out) {
+                   xAnswer = PHP_to_XMLRPC(data.return_data);
+               }
+           }
+
+           /* should we return data as xml? */
+           if(!out.b_php_out) {
+              XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
+              if(xResponse) {
+                 char* outBuf = 0;
+                 int buf_len = 0;
+
+                 /* set some required request hoojum */
+                 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
+                 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
+                 XMLRPC_RequestSetData(xResponse, xAnswer);
+
+                 /* generate xml */
+                 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
+                 if(outBuf) {
+                    RETVAL_STRINGL(outBuf, buf_len, 1);
+                    free(outBuf);
+                 }
+                 /* cleanup after ourselves.  what a sty! */
+                 XMLRPC_RequestFree(xResponse, 0);
+              }
+           }
+           /* or as native php types? */
+           else {
+              *return_value = *data.return_data;
+              zval_copy_ctor(return_value);
+           }
+
+            /* cleanup after ourselves.  what a sty! */
+            zval_dtor(data.xmlrpc_method);
+            FREE_ZVAL(data.xmlrpc_method);
+            zval_dtor(data.return_data);
+            FREE_ZVAL(data.return_data);
+
+            if(xAnswer) {
+               XMLRPC_CleanupValue(xAnswer);
+            }
+         }
+
+         XMLRPC_RequestFree(xRequest, 1);
+      }
+   }
+}
+
+
+/* {{{ proto int xmlrpc_server_add_introspection_data(handle server, array desc)
+   add introspection documentation  */
+PHP_FUNCTION(xmlrpc_server_add_introspection_data) {
+
+   pval *method, *handle, *desc;
+   int type;
+   xmlrpc_server_data* server;
+
+   /* get some params.  should be 2 */
+   if ( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &desc) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if (type == XMLRPCG(le_xmlrpc_server)) {
+      XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc);
+      if (xDesc) {
+         int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
+         XMLRPC_CleanupValue(xDesc);
+         RETURN_LONG(retval);
+      }
+   }
+   RETURN_LONG(0);
+}
+
+
+/* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
+   decode xml into a list of method descriptions */
+PHP_FUNCTION(xmlrpc_parse_method_descriptions)
+{
+   pval* arg1, *arg2, *retval;
+
+   if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   convert_to_string(arg1);
+
+   if(return_value_used) {
+      STRUCT_XMLRPC_ERROR err = {0};
+      XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1->value.str.val, &err);
+      if(xVal) {
+         retval = XMLRPC_to_PHP(xVal);
+
+         if(retval) {
+            *return_value = *retval;
+            zval_copy_ctor(return_value);
+         }
+         /* dust, sweep, and mop */
+         XMLRPC_CleanupValue(xVal);
+      }
+      else {
+         /* could not create description */
+         if(err.xml_elem_error.parser_code) {
+            zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to create introspection data", 
+                       err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
+         }
+         else {
+            zend_error(E_WARNING, "Invalid xml structure. Unable to create introspection data");
+         }
+
+         zend_error(E_WARNING, "xml parse error.  no method description created");
+      }
+   }
+}
+
+
+/************
+* type data *
+************/
+
+#define XMLRPC_TYPE_COUNT 9
+#define XMLRPC_VECTOR_TYPE_COUNT 4
+#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
+
+/* return a string matching a given xmlrpc type */
+static const char** get_type_str_mapping() {
+   static const char* str_mapping[TYPE_STR_MAP_SIZE];
+   static int first = 1;
+   if(first) {
+      /* warning. do not add/delete without changing size define */
+      str_mapping[xmlrpc_none] = "none";
+      str_mapping[xmlrpc_empty] = "empty";
+      str_mapping[xmlrpc_base64] = "base64";
+      str_mapping[xmlrpc_boolean] = "boolean";
+      str_mapping[xmlrpc_datetime] = "datetime";
+      str_mapping[xmlrpc_double] = "double";
+      str_mapping[xmlrpc_int] = "int";
+      str_mapping[xmlrpc_string] = "string";
+      str_mapping[xmlrpc_vector] = "vector";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
+      first = 0;
+   }
+   return (const char**)str_mapping;
+}
+
+/* map an xmlrpc type to a string */
+const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) {
+   const char** str_mapping = get_type_str_mapping();
+   if(vtype == xmlrpc_vector_none) {
+      return str_mapping[type];
+   }
+   else {
+      return str_mapping[XMLRPC_TYPE_COUNT + vtype];
+   }
+}
+
+/* map a string to an xmlrpc type */
+XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) {
+   const char** str_mapping = get_type_str_mapping();
+   int i;
+
+   if(str) {
+      for(i = 0; i < XMLRPC_TYPE_COUNT; i++) {
+         if(!strcmp(str_mapping[i], str)) {
+            return (XMLRPC_VALUE_TYPE)i;
+         }
+      }
+   }
+   return xmlrpc_none;
+}
+
+/* map a string to an xmlrpc vector type */
+XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) {
+   const char** str_mapping = get_type_str_mapping();
+   int i;
+
+   if(str) {
+      for(i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
+         if(!strcmp(str_mapping[i], str)) {
+            return (XMLRPC_VECTOR_TYPE)(i - XMLRPC_TYPE_COUNT);
+         }
+      }
+   }
+   return xmlrpc_none;
+}
+
+
+/* set a given value to a particular type. 
+ * note: this only works on strings, and only for date and base64,
+ *       which do not have native php types. black magic lies herein.
+ */
+int set_pval_xmlrpc_type(pval* value, XMLRPC_VALUE_TYPE type) {
+   int bSuccess = FAILURE;
+
+   /* we only really care about strings because they can represent
+    * base64 and datetime.  all other types have corresponding php types
+    */
+   if(value->type == IS_STRING) {
+      if(type == xmlrpc_base64 || type == xmlrpc_datetime) {
+         const char* typestr = xmlrpc_type_as_str(type, xmlrpc_vector_none);
+         pval* type;
+
+         MAKE_STD_ZVAL(type);
+
+         type->type = IS_STRING;
+         type->value.str.val = estrdup(typestr);
+         type->value.str.len = strlen(typestr);
+
+         convert_to_object(value);
+         bSuccess = zend_hash_update(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
+      }
+   }
+   
+   return bSuccess;
+}
+
+/* return xmlrpc type of a php value */
+XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue) {
+   XMLRPC_VALUE_TYPE type = xmlrpc_none;
+
+   if(value) {
+      switch(value->type) {
+         case IS_NULL:
+            type = xmlrpc_base64;
+            break;
+   #ifndef BOOL_AS_LONG
+
+   /* Right thing to do, but it breaks some legacy code. */
+         case IS_BOOL:
+            type = xmlrpc_boolean;
+            break;
+   #else
+         case IS_BOOL:
+   #endif
+         case IS_LONG:
+         case IS_RESOURCE:
+            type = xmlrpc_int;
+            break;
+         case IS_DOUBLE:
+            type = xmlrpc_double;
+            break;
+         case IS_CONSTANT:
+            type = xmlrpc_string;
+            break;
+         case IS_STRING:
+            type = xmlrpc_string;
+            break;
+         case IS_ARRAY:
+         case IS_CONSTANT_ARRAY:
+            type = xmlrpc_vector;
+            break;
+         case IS_OBJECT:
+         {
+            pval** attr;
+            type = xmlrpc_vector;
+
+            if(zend_hash_find(value->value.obj.properties, 
+                              OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), 
+                              (void**)&attr) == SUCCESS) {
+               if((*attr)->type == IS_STRING) {
+                  type = xmlrpc_str_as_type((*attr)->value.str.val);
+               }
+            }
+            break;
+         }
+      }
+
+      /* if requested, return an unmolested (magic removed) copy of the value */
+      if(newvalue) {
+         pval** val;
+         if( (type == xmlrpc_base64 && value->type != IS_NULL) || type == xmlrpc_datetime) {
+            if(zend_hash_find(value->value.obj.properties, 
+                           OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), 
+                           (void**)&val) == SUCCESS) {
+               *newvalue = *val;
+            }
+         }
+         else {
+            *newvalue = value;
+         }
+      }
+   }
+
+   return type;
+}
+
+
+/* {{{ proto bool xmlrpc_set_type(string value, string type)
+   set xmlrpc type, base64 or datetime, for a php string value */
+PHP_FUNCTION(xmlrpc_set_type) {
+   pval* arg, *type;
+   XMLRPC_VALUE_TYPE vtype;
+
+   if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg, &type) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+#if ZEND_MODULE_API_NO < 20010901
+   if (!ParameterPassedByReference(ht,1)) {
+       zend_error(E_WARNING,"first argument to xmlrpc_set_type() passed by value, expecting reference");
+   }
+#endif
+
+   convert_to_string(type);
+   vtype = xmlrpc_str_as_type(type->value.str.val);
+   if(vtype != xmlrpc_none) {
+      if(set_pval_xmlrpc_type(arg, vtype) == SUCCESS) {
+         RETURN_TRUE;
+      }
+   }
+   else {
+      zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type->value.str.val);
+   }
+   RETURN_FALSE;
+}
+
+/* {{{ proto string xmlrpc_get_type(mixed value)
+   get xmlrpc type for a php value. especially useful for base64 and datetime strings */
+PHP_FUNCTION(xmlrpc_get_type) {
+   pval* arg;
+   XMLRPC_VALUE_TYPE type;
+   XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
+
+   if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   type = get_pval_xmlrpc_type(arg, 0);
+   if(type == xmlrpc_vector) {
+      vtype = determine_vector_type(arg->value.ht);
+   }
+   
+   RETURN_STRING((char*)xmlrpc_type_as_str(type, vtype), 1);
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
diff --git a/ext/xmlrpc/CREDITS b/ext/xmlrpc/CREDITS
new file mode 100644 (file)
index 0000000..cfb14fa
--- /dev/null
@@ -0,0 +1,2 @@
+xmlrpc
+Dan Libby
diff --git a/ext/xmlrpc/EXPERIMENTAL b/ext/xmlrpc/EXPERIMENTAL
new file mode 100644 (file)
index 0000000..6443e99
--- /dev/null
@@ -0,0 +1,5 @@
+this extension is experimental,
+its functions may change their names 
+or move to extension all together 
+so do not rely to much on them 
+you have been warned!
diff --git a/ext/xmlrpc/Makefile.in b/ext/xmlrpc/Makefile.in
new file mode 100644 (file)
index 0000000..a3ba38b
--- /dev/null
@@ -0,0 +1,12 @@
+# $Id$
+
+LTLIBRARY_NAME        = libxmlrpc.la
+LTLIBRARY_SOURCES     = xmlrpc-epi-php.c
+LTLIBRARY_SHARED_NAME = xmlrpc.la
+EXTRA_INCLUDES = -I$(XMLRPC_INC_DIR)
+LTLIBRARY_LIBADD  = $(XMLRPC_LIBADD)
+LTLIBRARY_SHARED_LIBADD  = $(XMLRPC_SHARED_LIBADD)
+
+SUBDIRS = $(XMLRPC_SUBDIRS)
+
+include $(top_srcdir)/build/dynlib.mk
diff --git a/ext/xmlrpc/config.m4 b/ext/xmlrpc/config.m4
new file mode 100644 (file)
index 0000000..1880903
--- /dev/null
@@ -0,0 +1,122 @@
+
+dnl $Id$
+
+sinclude(ext/xmlrpc/libxmlrpc/acinclude.m4)
+sinclude(ext/xmlrpc/libxmlrpc/xmlrpc.m4)
+sinclude(libxmlrpc/acinclude.m4)
+sinclude(libxmlrpc/xmlrpc.m4)
+
+AC_DEFUN(XMLRPC_LIB_CHK,[
+  str="$XMLRPC_DIR/$1/libxmlrpc.*"
+  for j in `echo $str`; do
+    if test -r $j; then
+      XMLRPC_LIB_DIR=$XMLRPC_DIR/$1
+      break 2
+    fi
+  done
+])
+       
+PHP_ARG_WITH(xmlrpc, for XMLRPC-EPI support,
+[  --with-xmlrpc[=DIR]      Include XMLRPC-EPI support. DIR is the XMLRPC-EPI base
+                          directory. If unspecified, the bundled XMLRPC-EPI library
+                          will be used.], yes)
+                          
+PHP_ARG_WITH(expat-dir, libexpat dir for XMLRPC-EPI
+[  --with-expat-dir=DIR    XMLRPC-EPI: libexpat dir for XMLRPC-EPI])
+
+
+if test "$PHP_XMLRPC" != "no"; then
+  AC_DEFINE(HAVE_XMLRPC, 1, [Whether you have XMLRPC-EPI])
+  PHP_EXTENSION(xmlrpc,$ext_shared)
+
+dnl check for iconv
+  found_iconv=no
+  AC_CHECK_LIB(c, iconv_open, found_iconv=yes)
+  if test "$found_iconv" = "no"; then
+      for i in /usr /usr/local $ICONV_DIR; do
+        if test -f $i/lib/libconv.a -o -f $i/lib/libiconv.s?; then
+          PHP_ADD_LIBRARY_WITH_PATH(iconv, $i/lib, XMLRPC_SHARED_LIBADD)
+          found_iconv=yes
+        fi
+      done
+  fi
+  
+  if test "$found_iconv" = "no"; then
+    AC_MSG_ERROR(iconv not found, in order to build XMLRPC-EPI you need the iconv library)
+  fi
+  
+dnl check for expat
+  testval=no
+  for i in $PHP_EXPAT_DIR; do
+    if test -f $i/lib/libexpat.a -o -f $i/lib/libexpat.s?; then
+      AC_DEFINE(HAVE_LIBEXPAT2,1,[ ])
+      PHP_ADD_LIBRARY_WITH_PATH(expat, $i/lib, XMLRPC_SHARED_LIBADD)
+      PHP_ADD_INCLUDE($i/include)
+      testval=yes
+    fi
+  done
+
+  if test "$testval" = "no"; then
+    PHP_ADD_LIBRARY(xmlparse)
+    PHP_ADD_LIBRARY(xmltok)
+  fi
+
+fi
+
+if test "$PHP_XMLRPC" = "yes"; then
+  XMLRPC_CHECKS
+  XMLRPC_LIBADD=libxmlrpc/libxmlrpc.la
+  XMLRPC_SHARED_LIBADD=libxmlrpc/libxmlrpc.la
+  XMLRPC_SUBDIRS=libxmlrpc
+  PHP_SUBST(XMLRPC_LIBADD)
+  PHP_SUBST(XMLRPC_SUBDIRS)
+  LIB_BUILD($ext_builddir/libxmlrpc,$ext_shared,yes)
+  PHP_ADD_INCLUDE($ext_srcdir/libxmlrpc)
+  XMLRPC_MODULE_TYPE=builtin
+elif test "$PHP_XMLRPC" != "no"; then
+  for i in $PHP_XMLRPC; do
+    if test -r $i/include/xmlrpc/xmlrpc.h; then
+      XMLRPC_DIR=$i
+      XMLRPC_INC_DIR=$i/include/xmlrpc
+    elif test -r $i/include/xmlrpc.h; then
+      XMLRPC_DIR=$i
+      XMLRPC_INC_DIR=$i/include
+    fi
+  done
+
+  if test -z "$XMLRPC_DIR"; then
+    AC_MSG_ERROR(Cannot find header files under $PHP_XMLRPC)
+  fi
+
+  XMLRPC_MODULE_TYPE=external
+
+  for i in lib lib/xmlrpc; do
+    XMLRPC_LIB_CHK($i)
+  done
+
+  if test -z "$XMLRPC_LIB_DIR"; then
+    AC_MSG_ERROR(Cannot find xmlrpc library under $XMLRPC_DIR)
+  fi
+
+  if test "$PHP_ZLIB_DIR" != "no"; then
+    PHP_ADD_LIBRARY(z,, XMLRPC_SHARED_LIBADD)
+    XMLRPC_LIBS="-L$PHP_ZLIB_DIR -z"
+  fi
+
+  PHP_ADD_LIBRARY_WITH_PATH(xmlrpc, $XMLRPC_LIB_DIR, XMLRPC_SHARED_LIBADD)
+  XMLRPC_LIBS="-L$XMLRPC_LIB_DIR -lxmlrpc $XMLRPC_LIBS"
+
+  PHP_ADD_INCLUDE($XMLRPC_INC_DIR)
+  XMLRPC_INCLUDE="-I$XMLRPC_INC_DIR"
+
+else
+  XMLRPC_MODULE_TYPE=none
+fi
+
+
+PHP_SUBST(XMLRPC_SHARED_LIBADD)
+PHP_SUBST_OLD(XMLRPC_MODULE_TYPE)
+PHP_SUBST_OLD(XMLRPC_LIBS)
+PHP_SUBST_OLD(XMLRPC_INCLUDE)
+
+
diff --git a/ext/xmlrpc/libs.mk b/ext/xmlrpc/libs.mk
new file mode 100644 (file)
index 0000000..7c6e176
--- /dev/null
@@ -0,0 +1,7 @@
+include $(top_builddir)/config_vars.mk
+LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X)
+LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo)
+$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES)
+       $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD)
+
+targets = $(LTLIBRARY_NAME)
diff --git a/ext/xmlrpc/libxmlrpc/Makefile.in b/ext/xmlrpc/libxmlrpc/Makefile.in
new file mode 100644 (file)
index 0000000..44257c4
--- /dev/null
@@ -0,0 +1,9 @@
+
+LTLIBRARY_NAME    = libxmlrpc.la
+LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \
+                    xmlrpc_introspection.c encodings.c system_methods.c \
+                    xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c
+                   
+DEFS = -DVERSION="0.41"
+
+include $(top_srcdir)/build/dynlib.mk
diff --git a/ext/xmlrpc/libxmlrpc/README b/ext/xmlrpc/libxmlrpc/README
new file mode 100644 (file)
index 0000000..323edfa
--- /dev/null
@@ -0,0 +1,17 @@
+organization of this directory is moving towards this approach:
+
+<module>.h               -- public API and data types
+<module>_private.h       -- protected API and data types
+<module>.c               -- implementation and private API / types
+
+The rules are:
+.c files may include *_private.h.
+.h files may not include *_private.h
+
+This allows us to have a nicely encapsulated C api with opaque data types and private functions
+that are nonetheless shared between source files without redundant extern declarations..
+
+
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/acinclude.m4 b/ext/xmlrpc/libxmlrpc/acinclude.m4
new file mode 100644 (file)
index 0000000..9535e45
--- /dev/null
@@ -0,0 +1,38 @@
+# Local macros for automake & autoconf
+
+AC_DEFUN(XMLRPC_FUNCTION_CHECKS,[
+
+# Standard XMLRPC list
+AC_CHECK_FUNCS( \
+ strtoul strtoull snprintf \
+ strstr strpbrk strerror\
+ memcpy memmove)
+
+])
+
+AC_DEFUN(XMLRPC_HEADER_CHECKS,[
+AC_HEADER_STDC
+AC_CHECK_HEADERS(xmlparse.h xmltok.h stdlib.h strings.h string.h)
+])
+
+AC_DEFUN(XMLRPC_TYPE_CHECKS,[
+
+AC_REQUIRE([AC_C_CONST])
+AC_REQUIRE([AC_C_INLINE])
+AC_CHECK_SIZEOF(char, 1)
+
+AC_CHECK_SIZEOF(int, 4)
+AC_CHECK_SIZEOF(long, 4)
+AC_CHECK_SIZEOF(long long, 8)
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_TYPE_UID_T
+
+XMLRPC_CHECK_ULONG
+XMLRPC_CHECK_UCHAR
+XMLRPC_CHECK_UINT
+XMLRPC_CHECK_INT_8_16_32
+
+XMLRPC_TYPE_QSORT
+
+])
diff --git a/ext/xmlrpc/libxmlrpc/base64.c b/ext/xmlrpc/libxmlrpc/base64.c
new file mode 100644 (file)
index 0000000..bdbf95b
--- /dev/null
@@ -0,0 +1,192 @@
+static const char rcsid[] = "#(@) $Id$";
+
+/*
+
+          Encode or decode file as MIME base64 (RFC 1341)
+
+                           by John Walker
+                      http://www.fourmilab.ch/
+
+               This program is in the public domain.
+
+*/
+#include <stdio.h>
+
+/*  ENCODE  -- Encode binary file into base64.  */
+#include <stdlib.h>
+
+#include "base64.h"
+
+static unsigned char dtable[512];
+
+void buffer_new(struct buffer_st *b)
+{
+  b->length = 512;
+  b->data = malloc(sizeof(char)*(b->length));
+  b->data[0] = 0;
+  b->ptr = b->data;
+  b->offset = 0;
+}
+
+void buffer_add(struct buffer_st *b, char c)
+{
+  *(b->ptr++) = c;
+  b->offset++;
+  if (b->offset == b->length) {
+    b->length += 512;
+    b->data = realloc(b->data, b->length);
+    b->ptr = b->data + b->offset;
+  }
+}
+
+void buffer_delete(struct buffer_st *b)
+{
+  free(b->data);
+  b->length = 0;
+  b->offset = 0;
+  b->ptr = NULL;
+  b->data = NULL;
+}
+
+void base64_encode(struct buffer_st *b, const char *source, int length)
+{
+  int i, hiteof = 0;
+  int offset = 0;
+  int olen;
+  char *dest;
+  
+  olen = 0;
+  
+  buffer_new(b);
+  
+  /*   Fill dtable with character encodings.  */
+  
+  for (i = 0; i < 26; i++) {
+    dtable[i] = 'A' + i;
+    dtable[26 + i] = 'a' + i;
+  }
+  for (i = 0; i < 10; i++) {
+    dtable[52 + i] = '0' + i;
+  }
+  dtable[62] = '+';
+  dtable[63] = '/';
+  
+  while (!hiteof) {
+    unsigned char igroup[3], ogroup[4];
+    int c, n;
+    
+    igroup[0] = igroup[1] = igroup[2] = 0;
+    for (n = 0; n < 3; n++) {
+      c = *(source++);
+      offset++;
+      if (offset > length) {
+       hiteof = 1;
+       break;
+      }
+      igroup[n] = (unsigned char) c;
+    }
+    if (n > 0) {
+      ogroup[0] = dtable[igroup[0] >> 2];
+      ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
+      ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
+      ogroup[3] = dtable[igroup[2] & 0x3F];
+      
+      /* Replace characters in output stream with "=" pad
+        characters if fewer than three characters were
+        read from the end of the input stream. */
+      
+      if (n < 3) {
+       ogroup[3] = '=';
+       if (n < 2) {
+         ogroup[2] = '=';
+       }
+      }
+      for (i = 0; i < 4; i++) {
+       buffer_add(b, ogroup[i]);
+       if (!(b->offset % 72)) {
+         // buffer_add(b, '\r');
+         buffer_add(b, '\n');
+       }
+      }
+    }
+  }
+  // buffer_add(b, '\r');
+  buffer_add(b, '\n');
+}
+
+void base64_decode(struct buffer_st *bfr, const char *source, int length)
+{
+    int i;
+    int offset = 0;
+    int endoffile;
+    int count;
+
+    buffer_new(bfr);
+
+    for (i = 0; i < 255; i++) {
+       dtable[i] = 0x80;
+    }
+    for (i = 'A'; i <= 'Z'; i++) {
+        dtable[i] = 0 + (i - 'A');
+    }
+    for (i = 'a'; i <= 'z'; i++) {
+        dtable[i] = 26 + (i - 'a');
+    }
+    for (i = '0'; i <= '9'; i++) {
+        dtable[i] = 52 + (i - '0');
+    }
+    dtable['+'] = 62;
+    dtable['/'] = 63;
+    dtable['='] = 0;
+
+    endoffile = 0;
+
+    /*CONSTANTCONDITION*/
+    while (1) {
+       unsigned char a[4], b[4], o[3];
+
+       for (i = 0; i < 4; i++) {
+           int c;
+           while (1) {
+             c = *(source++);
+             offset++;
+             if (offset > length) endoffile = 1;
+             if (isspace(c) || c == '\n' || c == '\r') continue;
+             break;
+           }
+
+           if (endoffile) {
+             /*
+               if (i > 0) {
+                    fprintf(stderr, "Input file incomplete.\n");
+                   exit(1);
+               }
+             */
+               return;
+           }
+
+           if (dtable[c] & 0x80) {
+             /*
+             fprintf(stderr, "Offset %i length %i\n", offset, length);
+             fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]);
+             exit(1);
+             */
+             i--;
+             continue;
+           }
+           a[i] = (unsigned char) c;
+           b[i] = (unsigned char) dtable[c];
+       }
+       o[0] = (b[0] << 2) | (b[1] >> 4);
+       o[1] = (b[1] << 4) | (b[2] >> 2);
+       o[2] = (b[2] << 6) | b[3];
+        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+       count = 0;
+       while (count < i) {
+         buffer_add(bfr, o[count++]);
+       }
+       if (i < 3) {
+           return;
+       }
+    }
+}
diff --git a/ext/xmlrpc/libxmlrpc/base64.h b/ext/xmlrpc/libxmlrpc/base64.h
new file mode 100644 (file)
index 0000000..4cf156a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+
+          Encode or decode file as MIME base64 (RFC 1341)
+
+                           by John Walker
+                      http://www.fourmilab.ch/
+
+               This program is in the public domain.
+
+*/
+
+
+struct buffer_st {
+  char *data;
+  int length;
+  char *ptr;
+  int offset;
+};
+
+void buffer_new(struct buffer_st *b);
+void buffer_add(struct buffer_st *b, char c);
+void buffer_delete(struct buffer_st *b);
+
+void base64_encode(struct buffer_st *b, const char *source, int length);
+void base64_decode(struct buffer_st *b, const char *source, int length);
+
+/*
+#define DEBUG_MALLOC
+ */
+
+#ifdef DEBUG_MALLOC
+void *_malloc_real(size_t s, char *file, int line);
+void _free_real(void *p, char *file, int line);
+
+#define malloc(s)      _malloc_real(s,__FILE__,__LINE__)
+#define free(p)                _free_real(p, __FILE__,__LINE__)
+#endif
+
diff --git a/ext/xmlrpc/libxmlrpc/encodings.c b/ext/xmlrpc/libxmlrpc/encodings.c
new file mode 100644 (file)
index 0000000..544cfa2
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+#include <errno.h>
+#include <iconv.h>
+#include "encodings.h"
+
+static char* convert(const char* src, int src_len, int *new_len, const char* from_enc, const char* to_enc) {
+   char* outbuf = 0;
+
+   if(src && src_len && from_enc && to_enc) {
+      int outlenleft = src_len;
+      int outlen = src_len;
+      int inlenleft = src_len;
+      iconv_t ic = iconv_open(to_enc, from_enc);
+      char* src_ptr = (char*)src;
+      char* out_ptr = 0;
+
+      if(ic != (iconv_t)-1) {
+         size_t st;
+         outbuf = (char*)malloc(outlen + 1);
+
+         if(outbuf) {
+            out_ptr = (char*)outbuf;
+            while(inlenleft) {
+               st = iconv(ic, &src_ptr, &inlenleft, &out_ptr, &outlenleft);
+               if(st == -1) {
+                  if(errno == E2BIG) {
+                     int diff = out_ptr - outbuf;
+                     outlen += inlenleft;
+                     outlenleft += inlenleft;
+                     outbuf = (char*)realloc(outbuf, outlen + 1);
+                     if(!outbuf) {
+                        break;
+                     }
+                     out_ptr = outbuf + diff;
+                  }
+                  else {
+                     free(outbuf);
+                     outbuf = 0;
+                     break;
+                  }
+               }
+            }
+         }
+         iconv_close(ic);
+      }
+      outlen -= outlenleft;
+
+      if(new_len) {
+         *new_len = outbuf ? outlen : 0;
+      }
+      if(outbuf) {
+         outbuf[outlen] = 0;
+      }
+   }
+   return outbuf;
+}
+
+/* returns a new string that must be freed */
+char* utf8_encode(const char *s, int len, int *newlen, const char* encoding)
+{
+   return convert(s, len, newlen, encoding, "UTF-8");
+}
+
+/* returns a new string, possibly decoded */
+char* utf8_decode(const char *s, int len, int *newlen, const char* encoding)
+{
+   return convert(s, len, newlen, "UTF-8", encoding);
+}
+
diff --git a/ext/xmlrpc/libxmlrpc/encodings.h b/ext/xmlrpc/libxmlrpc/encodings.h
new file mode 100644 (file)
index 0000000..486360b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+#ifndef __ENCODINGS__H
+#define __ENCODINGS__H
+
+/* these defines are for legacy purposes. */
+#define encoding_utf_8 "UTF-8"
+typedef const char* ENCODING_ID;
+#define utf8_get_encoding_id_string(desired_enc) ((const char*)desired_enc)
+#define utf8_get_encoding_id_from_string(id_string) ((ENCODING_ID)id_string)
+
+char* utf8_encode(const char *s, int len, int *newlen, ENCODING_ID encoding);
+char* utf8_decode(const char *s, int len, int *newlen, ENCODING_ID encoding);
+
+#endif /* __ENCODINGS__H  */
diff --git a/ext/xmlrpc/libxmlrpc/libs.mk b/ext/xmlrpc/libxmlrpc/libs.mk
new file mode 100644 (file)
index 0000000..7c6e176
--- /dev/null
@@ -0,0 +1,7 @@
+include $(top_builddir)/config_vars.mk
+LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X)
+LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo)
+$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES)
+       $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD)
+
+targets = $(LTLIBRARY_NAME)
diff --git a/ext/xmlrpc/libxmlrpc/queue.c b/ext/xmlrpc/libxmlrpc/queue.c
new file mode 100644 (file)
index 0000000..25d62b9
--- /dev/null
@@ -0,0 +1,979 @@
+static const char rcsid[] = "#(@) $Id$";
+
+/* 
+ * Date last modified: Jan 2001
+ * Modifications by Dan Libby (dan@libby.com), including:
+ *  - various fixes, null checks, etc
+ *  - addition of Q_Iter funcs, macros
+ */
+
+
+/*-**************************************************************
+ *
+ *  File : q.c
+ *
+ *  Author: Peter Yard [1993.01.02] -- 02 Jan 1993
+ *
+ *  Disclaimer: This code is released to the public domain.
+ *
+ *  Description:
+ *      Generic double ended queue (Deque pronounced DEK) for handling
+ *      any data types, with sorting.
+ *
+ *      By use of various functions in this module the caller
+ *      can create stacks, queues, lists, doubly linked lists,
+ *      sorted lists, indexed lists.  All lists are dynamic.
+ *
+ *      It is the responsibility of the caller to malloc and free
+ *      memory for insertion into the queue. A pointer to the object
+ *      is used so that not only can any data be used but various kinds
+ *      of data can be pushed on the same queue if one so wished e.g.
+ *      various length string literals mixed with pointers to structures
+ *      or integers etc.
+ *
+ *  Enhancements:
+ *      A future improvement would be the option of multiple "cursors"
+ *      so that multiple locations could occur in the one queue to allow
+ *      placemarkers and additional flexibility.  Perhaps even use queue
+ *      itself to have a list of cursors.
+ *
+ * Usage:
+ *
+ *          /x init queue x/
+ *          queue  q;
+ *          Q_Init(&q);
+ *
+ *      To create a stack :
+ *
+ *          Q_PushHead(&q, &mydata1); /x push x/
+ *          Q_PushHead(&q, &mydata2);
+ *          .....
+ *          data_ptr = Q_PopHead(&q); /x pop x/
+ *          .....
+ *          data_ptr = Q_Head(&q);   /x top of stack x/
+ *
+ *      To create a FIFO:
+ *
+ *          Q_PushHead(&q, &mydata1);
+ *          .....
+ *          data_ptr = Q_PopTail(&q);
+ *
+ *      To create a double list:
+ *
+ *          data_ptr = Q_Head(&q);
+ *          ....
+ *          data_ptr = Q_Next(&q);
+ *          data_ptr = Q_Tail(&q);
+ *          if (Q_IsEmpty(&q)) ....
+ *          .....
+ *          data_ptr = Q_Previous(&q);
+ *
+ *      To create a sorted list:
+ *
+ *          Q_PushHead(&q, &mydata1); /x push x/
+ *          Q_PushHead(&q, &mydata2);
+ *          .....
+ *          if (!Q_Sort(&q, MyFunction))
+ *              .. error ..
+ *
+ *          /x fill in key field of mydata1.
+ *           * NB: Q_Find does linear search
+ *           x/
+ *
+ *          if (Q_Find(&q, &mydata1, MyFunction))
+ *          {
+ *              /x found it, queue cursor now at correct record x/
+ *              /x can retrieve with x/
+ *              data_ptr = Q_Get(&q);
+ *
+ *              /x alter data , write back with x/
+ *              Q_Put(&q, data_ptr);
+ *          }
+ *
+ *          /x Search with binary search x/
+ *          if (Q_Seek(&q, &mydata, MyFunction))
+ *              /x etc x/
+ *
+ *
+ ****************************************************************/
+
+
+#include <stdlib.h>
+#include "queue.h"
+
+
+static void QuickSort(void *list[], int low, int high,
+                      int (*Comp)(const void *, const void *));
+static int  Q_BSearch(queue *q, void *key,
+                      int (*Comp)(const void *, const void *));
+
+/* The index: a pointer to pointers */
+
+static  void        **index;
+static  datanode    **posn_index;
+
+
+/***
+ *
+ ** function    : Q_Init
+ *
+ ** purpose     : Initialise queue object and pointers.
+ *
+ ** parameters  : 'queue' pointer.
+ *
+ ** returns     : True_ if init successful else False_
+ *
+ ** comments    :
+ ***/
+
+int Q_Init(queue  *q)
+{
+   if(q) {
+      q->head = q->tail = NULL;
+      q->cursor = q->head;
+      q->size = 0;
+      q->sorted = False_;
+   }
+
+   return True_;
+}
+
+/***
+ *
+ ** function    : Q_AtHead
+ *
+ ** purpose     : tests if cursor is at head of queue
+ *
+ ** parameters  : 'queue' pointer.
+ *
+ ** returns     : boolean - True_ is at head else False_
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_AtHead(queue *q)
+{
+   return(q && q->cursor == q->head);
+}
+
+
+/***
+ *
+ ** function    : Q_AtTail
+ *
+ ** purpose     : boolean test if cursor at tail of queue
+ *
+ ** parameters  : 'queue' pointer to test.
+ *
+ ** returns     : True_ or False_
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_AtTail(queue *q)
+{
+   return(q && q->cursor == q->tail);
+}
+
+
+/***
+ *
+ ** function    : Q_IsEmpty
+ *
+ ** purpose     : test if queue has nothing in it.
+ *
+ ** parameters  : 'queue' pointer
+ *
+ ** returns     : True_ if IsEmpty queue, else False_
+ *
+ ** comments    :
+ *
+ ***/
+
+inline int Q_IsEmpty(queue *q)
+{
+   return(!q || q->size == 0);
+}
+
+/***
+ *
+ ** function    : Q_Size
+ *
+ ** purpose     : return the number of elements in the queue
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : number of elements
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_Size(queue *q)
+{
+   return q ? q->size : 0;
+}
+
+
+/***
+ *
+ ** function    : Q_Head
+ *
+ ** purpose     : position queue cursor to first element (head) of queue.
+ *
+ ** parameters  : 'queue' pointer
+ *
+ ** returns     : pointer to data at head. If queue is IsEmpty returns NULL
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_Head(queue *q)
+{
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   q->cursor = q->head;
+
+   return  q->cursor->data;
+}
+
+
+/***
+ *
+ ** function    : Q_Tail
+ *
+ ** purpose     : locate cursor at tail of queue.
+ *
+ ** parameters  : 'queue' pointer
+ *
+ ** returns     : pointer to data at tail , if queue IsEmpty returns NULL
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_Tail(queue *q)
+{
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   q->cursor = q->tail;
+
+   return  q->cursor->data;
+}
+
+
+/***
+ *
+ ** function    : Q_PushHead
+ *
+ ** purpose     : put a data pointer at the head of the queue
+ *
+ ** parameters  : 'queue' pointer, void pointer to the data.
+ *
+ ** returns     : True_ if success else False_ if unable to push data.
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_PushHead(queue *q, void *d)
+{
+   if(q && d) {
+      node    *n;
+      datanode *p;
+
+      p = malloc(sizeof(datanode));
+      if(p == NULL)
+         return False_;
+
+      n = q->head;
+
+      q->head = (node*)p;
+      q->head->prev = NULL;
+
+      if(q->size == 0) {
+         q->head->next = NULL;
+         q->tail = q->head;
+      }
+      else {
+         q->head->next = (datanode*)n;
+         n->prev = q->head;
+      }
+
+      q->head->data = d;
+      q->size++;
+
+      q->cursor = q->head;
+
+      q->sorted = False_;
+
+      return True_;
+   }
+   return False_;
+}
+
+
+
+/***
+ *
+ ** function    : Q_PushTail
+ *
+ ** purpose     : put a data element pointer at the tail of the queue
+ *
+ ** parameters  : queue pointer, pointer to the data
+ *
+ ** returns     : True_ if data pushed, False_ if data not inserted.
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_PushTail(queue *q, void *d)
+{
+   if(q && d) {
+      node        *p;
+      datanode    *n;
+
+      n = malloc(sizeof(datanode));
+      if(n == NULL)
+         return False_;
+
+      p = q->tail;
+      q->tail = (node *)n;
+
+      if(q->size == 0) {
+         q->tail->prev = NULL;
+         q->head = q->tail;
+      }
+      else {
+         q->tail->prev = (datanode *)p;
+         p->next = q->tail;
+      }
+
+      q->tail->next = NULL;
+
+      q->tail->data =  d;
+      q->cursor = q->tail;
+      q->size++;
+
+      q->sorted = False_;
+
+      return True_;
+   }
+}
+
+
+
+/***
+ *
+ ** function    : Q_PopHead
+ *
+ ** purpose     : remove and return the top element at the head of the
+ *                queue.
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : pointer to data element or NULL if queue is IsEmpty.
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_PopHead(queue *q)
+{
+   datanode    *n;
+   void        *d;
+
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   d = q->head->data;
+   n = q->head->next;
+   free(q->head);
+
+   q->size--;
+
+   if(q->size == 0)
+      q->head = q->tail = q->cursor = NULL;
+   else {
+      q->head = (node *)n;
+      q->head->prev = NULL;
+      q->cursor = q->head;
+   }
+
+   q->sorted = False_;
+
+   return d;
+}
+
+
+/***
+ *
+ ** function    : Q_PopTail
+ *
+ ** purpose     : remove element from tail of queue and return data.
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : pointer to data element that was at tail. NULL if queue
+ *                IsEmpty.
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_PopTail(queue *q)
+{
+   datanode    *p;
+   void        *d;
+
+   if(Q_IsEmpty(q))
+      return NULL;
+
+   d = q->tail->data;
+   p = q->tail->prev;
+   free(q->tail);
+   q->size--;
+
+   if(q->size == 0)
+      q->head = q->tail = q->cursor = NULL;
+   else {
+      q->tail = (node *)p;
+      q->tail->next = NULL;
+      q->cursor = q->tail;
+   }
+
+   q->sorted = False_;
+
+   return d;
+}
+
+
+
+/***
+ *
+ ** function    : Q_Next
+ *
+ ** purpose     : Move to the next element in the queue without popping
+ *
+ ** parameters  : queue pointer.
+ *
+ ** returns     : pointer to data element of new element or NULL if end
+ *                of the queue.
+ *
+ ** comments    : This uses the cursor for the current position. Q_Next
+ *                only moves in the direction from the head of the queue
+ *                to the tail.
+ ***/
+
+void *Q_Next(queue *q)
+{
+   if(!q)
+      return NULL;
+
+   if(q->cursor->next == NULL)
+      return NULL;
+
+   q->cursor = (node *)q->cursor->next;
+
+   return  q->cursor->data ;
+}
+
+
+
+/***
+ *
+ ** function    : Q_Previous
+ *
+ ** purpose     : Opposite of Q_Next. Move to next element closer to the
+ *                head of the queue.
+ *
+ ** parameters  : pointer to queue
+ *
+ ** returns     : pointer to data of new element else NULL if queue IsEmpty
+ *
+ ** comments    : Makes cursor move towards the head of the queue.
+ *
+ ***/
+
+void *Q_Previous(queue *q)
+{
+   if(!q)
+      return NULL;
+   
+   if(q->cursor->prev == NULL)
+      return NULL;
+
+   q->cursor = (node *)q->cursor->prev;
+
+   return q->cursor->data;
+}
+
+
+void *Q_Iter_Del(queue *q, q_iter iter)
+{
+   void        *d;
+   datanode    *n, *p;
+
+   if(!q)
+      return NULL;
+
+   if(iter == NULL)
+      return NULL;
+
+   if(iter == (q_iter)q->head)
+      return Q_PopHead(q);
+
+   if(iter == (q_iter)q->tail)
+      return Q_PopTail(q);
+
+   n = ((node*)iter)->next;
+   p = ((node*)iter)->prev;
+   d = ((node*)iter)->data;
+
+   free(iter);
+
+   if(p) {
+      p->next = n;
+   }
+   if (q->cursor == (node*)iter) {
+      if (p) {
+         q->cursor = p;
+      } else {
+         q->cursor = n;
+      }
+   }
+
+
+   if (n != NULL) {
+       n->prev = p;
+   }
+
+   q->size--;
+
+   q->sorted = False_;
+
+   return d;
+}
+
+
+
+/***
+ *
+ ** function    : Q_DelCur
+ *
+ ** purpose     : Delete the current queue element as pointed to by
+ *                the cursor.
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : pointer to data element.
+ *
+ ** comments    : WARNING! It is the responsibility of the caller to
+ *                free any memory. Queue cannot distinguish between
+ *                pointers to literals and malloced memory.
+ *
+ ***/
+
+void *Q_DelCur(queue* q) {
+   if(q) {
+      return Q_Iter_Del(q, (q_iter)q->cursor);
+   }
+   return 0;
+}
+
+
+/***
+ *
+ ** function    : Q_Destroy
+ *
+ ** purpose     : Free all queue resources
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : null.
+ *
+ ** comments    : WARNING! It is the responsibility of the caller to
+ *                free any memory. Queue cannot distinguish between
+ *                pointers to literals and malloced memory.
+ *
+ ***/
+
+void Q_Destroy(queue *q)
+{
+   while(!Q_IsEmpty(q)) {
+      Q_PopHead(q);
+   }
+}
+
+
+/***
+ *
+ ** function    : Q_Get
+ *
+ ** purpose     : get the pointer to the data at the cursor location
+ *
+ ** parameters  : queue pointer
+ *
+ ** returns     : data element pointer
+ *
+ ** comments    :
+ *
+ ***/
+
+void *Q_Get(queue *q)
+{
+   if(!q)
+      return NULL;
+
+   if(q->cursor == NULL)
+      return NULL;
+   return q->cursor->data;
+}
+
+
+
+/***
+ *
+ ** function    : Q_Put
+ *
+ ** purpose     : replace pointer to data with new pointer to data.
+ *
+ ** parameters  : queue pointer, data pointer
+ *
+ ** returns     : boolean- True_ if successful, False_ if cursor at NULL
+ *
+ ** comments    :
+ *
+ ***/
+
+int Q_Put(queue *q, void *data)
+{
+   if(q && data) {
+      if(q->cursor == NULL)
+         return False_;
+
+      q->cursor->data = data;
+      return True_;
+   }
+   return False_;
+}
+
+
+/***
+ *
+ ** function    : Q_Find
+ *
+ ** purpose     : Linear search of queue for match with key in *data
+ *
+ ** parameters  : queue pointer q, data pointer with data containing key
+ *                comparison function here called Comp.
+ *
+ ** returns     : True_ if found , False_ if not in queue.
+ *
+ ** comments    : Useful for small queues that are constantly changing
+ *                and would otherwise need constant sorting with the
+ *                Q_Seek function.
+ *                For description of Comp see Q_Sort.
+ *                Queue cursor left on position found item else at end.
+ *
+ ***/
+
+int Q_Find(queue *q, void *data,
+           int (*Comp)(const void *, const void *))
+{
+   void *d;
+
+   if (q == NULL) {
+       return False_;
+   }
+
+   d = Q_Head(q);
+   do {
+      if(Comp(d, data) == 0)
+         return True_;
+      d = Q_Next(q);
+   } while(!Q_AtTail(q));
+
+   if(Comp(d, data) == 0)
+      return True_;
+
+   return False_;
+}
+
+/*========  Sorted Queue and Index functions   ========= */
+
+
+static void QuickSort(void *list[], int low, int high,
+                      int (*Comp)(const void *, const void *))
+{
+   int     flag = 1, i, j;
+   void    *key, *temp;
+
+   if(low < high) {
+      i = low;
+      j = high + 1;
+
+      key = list[ low ];
+
+      while(flag) {
+         i++;
+         while(Comp(list[i], key) < 0)
+            i++;
+
+         j--;
+         while(Comp(list[j], key) > 0)
+            j--;
+
+         if(i < j) {
+            temp = list[i];
+            list[i] = list[j];
+            list[j] = temp;
+         }
+         else  flag = 0;
+      }
+
+      temp = list[low];
+      list[low] = list[j];
+      list[j] = temp;
+
+      QuickSort(list, low, j-1, Comp);
+      QuickSort(list, j+1, high, Comp);
+   }
+}
+
+
+/***
+ *
+ ** function    : Q_Sort
+ *
+ ** purpose     : sort the queue and allow index style access.
+ *
+ ** parameters  : queue pointer, comparison function compatible with
+ *                with 'qsort'.
+ *
+ ** returns     : True_ if sort succeeded. False_ if error occurred.
+ *
+ ** comments    : Comp function supplied by caller must return
+ *                  -1 if data1  < data2
+ *                   0 if data1 == data2
+ *                  +1 if data1  > data2
+ *
+ *                    for Comp(data1, data2)
+ *
+ *                If queue is already sorted it frees the memory of the
+ *                old index and starts again.
+ *
+ ***/
+
+int Q_Sort(queue *q, int (*Comp)(const void *, const void *))
+{
+   int         i;
+   void        *d;
+   datanode    *dn;
+
+   /* if already sorted free memory for tag array */
+
+   if(q->sorted) {
+      free(index);
+      free(posn_index);
+      q->sorted = False_;
+   }
+
+   /* Now allocate memory of array, array of pointers */
+
+   index = malloc(q->size * sizeof(q->cursor->data));
+   if(index == NULL)
+      return False_;
+
+   posn_index = malloc(q->size * sizeof(q->cursor));
+   if(posn_index == NULL) {
+      free(index);
+      return False_;
+   }
+
+   /* Walk queue putting pointers into array */
+
+   d = Q_Head(q);
+   for(i=0; i < q->size; i++) {
+      index[i] = d;
+      posn_index[i] = q->cursor;
+      d = Q_Next(q);
+   }
+
+   /* Now sort the index */
+
+   QuickSort(index, 0, q->size - 1, Comp);
+
+   /* Rearrange the actual queue into correct order */
+
+   dn = q->head;
+   i = 0;
+   while(dn != NULL) {
+      dn->data = index[i++];
+      dn = dn->next;
+   }
+
+   /* Re-position to original element */
+
+   if(d != NULL)
+      Q_Find(q, d, Comp);
+   else  Q_Head(q);
+
+   q->sorted = True_;
+
+   return True_;
+}
+
+
+/***
+ *
+ ** function    : Q_BSearch
+ *
+ ** purpose     : binary search of queue index for node containing key
+ *
+ ** parameters  : queue pointer 'q', data pointer of key 'key',
+ *                  Comp comparison function.
+ *
+ ** returns     : integer index into array of node pointers,
+ *                or -1 if not found.
+ *
+ ** comments    : see Q_Sort for description of 'Comp' function.
+ *
+ ***/
+
+static int Q_BSearch( queue *q, void *key,
+                      int (*Comp)(const void *, const void*))
+{
+   int low, mid, hi, val;
+
+   low = 0;
+   hi = q->size - 1;
+
+   while(low <= hi) {
+      mid = (low + hi) / 2;
+      val = Comp(key, index[ mid ]);
+
+      if(val < 0)
+         hi = mid - 1;
+
+      else if(val > 0)
+         low = mid + 1;
+
+      else /* Success */
+         return mid;
+   }
+
+   /* Not Found */
+
+   return -1;
+}
+
+
+/***
+ *
+ ** function    : Q_Seek
+ *
+ ** purpose     : use index to locate data according to key in 'data'
+ *
+ ** parameters  : queue pointer 'q', data pointer 'data', Comp comparison
+ *                function.
+ *
+ ** returns     : pointer to data or NULL if could not find it or could
+ *                not sort queue.
+ *
+ ** comments    : see Q_Sort for description of 'Comp' function.
+ *
+ ***/
+
+void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *))
+{
+   int idx;
+
+   if (q == NULL) {
+       return NULL;
+   }
+
+   if(!q->sorted) {
+      if(!Q_Sort(q, Comp))
+         return NULL;
+   }
+
+   idx = Q_BSearch(q, data, Comp);
+
+   if(idx < 0)
+      return NULL;
+
+   q->cursor = posn_index[idx];
+
+   return index[idx];
+}
+
+
+
+/***
+ *
+ ** function    : Q_Insert
+ *
+ ** purpose     : Insert an element into an indexed queue
+ *
+ ** parameters  : queue pointer 'q', data pointer 'data', Comp comparison
+ *                function.
+ *
+ ** returns     : pointer to data or NULL if could not find it or could
+ *                not sort queue.
+ *
+ ** comments    : see Q_Sort for description of 'Comp' function.
+ *                WARNING! This code can be very slow since each new
+ *                element means a new Q_Sort.  Should only be used for
+ *                the insertion of the odd element ,not the piecemeal
+ *                building of an entire queue.
+ ***/
+
+int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *))
+{
+   if (q == NULL) {
+       return False_;
+   }
+
+   Q_PushHead(q, data);
+
+   if(!Q_Sort(q, Comp))
+      return False_;
+
+   return True_;
+}
+
+/* read only funcs for iterating through queue. above funcs modify queue */
+q_iter Q_Iter_Head(queue *q) {
+   return q ? (q_iter)q->head : NULL;
+}
+
+q_iter Q_Iter_Tail(queue *q) {
+   return q ? (q_iter)q->tail : NULL;
+}
+
+q_iter Q_Iter_Next(q_iter qi) {
+   return qi ? (q_iter)((node*)qi)->next : NULL;
+}
+
+q_iter Q_Iter_Prev(q_iter qi) {
+   return qi ? (q_iter)((node*)qi)->prev : NULL;
+}
+
+void * Q_Iter_Get(q_iter qi) {
+   return qi ? ((node*)qi)->data : NULL;
+}
+
+int Q_Iter_Put(q_iter qi, void* data) {
+   if(qi) {
+      ((node*)qi)->data = data;
+      return True_;
+   }
+   return False_;
+}
diff --git a/ext/xmlrpc/libxmlrpc/queue.h b/ext/xmlrpc/libxmlrpc/queue.h
new file mode 100644 (file)
index 0000000..e850b57
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+ * Date last modified: Jan 2001
+ * Modifications by Dan Libby (dan@libby.com), including:
+ *  - various fixes, null checks, etc
+ *  - addition of Q_Iter funcs, macros
+ */
+
+/*
+ *  File : q.h
+ *
+ *  Peter Yard  02 Jan 1993.
+ *
+ *  Disclaimer: This code is released to the public domain.
+ */
+
+#ifndef Q__H
+#define Q__H
+
+#ifndef False_
+   #define False_ 0
+#endif
+
+#ifndef True_
+   #define True_ 1
+#endif
+
+typedef struct nodeptr datanode;
+
+typedef struct nodeptr {
+   void        *data ;
+   datanode    *prev, *next ;
+} node ;
+
+/* For external use with Q_Iter* funcs */
+typedef struct nodeptr* q_iter;
+
+typedef struct {
+   node        *head, *tail, *cursor;
+   int         size, sorted, item_deleted;
+} queue;
+
+typedef  struct {
+   void        *dataptr;
+   node        *loc ;
+} index_elt ;
+
+
+int    Q_Init(queue  *q);
+void   Q_Destroy(queue *q);
+int    Q_IsEmpty(queue *q);
+int    Q_Size(queue *q);
+int    Q_AtHead(queue *q);
+int    Q_AtTail(queue *q);
+int    Q_PushHead(queue *q, void *d);
+int    Q_PushTail(queue *q, void *d);
+void  *Q_Head(queue *q);
+void  *Q_Tail(queue *q);
+void  *Q_PopHead(queue *q);
+void  *Q_PopTail(queue *q);
+void  *Q_Next(queue *q);
+void  *Q_Previous(queue *q);
+void  *Q_DelCur(queue *q);
+void  *Q_Get(queue *q);
+int    Q_Put(queue *q, void *data);
+int    Q_Sort(queue *q, int (*Comp)(const void *, const void *));
+int    Q_Find(queue *q, void *data,
+              int (*Comp)(const void *, const void *));
+void  *Q_Seek(queue *q, void *data,
+              int (*Comp)(const void *, const void *));
+int    Q_Insert(queue *q, void *data,
+                int (*Comp)(const void *, const void *));
+
+/* read only funcs for iterating through queue. above funcs modify queue */
+q_iter Q_Iter_Head(queue *q);
+q_iter Q_Iter_Tail(queue *q);
+q_iter Q_Iter_Next(q_iter qi);
+q_iter Q_Iter_Prev(q_iter qi);
+void*  Q_Iter_Get(q_iter qi);
+int    Q_Iter_Put(q_iter qi, void* data); // not read only! here for completeness.
+void*  Q_Iter_Del(queue *q, q_iter iter); // not read only! here for completeness.
+
+/* Fast (macro'd) versions of above */
+#define Q_Iter_Head_F(q) (q ? (q_iter)((queue*)q)->head : NULL)
+#define Q_Iter_Tail_F(q) (q ? (q_iter)((queue*)q)->tail : NULL)
+#define Q_Iter_Next_F(qi) (qi ? (q_iter)((node*)qi)->next : NULL)
+#define Q_Iter_Prev_F(qi) (qi ? (q_iter)((node*)qi)->prev : NULL)
+#define Q_Iter_Get_F(qi) (qi ? ((node*)qi)->data : NULL)
+
+#endif /* Q__H */
diff --git a/ext/xmlrpc/libxmlrpc/simplestring.c b/ext/xmlrpc/libxmlrpc/simplestring.c
new file mode 100644 (file)
index 0000000..7ce68d3
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+
+#define SIMPLESTRING_INCR 32
+
+/****h* ABOUT/simplestring
+ * NAME
+ *   simplestring
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * CREATION DATE
+ *   06/2000
+ * HISTORY
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ * PORTABILITY
+ *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
+ *   about anything with minor mods.
+ * NOTES
+ *   This code was written primarily for xmlrpc, but has found some other uses.
+ *
+ *   simplestring is, as the name implies, a simple API for dealing with C strings.
+ *   Why would I write yet another string API?  Because I couldn't find any that were
+ *   a) free / GPL, b) simple/lightweight, c) fast, not doing unneccesary strlens all
+ *   over the place.  So.  It is simple, and it seems to work, and it is pretty fast.
+ *
+ *   Oh, and it is also binary safe, ie it can handle strings with embedded NULLs,
+ *   so long as the real length is passed in.
+ *   
+ *   And the masses rejoiced.
+ *
+ * BUGS
+ *   there must be some.
+ ******/
+
+
+#include "simplestring.h"
+
+#define my_free(thing)  if(thing) {free(thing); thing = 0;}
+
+/*----------------------**
+* Begin String Functions *
+*-----------------------*/
+
+/****f* FUNC/simplestring_init
+ * NAME
+ *   simplestring_init
+ * SYNOPSIS
+ *   void simplestring_init(simplestring* string)
+ * FUNCTION
+ *   initialize string
+ * INPUTS
+ *   string - pointer to a simplestring struct that will be initialized
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   simplestring_free ()
+ *   simplestring_clear ()
+ * SOURCE
+ */
+void simplestring_init(simplestring* string) {
+   memset(string, 0, sizeof(simplestring));
+}
+/******/
+
+static void simplestring_init_str(simplestring* string) {
+   string->str = (char*)malloc(SIMPLESTRING_INCR);
+   if(string->str) {
+      string->str[0] = 0;
+      string->len = 0;
+      string->size = SIMPLESTRING_INCR;
+   }
+   else {
+      string->size = 0;
+   }
+}
+
+/****f* FUNC/simplestring_clear
+ * NAME
+ *   simplestring_clear
+ * SYNOPSIS
+ *   void simplestring_clear(simplestring* string)
+ * FUNCTION
+ *   clears contents of a string
+ * INPUTS
+ *   string - the string value to clear
+ * RESULT
+ *   void
+ * NOTES
+ *   This function is very fast as it does not de-allocate any memory.
+ * SEE ALSO
+ * 
+ * SOURCE
+ */
+void simplestring_clear(simplestring* string) {
+   if(string->str) {
+      string->str[0] = 0;
+   }
+   string->len = 0;
+}
+/******/
+
+/****f* FUNC/simplestring_free
+ * NAME
+ *   simplestring_free
+ * SYNOPSIS
+ *   void simplestring_free(simplestring* string)
+ * FUNCTION
+ *   frees contents of a string, if any. Does *not* free the simplestring struct itself.
+ * INPUTS
+ *   string - value containing string to be free'd
+ * RESULT
+ *   void
+ * NOTES
+ *   caller is responsible for allocating and freeing simplestring* struct itself.
+ * SEE ALSO
+ *   simplestring_init ()
+ * SOURCE
+ */
+void simplestring_free(simplestring* string) {
+   if(string && string->str) {
+      my_free(string->str);
+      string->len = 0;
+   }
+}
+/******/
+
+/****f* FUNC/simplestring_addn
+ * NAME
+ *   simplestring_addn
+ * SYNOPSIS
+ *   void simplestring_addn(simplestring* string, const char* add, int add_len)
+ * FUNCTION
+ *   copies n characters from source to target string
+ * INPUTS
+ *   target  - target string
+ *   source  - source string
+ *   add_len - number of characters to copy
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   simplestring_add ()
+ * SOURCE
+ */
+void simplestring_addn(simplestring* target, const char* source, int add_len) {
+   if(target && source) {
+      if(!target->str) {
+         simplestring_init_str(target);
+      }
+      if(target->len + add_len + 1 > target->size) {
+         /* newsize is current length + new length */
+         int newsize = target->len + add_len + 1;
+         int incr = target->size * 2;
+
+         /* align to SIMPLESTRING_INCR increments */
+         newsize = newsize - (newsize % incr) + incr;
+         target->str = (char*)realloc(target->str, newsize);
+
+         target->size = target->str ? newsize : 0;
+      }
+
+      if(target->str) {
+         if(add_len) {
+            memcpy(target->str + target->len, source, add_len);
+         }
+         target->len += add_len;
+         target->str[target->len] = 0; /* null terminate */
+      }
+   }
+}
+/******/
+
+/****f* FUNC/simplestring_add
+ * NAME
+ *   simplestring_add
+ * SYNOPSIS
+ *   void simplestring_add(simplestring* string, const char* add)
+ * FUNCTION
+ *   appends a string of unknown length from source to target
+ * INPUTS
+ *   target - the target string to append to
+ *   source - the source string of unknown length
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   simplestring_addn ()
+ * SOURCE
+ */
+void simplestring_add(simplestring* target, const char* source) {
+   if(target && source) {
+      simplestring_addn(target, source, strlen(source));
+   }
+}
+/******/
+
+
+/*----------------------
+* End String Functions *
+*--------------------**/
diff --git a/ext/xmlrpc/libxmlrpc/simplestring.h b/ext/xmlrpc/libxmlrpc/simplestring.h
new file mode 100644 (file)
index 0000000..a891ba6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef __SIMPLESTRING_H__
+ #define __SIMPLESTRING_H__
+
+/*-********************************
+* begin simplestring header stuff *
+**********************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+   /****s* struct/simplestring
+ * NAME
+ *  simplestring
+ * NOTES
+ *   represents a string efficiently for fast appending, etc.
+ * SOURCE
+ */
+typedef struct _simplestring {
+   char* str;         /* string buf               */
+   int len;           /* length of string/buf     */
+   int size;          /* size of allocated buffer */
+} simplestring;
+/******/
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+void simplestring_init(simplestring* string);
+void simplestring_clear(simplestring* string);
+void simplestring_free(simplestring* string);
+void simplestring_addn(simplestring* string, const char* add, int add_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*-******************************
+* end simplestring header stuff *
+********************************/
+
+#endif /* __SIMPLESTRING_H__ */
diff --git a/ext/xmlrpc/libxmlrpc/system_methods.c b/ext/xmlrpc/libxmlrpc/system_methods.c
new file mode 100644 (file)
index 0000000..742b837
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+/****h* ABOUT/system_methods
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * HISTORY
+ *   4/28/2001 -- danda -- adding system.multicall and separating out system methods.
+ * TODO
+ * NOTES
+ *******/
+
+
+#include "queue.h"
+#include "xmlrpc.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+#include "system_methods_private.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+static const char* xsm_introspection_xml =
+"<?xml version='1.0' ?>"
+
+"<introspection version='1.0'>"
+ "<typeList>"
+
+ "<typeDescription name='system.value' basetype='struct' desc='description of a value'>"
+   "<value type='string' name='name' optional='yes'>value identifier</value>"
+   "<value type='string' name='type'>value&apos;s xmlrpc or user-defined type</value>"
+   "<value type='string' name='description'>value&apos;s textual description</value> "
+   "<value type='boolean' name='optional'>true if value is optional, else it is required</value> "
+   "<value type='any' name='member' optional='yes'>a child of this element. n/a for scalar types</value> "
+ "</typeDescription>"
+
+ "<typeDescription name='system.valueList' basetype='array' desc='list of value descriptions'>"
+   "<value type='system.value'/>"
+ "</typeDescription>"
+
+ "<typeDescription name='system.stringList' basetype='array' desc='list of strings'>"
+   "<value type='string'/>"
+ "</typeDescription>"
+
+
+ "</typeList>"
+
+ "<methodList>"
+
+ "<!-- system.describeMethods -->"
+ "<methodDescription name='system.describeMethods'>"
+  "<author>Dan Libby</author>"
+  "<purpose>fully describes the methods and types implemented by this XML-RPC server.</purpose>"
+  "<version>1.1</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='array' name='methodList' optional='yes' desc='a list of methods to be described. if omitted, all are described.'>"
+      "<value type='string'>a valid method name</value>"
+     "</value>"
+    "</params>"
+    "<returns>"
+     "<value type='struct' desc='contains methods list and types list'>"
+      "<value type='array' name='methodList' desc='a list of methods'>"
+       "<value type='struct' desc='representation of a single method'>"
+        "<value type='string' name='name'>method name</value>"
+        "<value type='string' name='version' optional='yes'>method version</value>"
+        "<value type='string' name='author' optional='yes'>method author</value>"
+        "<value type='string' name='purpose' optional='yes'>method purpose</value>"
+        "<value type='array' name='signatures' desc='list of method signatures'>"
+         "<value type='struct' desc='representation of a single signature'>"
+          "<value type='system.valueList' name='params' optional='yes'>parameter list</value>"
+          "<value type='system.valueList' name='returns' optional='yes'>return value list</value>"
+         "</value>"
+        "</value>"
+        "<value type='system.stringList' name='bugs' optional='yes'>list of known bugs</value>"
+        "<value type='system.stringList' name='errors' optional='yes'>list of possible errors and error codes</value>"
+        "<value type='system.stringList' name='examples' optional='yes'>list of examples</value>"
+        "<value type='system.stringList' name='history' optional='yes'>list of modifications</value>"
+        "<value type='system.stringList' name='notes' optional='yes'>list of notes</value>"
+        "<value type='system.stringList' name='see' optional='yes'>see also.  list of related methods</value>"
+        "<value type='system.stringList' name='todo' optional='yes'>list of unimplemented features</value>"
+       "</value>"
+      "</value>"
+      "<value type='array' name='typeList' desc='a list of type descriptions. Typically used for referencing complex types'>"
+       "<value type='system.value'>a type description</value>"
+      "</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodSignature' />"
+   "<item name='system.methodHelp' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.listMethods -->"
+ "<methodDescription name='system.listMethods'>"
+  "<author>Dan Libby</author>"
+  "<purpose>enumerates the methods implemented by this XML-RPC server.</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<returns>"
+     "<value type='array' desc='an array of strings'>"
+      "<value type='string'>name of a method implemented by the server.</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.describeMethods' />"
+   "<item name='system.methodSignature' />"
+   "<item name='system.methodHelp' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.methodHelp -->"
+ "<methodDescription name='system.methodHelp'>"
+  "<author>Dan Libby</author>"
+  "<purpose>provides documentation string for a single method</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='string' name='methodName'>name of the method for which documentation is desired</value>"
+    "</params>"
+    "<returns>"
+     "<value type='string'>help text if defined for the method passed, otherwise an empty string</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodSignature' />"
+   "<item name='system.methodHelp' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.methodSignature -->"
+ "<methodDescription name='system.methodSignature'>"
+  "<author>Dan Libby</author>"
+  "<purpose>provides 1 or more signatures for a single method</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='string' name='methodName'>name of the method for which documentation is desired</value>"
+    "</params>"
+    "<returns>"
+     "<value type='array' desc='a list of arrays, each representing a signature'>"
+      "<value type='array' desc='a list of strings. the first element represents the method return value. subsequent elements represent parameters.'>"
+       "<value type='string'>a string indicating the xmlrpc type of a value. one of: string, int, double, base64, datetime, array, struct</value>"
+      "</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodHelp' />"
+   "<item name='system.describeMethods' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.multiCall -->"
+ "<methodDescription name='system.multiCall'>"
+  "<author>Dan Libby</author>"
+  "<purpose>executes multiple methods in sequence and returns the results</purpose>"
+  "<version>1.0</version>"
+  "<signatures>"
+   "<signature>"
+    "<params>"
+     "<value type='array' name='methodList' desc='an array of method call structs'>"
+      "<value type='struct' desc='a struct representing a single method call'>"
+       "<value type='string' name='methodName' desc='name of the method to be executed'/>"
+       "<value type='array' name='params' desc='an array representing the params to a method. sub-elements should match method signature'/>"
+      "</value>"
+     "</value>"
+    "</params>"
+    "<returns>"
+     "<value type='array' desc='an array of method responses'>"
+      "<value type='array' desc='an array containing a single value, which is the method&apos;s response'/>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodHelp' />"
+   "<item name='system.describeMethods' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "<!-- system.getCapabilities -->"
+ "<methodDescription name='system.getCapabilities'>"
+  "<author>Dan Libby</author>"
+  "<purpose>returns a list of capabilities supported by this server</purpose>"
+  "<version>1.0</version>"
+  "<notes><item>spec url: http://groups.yahoo.com/group/xml-rpc/message/2897</item></notes>"
+  "<signatures>"
+   "<signature>"
+    "<returns>"
+     "<value type='struct' desc='list of capabilities, each with a unique key defined by the capability&apos;s spec'>"
+      "<value type='struct' desc='definition of a single capability'>"
+       "<value type='string' name='specURL'>www address of the specification defining this capability</value>"
+       "<value type='int' name='specVersion'>version of the spec that this server's implementation conforms to</value>"
+      "</value>"
+     "</value>"
+    "</returns>"
+   "</signature>"
+  "</signatures>"
+  "<see>"
+   "<item name='system.listMethods' />"
+   "<item name='system.methodHelp' />"
+   "<item name='system.describeMethods' />"
+  "</see>"
+  "<example/>"
+  "<error/>"
+  "<note/>"
+  "<bug/>"
+  "<todo/>"
+ "</methodDescription>"
+
+ "</methodList>"
+"</introspection>";
+
+
+/* forward declarations for static (non public, non api) funcs */
+static XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+
+/*-*******************
+* System Methods API *
+*********************/
+
+static void xsm_lazy_doc_methods_cb(XMLRPC_SERVER server, void* userData) {
+   XMLRPC_VALUE xDesc = XMLRPC_IntrospectionCreateDescription(xsm_introspection_xml, NULL);
+   XMLRPC_ServerAddIntrospectionData(server, xDesc);
+   XMLRPC_CleanupValue(xDesc);
+}
+
+void xsm_register(XMLRPC_SERVER server) {
+   xi_register_system_methods(server);
+
+   XMLRPC_ServerRegisterMethod(server, xsm_token_system_multicall, xsm_system_multicall_cb);
+   XMLRPC_ServerRegisterMethod(server, xsm_token_system_get_capabilities, xsm_system_get_capabilities_cb);
+
+   /* callback for documentation generation should it be requested */
+   XMLRPC_ServerRegisterIntrospectionCallback(server, xsm_lazy_doc_methods_cb);
+}
+
+XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xArray = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
+   XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_array);
+
+   if (xArray) {
+      XMLRPC_VALUE xMethodIter = XMLRPC_VectorRewind(xArray);
+
+      while (xMethodIter) {
+         XMLRPC_REQUEST request = XMLRPC_RequestNew();
+         if(request) {
+            const char* methodName = XMLRPC_VectorGetStringWithID(xMethodIter, "methodName");
+            XMLRPC_VALUE params = XMLRPC_VectorGetValueWithID(xMethodIter, "params");
+
+            if(methodName && params) {
+               XMLRPC_VALUE xRandomArray = XMLRPC_CreateVector(0, xmlrpc_vector_array);
+               XMLRPC_RequestSetMethodName(request, methodName);
+               XMLRPC_RequestSetData(request, params);
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+
+               XMLRPC_AddValueToVector(xRandomArray, 
+                                       XMLRPC_ServerCallMethod(server, request, userData));
+
+               XMLRPC_AddValueToVector(xReturn, xRandomArray);
+            }
+            XMLRPC_RequestFree(request, 1);
+         }
+         xMethodIter = XMLRPC_VectorNext(xArray);
+      }
+   }
+   return xReturn;
+}
+
+
+XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_struct);
+   XMLRPC_VALUE xFaults = XMLRPC_CreateVector("faults_interop", xmlrpc_vector_struct);
+   XMLRPC_VALUE xIntro = XMLRPC_CreateVector("introspection", xmlrpc_vector_struct);
+
+   /* support for fault spec */
+   XMLRPC_VectorAppendString(xFaults, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php", 0);
+   XMLRPC_VectorAppendInt(xFaults, "specVersion", 20010516);
+
+   /* support for introspection spec */
+   XMLRPC_VectorAppendString(xIntro, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.introspection.php", 0);
+   XMLRPC_VectorAppendInt(xIntro, "specVersion", 20010516);
+
+   XMLRPC_AddValuesToVector(xReturn,
+                            xFaults,
+                            xIntro,
+                            NULL);
+
+   return xReturn;
+                            
+}
+
+/*-***********************
+* End System Methods API *
+*************************/
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/system_methods_private.h b/ext/xmlrpc/libxmlrpc/system_methods_private.h
new file mode 100644 (file)
index 0000000..72408fd
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Dan Libby, Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* IMPORTANT!
+ *
+ * only non-public things should be in this file.  It is fine for any .c file
+ * in xmlrpc/src to include it, but users of the public API should never
+ * include it, and thus *.h files that are part of the public API should
+ * never include it, or they would break if this file is not present.
+ */
+
+
+#ifndef __SYSTEM_METHODS_PRIVATE_H
+/*
+ * Avoid include redundancy.
+ */
+#define __SYSTEM_METHODS_PRIVATE_H
+
+/*----------------------------------------------------------------------------
+ * system_methods_private.h
+ *
+ * Purpose:
+ *   define non-public system.* methods
+ * Comments:
+ *   xsm = xmlrpc system methods
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+#define xsm_token_system_multicall "system.multiCall"
+#define xsm_token_system_get_capabilities "system.getCapabilities"
+
+
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+void xsm_register(XMLRPC_SERVER server);
+int xsm_is_system_method(XMLRPC_Callback cb);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+
+#endif /* __SYSTEM_METHODS_PRIVATE_H */
+
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/xml_element.c b/ext/xmlrpc/libxmlrpc/xml_element.c
new file mode 100644 (file)
index 0000000..255c0c5
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+
+
+/****h* ABOUT/xml_element
+ * NAME
+ *   xml_element
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * CREATION DATE
+ *   06/2000
+ * HISTORY
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ * TODO
+ *   Nicer external API. Get rid of macros.  Make opaque types, etc.
+ * PORTABILITY
+ *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
+ *   about anything with minor mods.
+ * NOTES
+ *   This code incorporates ideas from expat-ensor from http://xml.ensor.org.
+ *  
+ *   It was coded primarily to act as a go-between for expat and xmlrpc. To this
+ *   end, it stores xml elements, their sub-elements, and their attributes in an
+ *   in-memory tree.  When expat is done parsing, the tree can be walked, thus
+ *   retrieving the values.  The code can also be used to build a tree via API then
+ *   write out the tree to a buffer, thus "serializing" the xml.
+ *
+ *   It turns out this is useful for other purposes, such as parsing config files.
+ *   YMMV.
+ *
+ *   Some Features:
+ *     - output option for xml escaping data.  Choices include no escaping, entity escaping,
+ *       or CDATA sections.
+ *     - output option for character encoding.  Defaults to (none) utf-8.
+ *     - output option for verbosity/readability.  ultra-compact, newlines, pretty/level indented. 
+ *
+ * BUGS
+ *   there must be some.
+ ******/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xml_element.h"
+#include "queue.h"
+#include "expat.h"
+#include "encodings.h"
+
+#define my_free(thing)  if(thing) {free(thing); thing = 0;}
+
+#define XML_DECL_START                 "<?xml"
+#define XML_DECL_START_LEN             sizeof(XML_DECL_START) - 1
+#define XML_DECL_VERSION               "version='1.0'"
+#define XML_DECL_VERSION_LEN           sizeof(XML_DECL_VERSION) - 1
+#define XML_DECL_ENCODING_ATTR         "encoding"
+#define XML_DECL_ENCODING_ATTR_LEN     sizeof(XML_DECL_ENCODING_ATTR) - 1
+#define XML_DECL_ENCODING_DEFAULT      "utf-8"
+#define XML_DECL_ENCODING_DEFAULT_LEN  sizeof(XML_DECL_ENCODING_DEFAULT) - 1
+#define XML_DECL_END                   "?>"
+#define XML_DECL_END_LEN               sizeof(XML_DECL_END) - 1
+#define START_TOKEN_BEGIN              "<"
+#define START_TOKEN_BEGIN_LEN          sizeof(START_TOKEN_BEGIN) - 1
+#define START_TOKEN_END                ">"
+#define START_TOKEN_END_LEN            sizeof(START_TOKEN_END) - 1
+#define EMPTY_START_TOKEN_END          "/>"
+#define EMPTY_START_TOKEN_END_LEN      sizeof(EMPTY_START_TOKEN_END) - 1
+#define END_TOKEN_BEGIN                "</"
+#define END_TOKEN_BEGIN_LEN            sizeof(END_TOKEN_BEGIN) - 1
+#define END_TOKEN_END                  ">"
+#define END_TOKEN_END_LEN              sizeof(END_TOKEN_END) - 1
+#define ATTR_DELIMITER                 "\""
+#define ATTR_DELIMITER_LEN             sizeof(ATTR_DELIMITER) - 1
+#define CDATA_BEGIN                    "<![CDATA["
+#define CDATA_BEGIN_LEN                sizeof(CDATA_BEGIN) - 1
+#define CDATA_END                      "]]>"
+#define CDATA_END_LEN                  sizeof(CDATA_END) - 1
+#define EQUALS                         "="
+#define EQUALS_LEN                     sizeof(EQUALS) - 1
+#define WHITESPACE                     " "
+#define WHITESPACE_LEN                 sizeof(WHITESPACE) - 1
+#define NEWLINE                        "\n"
+#define NEWLINE_LEN                    sizeof(NEWLINE) - 1
+#define MAX_VAL_BUF                    144
+#define SCALAR_STR                     "SCALAR"
+#define SCALAR_STR_LEN                 sizeof(SCALAR_STR) - 1
+#define VECTOR_STR                     "VECTOR"
+#define VECTOR_STR_LEN                 sizeof(VECTOR_STR) - 1
+#define RESPONSE_STR                   "RESPONSE"
+#define RESPONSE_STR_LEN               sizeof(RESPONSE_STR) - 1
+
+
+/*-----------------------------
+- Begin xml_element Functions -
+-----------------------------*/
+
+/****f* xml_element/xml_elem_free_non_recurse
+ * NAME
+ *   xml_elem_free_non_recurse
+ * SYNOPSIS
+ *   void xml_elem_free_non_recurse(xml_element* root)
+ * FUNCTION
+ *   free a single xml element.  child elements will not be freed.
+ * INPUTS
+ *   root - the element to free
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_free ()
+ *   xml_elem_new ()
+ * SOURCE
+ */
+void xml_elem_free_non_recurse(xml_element* root) {
+   if(root) {
+      xml_element_attr* attrs = Q_Head(&root->attrs);
+      while(attrs) {
+         my_free(attrs->key);
+         my_free(attrs->val);
+         my_free(attrs);
+         attrs = Q_Next(&root->attrs);
+      }
+
+      Q_Destroy(&root->children);
+      Q_Destroy(&root->attrs);
+      my_free((char*)root->name);
+      simplestring_free(&root->text);
+      my_free(root);
+   }
+}
+/******/
+
+/****f* xml_element/xml_elem_free
+ * NAME
+ *   xml_elem_free
+ * SYNOPSIS
+ *   void xml_elem_free(xml_element* root)
+ * FUNCTION
+ *   free an xml element and all of its child elements
+ * INPUTS
+ *   root - the root of an xml tree you would like to free
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_free_non_recurse ()
+ *   xml_elem_new ()
+ * SOURCE
+ */
+void xml_elem_free(xml_element* root) {
+   if(root) {
+      xml_element* kids = Q_Head(&root->children);
+      while(kids) {
+         xml_elem_free(kids);
+         kids = Q_Next(&root->children);
+      }
+      xml_elem_free_non_recurse(root);
+   }
+}
+/******/
+
+/****f* xml_element/xml_elem_new
+ * NAME
+ *   xml_elem_new
+ * SYNOPSIS
+ *   xml_element* xml_elem_new()
+ * FUNCTION
+ *   allocates and initializes a new xml_element
+ * INPUTS
+ *   none
+ * RESULT
+ *   xml_element* or NULL.  NULL indicates an out-of-memory condition.
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_free ()
+ *   xml_elem_free_non_recurse ()
+ * SOURCE
+ */
+xml_element* xml_elem_new() {
+   xml_element* elem = calloc(1, sizeof(xml_element));
+   if(elem) {
+      Q_Init(&elem->children);
+      Q_Init(&elem->attrs);
+      simplestring_init(&elem->text);
+
+      /* init empty string in case we don't find any char data */
+      simplestring_addn(&elem->text, "", 0);
+   }
+   return elem;
+}
+/******/
+
+static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len)
+{
+   return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0;
+}
+
+
+
+static int create_xml_escape(char *pString, unsigned char c)
+{ 
+  int counter = 0;
+
+  pString[counter++] = '&';
+  pString[counter++] = '#';
+  if(c >= 100) {
+    pString[counter++] = c / 100 + '0';
+    c = c % 100;
+  }
+  if(c >= 10) {
+    pString[counter++] = c / 10 + '0';
+    c = c % 10;
+  }
+  pString[counter++] = c + '0';
+  pString[counter++] = ';';
+  return counter; 
+}
+
+#define non_ascii(c) (c > 127)
+#define non_print(c) (!isprint(c))
+#define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<')
+#define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */
+
+/*
+ * xml_elem_entity_escape
+ *
+ * Purpose:
+ *   escape reserved xml chars and non utf-8 chars as xml entities
+ * Comments:
+ *   The return value may be a new string, or null if no
+ *     conversion was performed. In the latter case, *newlen will
+ *     be 0.
+ * Flags (to escape)
+ *  xml_elem_no_escaping             = 0x000,
+ *  xml_elem_entity_escaping         = 0x002,   // escape xml special chars as entities
+ *  xml_elem_non_ascii_escaping      = 0x008,   // escape chars above 127
+ *  xml_elem_cdata_escaping          = 0x010,   // wrap in cdata
+ */
+static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) {
+  char *pRetval = 0;
+  int iNewBufLen=0;
+
+#define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \
+                                 ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \
+                                 ((flag & xml_elem_non_print_escaping) && non_print(c)) )
+
+  if(buf && *buf) {
+    const unsigned char *bufcopy;
+    char *NewBuffer;
+    int ToBeXmlEscaped=0;
+    int iLength;
+    bufcopy = buf;
+    iLength= old_len ? old_len : strlen(buf);
+    while(*bufcopy) {
+      if( should_escape(*bufcopy, flags) ) {
+       /* the length will increase by length of xml escape - the character length */
+       iLength += entity_length(*bufcopy);
+       ToBeXmlEscaped=1;
+      }
+      bufcopy++;
+    }
+
+    if(ToBeXmlEscaped) {
+
+      NewBuffer= malloc(iLength+1);
+      if(NewBuffer) {
+       bufcopy=buf;
+       while(*bufcopy) {
+         if(should_escape(*bufcopy, flags)) {
+           iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy);
+         }
+         else {
+           NewBuffer[iNewBufLen++]=*bufcopy;
+         }
+         bufcopy++;
+       }
+       NewBuffer[iNewBufLen] = 0;
+       pRetval = NewBuffer;
+      }
+    }
+  }
+
+  if(newlen) {
+     *newlen = iNewBufLen;
+  }
+
+  return pRetval;
+}
+
+
+static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth)
+{
+   int i;
+   static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT};
+   static char whitespace[] = "                                                                                               "
+                              "                                                                                               "
+                              "                                                                                               ";
+   depth++;
+
+   if(!el) {
+      fprintf(stderr, "Nothing to write\n");
+      return;
+   }
+   if(!options) {
+      options = &default_opts;
+   }
+
+   /* print xml declaration if at root level */
+   if(depth == 1) {
+      xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN);
+      xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+      xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN);
+      if(options->encoding && *options->encoding) {
+          xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+          xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN);
+          xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
+          xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+          xml_elem_writefunc(fptr, options->encoding, data, 0);
+          xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+      }
+      xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+      xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN);
+      if(options->verbosity != xml_elem_no_white_space) {
+         xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
+      }
+   }
+
+   if(options->verbosity == xml_elem_pretty && depth > 2) {
+         xml_elem_writefunc(fptr, whitespace, data, depth - 2);
+   }
+   /* begin element */
+   xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN);
+   if(el->name) {
+      xml_elem_writefunc(fptr, el->name, data, 0);
+
+      /* write attrs, if any */
+      if(Q_Size(&el->attrs)) {
+         xml_element_attr* iter = Q_Head(&el->attrs);
+         while( iter ) {
+            xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
+            xml_elem_writefunc(fptr, iter->key, data, 0);
+            xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
+            xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+            xml_elem_writefunc(fptr, iter->val, data, 0);
+            xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
+
+            iter = Q_Next(&el->attrs);
+         }
+      }
+   }
+   else {
+      xml_elem_writefunc(fptr, "None", data, 0);
+   }
+   /* if no text and no children, use abbreviated form, eg: <foo/> */
+   if(!el->text.len && !Q_Size(&el->children)) {
+       xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN);
+   }
+   /* otherwise, print element contents */
+   else {
+       xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN);
+
+       /* print text, if any */
+       if(el->text.len) {
+          char* escaped_str = el->text.str;
+          int buflen = el->text.len;
+
+          if(options->escaping && options->escaping != xml_elem_cdata_escaping) {
+             escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping );
+             if(!escaped_str) {
+                escaped_str = el->text.str;
+             }
+          }
+
+          if(options->escaping & xml_elem_cdata_escaping) {
+             xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN);
+          }
+
+          xml_elem_writefunc(fptr, escaped_str, data, buflen);
+
+          if(escaped_str != el->text.str) {
+             my_free(escaped_str);
+          }
+
+          if(options->escaping & xml_elem_cdata_escaping) {
+             xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN);
+          }
+       }
+       /* no text, so print child elems */
+       else {
+          xml_element *kids = Q_Head(&el->children);
+          i = 0;
+          while( kids ) {
+             if(i++ == 0) {
+                if(options->verbosity != xml_elem_no_white_space) {
+                   xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
+                }
+             }
+             xml_element_serialize(kids, fptr, data, options, depth);
+             kids = Q_Next(&el->children);
+          }
+          if(i) {
+             if(options->verbosity == xml_elem_pretty && depth > 2) {
+                   xml_elem_writefunc(fptr, whitespace, data, depth - 2);
+             }
+          }
+       }
+
+       xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN);
+       xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0);
+       xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN);
+   }
+   if(options->verbosity != xml_elem_no_white_space) {
+      xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
+   }
+}
+
+/* print buf to file */
+static file_out_fptr(void *f, const char *text, int size)
+{
+   fputs(text, (FILE *)f);
+}
+
+/* print buf to simplestring */
+static simplestring_out_fptr(void *f, const char *text, int size)
+{
+   simplestring* buf = (simplestring*)f;
+   if(buf) {
+      simplestring_addn(buf, text, size);
+   }
+}
+
+/****f* xml_element/xml_elem_serialize_to_string
+ * NAME
+ *   xml_elem_serialize_to_string
+ * SYNOPSIS
+ *   void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
+ * FUNCTION
+ *   writes element tree as XML into a newly allocated buffer
+ * INPUTS
+ *   el      - root element of tree
+ *   options - options determining how output is written.  see XML_ELEM_OUTPUT_OPTIONS
+ *   buf_len - length of returned buffer, if not null.
+ * RESULT
+ *   char* or NULL. Must be free'd by caller.
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_serialize_to_stream ()
+ *   xml_elem_parse_buf ()
+ * SOURCE
+ */
+char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
+{
+   simplestring buf;
+   simplestring_init(&buf);
+
+   xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0);
+
+   if(buf_len) {
+      *buf_len = buf.len;
+   }
+
+   return buf.str;
+}
+/******/
+
+/****f* xml_element/xml_elem_serialize_to_stream
+ * NAME
+ *   xml_elem_serialize_to_stream
+ * SYNOPSIS
+ *   void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
+ * FUNCTION
+ *   writes element tree as XML into a stream (typically an opened file)
+ * INPUTS
+ *   el      - root element of tree
+ *   output  - stream handle
+ *   options - options determining how output is written.  see XML_ELEM_OUTPUT_OPTIONS
+ * RESULT
+ *   void
+ * NOTES
+ * SEE ALSO
+ *   xml_elem_serialize_to_string ()
+ *   xml_elem_parse_buf ()
+ * SOURCE
+ */
+void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
+{
+   xml_element_serialize(el, file_out_fptr, (void *)output, options, 0);
+}
+/******/
+
+/*--------------------------*
+* End xml_element Functions *
+*--------------------------*/
+
+
+/*----------------------
+* Begin Expat Handlers *
+*---------------------*/
+
+typedef struct _xml_elem_data {
+   xml_element*           root;
+   xml_element*           current;
+   XML_ELEM_INPUT_OPTIONS input_options;
+   int                    needs_enc_conversion;
+} xml_elem_data;
+
+
+/* expat start of element handler */
+static void startElement(void *userData, const char *name, const char **attrs)
+{
+   int i;
+   xml_element *c;
+   xml_elem_data* mydata = (xml_elem_data*)userData;
+   const char** p = attrs;
+
+   if(mydata) {
+      c = mydata->current;
+
+      mydata->current = xml_elem_new();
+      mydata->current->name = (char*)strdup(name);
+      mydata->current->parent = c;
+
+      /* init attrs */
+      while(p && *p) {
+         xml_element_attr* attr = malloc(sizeof(xml_element_attr));
+         if(attr) {
+            attr->key = strdup(*p);
+            attr->val = strdup(*(p+1));
+            Q_PushTail(&mydata->current->attrs, attr);
+
+            p += 2;
+         }
+      }
+   }
+}
+
+/* expat end of element handler */
+static void endElement(void *userData, const char *name)
+{
+   xml_elem_data* mydata = (xml_elem_data*)userData;
+
+   if(mydata && mydata->current && mydata->current->parent) {
+      Q_PushTail(&mydata->current->parent->children, mydata->current);
+
+      mydata->current = mydata->current->parent;
+   }
+}
+
+/* expat char data handler */
+static void charHandler(void *userData,
+                        const char *s,
+                        int len)
+{
+   xml_elem_data* mydata = (xml_elem_data*)userData;
+   if(mydata && mydata->current) {
+
+      /* Check if we need to decode utf-8 parser output to another encoding */
+      if(mydata->needs_enc_conversion && mydata->input_options->encoding) {
+         char* add_text = utf8_decode(s, len, &len, mydata->input_options->encoding);
+         if(add_text) {
+            simplestring_addn(&mydata->current->text, add_text, len);
+            free(add_text);
+            return;
+         }
+      }
+      simplestring_addn(&mydata->current->text, s, len);
+   }
+}
+/******/
+
+/*-------------------*
+* End Expat Handlers *
+*-------------------*/
+
+/*-------------------*
+* xml_elem_parse_buf *
+*-------------------*/
+
+/****f* xml_element/xml_elem_parse_buf
+ * NAME
+ *   xml_elem_parse_buf
+ * SYNOPSIS
+ *   xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
+ * FUNCTION
+ *   parse a buffer containing XML into an xml_element in-memory tree
+ * INPUTS
+ *   in_buf   - buffer containing XML document
+ *   len      - length of buffer
+ *   options  - input options. optional
+ *   error    - error result data. optional. check if result is null.
+ * RESULT
+ *   void
+ * NOTES
+ *   The returned data must be free'd by caller
+ * SEE ALSO
+ *   xml_elem_serialize_to_string ()
+ *   xml_elem_free ()
+ * SOURCE
+ */
+xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
+{
+   xml_element* xReturn = NULL;
+   char buf[100] = "";
+   static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8};
+
+   if(!options) {
+      options = &default_opts;
+   }
+
+   if(in_buf) {
+      XML_Parser parser;
+      xml_elem_data mydata = {0};
+
+      parser = XML_ParserCreate(NULL);
+
+      mydata.root = xml_elem_new();
+      mydata.current = mydata.root;
+      mydata.input_options = options;
+      mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8);
+
+      XML_SetElementHandler(parser, startElement, endElement);
+      XML_SetCharacterDataHandler(parser, charHandler);
+
+      /* pass the xml_elem_data struct along */
+      XML_SetUserData(parser, (void*)&mydata);
+
+      if(!len) {
+         len = strlen(in_buf);
+      }
+
+      /* parse the XML */
+      if(XML_Parse(parser, in_buf, len, 1) == 0) {
+         enum XML_Error err_code = XML_GetErrorCode(parser);
+         int line_num = XML_GetCurrentLineNumber(parser);
+         int col_num = XML_GetCurrentColumnNumber(parser);
+         long byte_idx = XML_GetCurrentByteIndex(parser);
+         int byte_total = XML_GetCurrentByteCount(parser);
+         const char * error_str = XML_ErrorString(err_code);
+         if(byte_idx >= 0) {
+             snprintf(buf, 
+                      sizeof(buf),
+                      "\n\tdata beginning %i before byte index: %s\n",
+                      byte_idx > 10  ? 10 : byte_idx,
+                      in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx));
+         }
+
+         fprintf(stderr, "expat reports error code %i\n"
+                "\tdescription: %s\n"
+                "\tline: %i\n"
+                "\tcolumn: %i\n"
+                "\tbyte index: %i\n"
+                "\ttotal bytes: %i\n%s ",
+                err_code, error_str, line_num, 
+                col_num, byte_idx, byte_total, buf);
+
+
+          /* error condition */
+          if(error) {
+              error->parser_code = (long)err_code;
+              error->line = line_num;
+              error->column = col_num;
+              error->byte_index = byte_idx;
+              error->parser_error = error_str;
+          }
+      }
+      else {
+         xReturn = (xml_element*)Q_Head(&mydata.root->children);
+      }
+
+      XML_ParserFree(parser);
+
+
+      xml_elem_free_non_recurse(mydata.root);
+   }
+
+   return xReturn;
+}
+
+/******/
diff --git a/ext/xmlrpc/libxmlrpc/xml_element.h b/ext/xmlrpc/libxmlrpc/xml_element.h
new file mode 100644 (file)
index 0000000..bf4f3a8
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef __XML_ELEMENT_H__
+ #define __XML_ELEMENT_H__
+
+/* includes */
+#include <stdio.h>
+#include "queue.h"
+#include "simplestring.h"
+#include "encodings.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/****d* enum/XML_ELEM_VERBOSITY
+ * NAME
+ *   XML_ELEM_VERBOSITY
+ * NOTES
+ *   verbosity/readability options for generated xml
+ * SEE ALSO
+ *   XML_ELEM_OUTPUT_OPTIONS
+ * SOURCE
+ */
+typedef enum _xml_elem_verbosity {
+   xml_elem_no_white_space,    /* compact xml with no white space            */
+   xml_elem_newlines_only,     /* add newlines for enhanced readability      */
+   xml_elem_pretty             /* add newlines and indent accordint to depth */
+} XML_ELEM_VERBOSITY;
+/******/
+
+
+/****d* enum/XML_ELEM_ESCAPING
+ * NAME
+ *   XML_ELEM_ESCAPING
+ * NOTES
+ * xml escaping options for generated xml
+ * SEE ALSO
+ *   XML_ELEM_OUTPUT_OPTIONS
+ * SOURCE
+ */
+typedef enum _xml_elem_escaping {
+   xml_elem_no_escaping             = 0x000,
+   xml_elem_markup_escaping         = 0x002,   /* entity escape xml special chars         */
+   xml_elem_non_ascii_escaping      = 0x008,   /* entity escape chars above 127           */
+   xml_elem_non_print_escaping      = 0x010,   /* entity escape non print (illegal) chars */
+   xml_elem_cdata_escaping          = 0x020,   /* wrap in cdata section                   */
+} XML_ELEM_ESCAPING;
+/******/
+
+
+/****s* struct/XML_ELEM_OUTPUT_OPTIONS
+ * NAME
+ *   XML_ELEM_OUTPUT_OPTIONS
+ * NOTES
+ *   defines various output options
+ * SOURCE
+ */
+typedef struct _xml_output_options {
+   XML_ELEM_VERBOSITY           verbosity;      /* length/verbosity of xml        */
+   XML_ELEM_ESCAPING            escaping;       /* how to escape special chars    */
+   const char*                  encoding;       /* <?xml encoding="<encoding>" ?> */
+} STRUCT_XML_ELEM_OUTPUT_OPTIONS, *XML_ELEM_OUTPUT_OPTIONS;
+/******/
+
+/****s* struct/XML_ELEM_INPUT_OPTIONS
+ * NAME
+ *   XML_ELEM_INPUT_OPTIONS
+ * NOTES
+ *   defines various input options
+ * SOURCE
+ */
+typedef struct _xml_input_options {
+  ENCODING_ID                  encoding;       /* which encoding to use.       */
+} STRUCT_XML_ELEM_INPUT_OPTIONS, *XML_ELEM_INPUT_OPTIONS;
+/******/
+
+/****s* struct/XML_ELEM_ERROR
+ * NAME
+ *   XML_ELEM_ERROR
+ * NOTES
+ *   defines an xml parser error
+ * SOURCE
+ */
+typedef struct _xml_elem_error {
+  int parser_code;
+  const char* parser_error;
+  long line;
+  long column;
+  long byte_index;
+} STRUCT_XML_ELEM_ERROR, *XML_ELEM_ERROR;
+/******/
+
+
+/*-************************
+* begin xml element stuff *
+**************************/
+
+/****s* struct/xml_elem_attr
+ * NAME
+ *  xml_elem_attr
+ * NOTES
+ *   representation of an xml attribute, foo="bar"
+ * SOURCE
+ */
+typedef struct _xml_element_attr {
+   char* key;        /* attribute key   */
+   char* val;        /* attribute value */
+} xml_element_attr;
+/******/
+
+/****s* struct/xml_elem_attr
+ * NAME
+ *  xml_elem_attr
+ * NOTES
+ *   representation of an xml element, eg <candidate name="Harry Browne" party="Libertarian"/>
+ * SOURCE
+ */
+typedef struct _xml_element {
+   const char*   name;           /* element identifier */
+   simplestring  text;           /* text contained between element begin/end pairs */
+   struct _xml_element* parent;  /* element's parent */
+                                 
+   queue        attrs;           /* attribute list */
+   queue        children;        /* child element list */
+} xml_element;
+/******/
+
+void xml_elem_free(xml_element* root);
+void xml_elem_free_non_recurse(xml_element* root);
+xml_element* xml_elem_new();
+char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len);
+void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options);
+xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error);
+
+/*-**********************
+* end xml element stuff *
+************************/
+
+/*-**********************
+* Begin xml_element API *
+************************/
+
+/****d* VALUE/XMLRPC_MACROS
+ * NAME
+ *   Some Helpful Macros
+ * NOTES
+ *   Some macros for making life easier.  Should be self-explanatory.
+ * SEE ALSO
+ *   XMLRPC_AddValueToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+#define xml_elem_next_element(el) ((el) ? (xml_element *)Q_Next(&el->children) : NULL)
+#define xml_elem_head_element(el) ((el) ? (xml_element *)Q_Head(&el->children) : NULL)
+#define xml_elem_next_attr(el) ((el) ? (xml_element_attr *)Q_Next(&el->attrs) : NULL)
+#define xml_elem_head_attr(el) ((el) ? (xml_element_attr *)Q_Head(&el->attrs) : NULL)
+#define xml_elem_get_name(el) (char *)((el) ? el->name : NULL)
+#define xml_elem_get_val(el) (char *)((el) ? el->text.str : NULL)
+/******/
+
+
+/*-********************
+* End xml_element API *
+**********************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_ELEMENT_H__ */
diff --git a/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c
new file mode 100644 (file)
index 0000000..0d827a5
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include "xml_to_dandarpc.h"
+#include "base64.h"
+
+/* list of tokens used in vocab */
+#define ELEM_METHODCALL     "methodCall"
+#define ELEM_METHODNAME     "methodName"
+#define ELEM_METHODRESPONSE "methodResponse"
+#define ELEM_ROOT           "simpleRPC"
+
+#define ATTR_ARRAY          "array"
+#define ATTR_BASE64         "base64"
+#define ATTR_BOOLEAN        "boolean"
+#define ATTR_DATETIME       "dateTime.iso8601"
+#define ATTR_DOUBLE         "double"
+#define ATTR_ID             "id"
+#define ATTR_INT            "int"
+#define ATTR_MIXED          "mixed"
+#define ATTR_SCALAR         "scalar"
+#define ATTR_STRING         "string"
+#define ATTR_STRUCT         "struct"
+#define ATTR_TYPE           "type"
+#define ATTR_VECTOR         "vector"
+#define ATTR_VERSION        "version"
+
+#define VAL_VERSION_0_9     "0.9"
+
+
+XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) {
+   if(!xCurrent) {
+      xCurrent = XMLRPC_CreateValueEmpty();
+   }
+
+   if(el->name) {
+      const char* id = NULL;
+      const char* type = NULL;
+      xml_element_attr* attr_iter = Q_Head(&el->attrs);
+
+      while(attr_iter) {
+         if(!strcmp(attr_iter->key, ATTR_ID)) {
+            id = attr_iter->val;
+         }
+         if(!strcmp(attr_iter->key, ATTR_TYPE)) {
+            type = attr_iter->val;
+         }
+         attr_iter = Q_Next(&el->attrs);
+      }
+
+      if(id) {
+         XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact);
+      }
+
+      if(!strcmp(el->name, ATTR_SCALAR)) {
+         if(!type || !strcmp(type, ATTR_STRING)) {
+            XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len);
+         }
+         else if(!strcmp(type, ATTR_INT)) {
+            XMLRPC_SetValueInt(xCurrent, atoi(el->text.str));
+         }
+         else if(!strcmp(type, ATTR_BOOLEAN)) {
+            XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str));
+         }
+         else if(!strcmp(type, ATTR_DOUBLE)) {
+            XMLRPC_SetValueDouble(xCurrent, atof(el->text.str));
+         }
+         else if(!strcmp(type, ATTR_DATETIME)) {
+            XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str);
+         }
+         else if(!strcmp(type, ATTR_BASE64)) {
+            struct buffer_st buf;
+            base64_decode(&buf, el->text.str, el->text.len);
+            XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset);
+            buffer_delete(&buf);
+         }
+      }
+      else if(!strcmp(el->name, ATTR_VECTOR)) {
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+
+         if(!type || !strcmp(type, ATTR_MIXED)) {
+            XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
+         }
+         else if(!strcmp(type, ATTR_ARRAY)) {
+            XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
+         }
+         else if(!strcmp(type, ATTR_STRUCT)) {
+            XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct);
+         }
+         while( iter ) {
+            XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty();
+            xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter);
+            XMLRPC_AddValueToVector(xCurrent, xNext);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      }
+      else {
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+         while( iter ) {
+            xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+
+         if(!strcmp(el->name, ELEM_METHODCALL)) {
+            if(request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+            }
+         }
+         else if(!strcmp(el->name, ELEM_METHODRESPONSE)) {
+            if(request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
+            }
+         }
+         else if(!strcmp(el->name, ELEM_METHODNAME)) {
+            if(request) {
+               XMLRPC_RequestSetMethodName(request, el->text.str);
+            }
+         }
+      }
+   }
+   return xCurrent;
+}
+
+XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el)
+{
+   return xml_element_to_DANDARPC_REQUEST_worker(NULL, NULL, el);
+}
+
+XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
+{
+   if(request) {
+      return XMLRPC_RequestSetData(request, xml_element_to_DANDARPC_REQUEST_worker(request, NULL, el));
+   }
+   return NULL;
+}
+
+xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) {
+#define BUF_SIZE 512
+   xml_element* root = NULL;
+   if(node) {
+      char buf[BUF_SIZE];
+      const char* id = XMLRPC_GetValueID(node);
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
+      XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request);
+      int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space);
+      xml_element* elem_val = xml_elem_new();
+      const char* pAttrType = NULL;
+
+      xml_element_attr* attr_type = bNoAddType ? NULL : malloc(sizeof(xml_element_attr));
+       
+      if(attr_type) {
+         attr_type->key = strdup(ATTR_TYPE);
+         attr_type->val = 0;
+         Q_PushTail(&elem_val->attrs, attr_type);
+      }
+
+      elem_val->name = (type == xmlrpc_vector) ? strdup(ATTR_VECTOR) : strdup(ATTR_SCALAR);
+
+      if(id && *id) {
+         xml_element_attr* attr_id = malloc(sizeof(xml_element_attr));
+         if(attr_id) {
+            attr_id->key = strdup(ATTR_ID);
+            attr_id->val = strdup(id);
+            Q_PushTail(&elem_val->attrs, attr_id);
+         }
+      }
+
+      switch(type) {
+         case xmlrpc_string:
+            pAttrType = ATTR_STRING;
+            simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
+            break;
+         case xmlrpc_int:
+            pAttrType = ATTR_INT;
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_boolean:
+            pAttrType = ATTR_BOOLEAN;
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_double:
+            pAttrType = ATTR_DOUBLE;
+            snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_datetime:
+            pAttrType = ATTR_DATETIME;
+            simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
+            break;
+         case xmlrpc_base64:
+            {
+               struct buffer_st buf;
+               pAttrType = ATTR_BASE64;
+               base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
+               simplestring_addn(&elem_val->text, buf.data, buf.offset );
+               buffer_delete(&buf);
+            }
+            break;
+         case xmlrpc_vector:
+            {
+               XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
+               XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
+
+               switch(my_type) {
+                  case xmlrpc_vector_array:
+                     pAttrType = ATTR_ARRAY;
+                     break;
+                  case xmlrpc_vector_mixed:
+                     pAttrType = ATTR_MIXED;
+                     break;
+                  case xmlrpc_vector_struct:
+                     pAttrType = ATTR_STRUCT;
+                     break;
+                  default:
+                     break;
+               }
+
+               /* recurse through sub-elements */
+               while( xIter ) {
+                  xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter);
+                  if(next_el) {
+                     Q_PushTail(&elem_val->children, next_el);
+                  }
+                  xIter = XMLRPC_VectorNext(node);
+               }
+            }
+            break;
+         default:
+            break;
+      }
+      if(pAttrType && attr_type && !bNoAddType) {
+         attr_type->val = strdup(pAttrType);
+      }
+      root = elem_val;
+   }
+   return root;
+}
+
+xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
+   return DANDARPC_to_xml_element_worker(NULL, node);
+}
+
+xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
+   xml_element* wrapper = NULL;
+   xml_element* root = NULL;
+   if(request) {
+      XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
+      const char* pStr = NULL;
+      xml_element_attr* version = malloc(sizeof(xml_element_attr));
+      version->key = strdup(ATTR_VERSION);
+      version->val = strdup(VAL_VERSION_0_9);
+      
+      wrapper = xml_elem_new();
+
+      if(request_type == xmlrpc_request_response) {
+         pStr = ELEM_METHODRESPONSE;
+      }
+      else if(request_type == xmlrpc_request_call) {
+         pStr = ELEM_METHODCALL;
+      }
+      if(pStr) {
+         wrapper->name = strdup(pStr);
+      }
+
+      root = xml_elem_new();
+      root->name = strdup(ELEM_ROOT);
+      Q_PushTail(&root->attrs, version);
+      Q_PushTail(&root->children, wrapper);
+
+      pStr = XMLRPC_RequestGetMethodName(request);
+
+      if(pStr) {
+         xml_element* method = xml_elem_new();
+         method->name = strdup(ELEM_METHODNAME);
+         simplestring_add(&method->text, pStr);
+         Q_PushTail(&wrapper->children, method);
+      }
+      Q_PushTail(&wrapper->children, 
+                 DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request)));
+   }
+   return root;
+}
+
diff --git a/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h
new file mode 100644 (file)
index 0000000..6facb55
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef XML_TO_DANDARPC_H
+ #define XML_TO_DANDARPC_H
+
+#include "time.h" 
+#include "xmlrpc.h"
+
+XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el);
+XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el);
+xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node);
+xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request);
+
+#endif /* XML_TO_DANDARPC_H */
diff --git a/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c
new file mode 100644 (file)
index 0000000..1cd3d9a
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+#include <string.h>
+#include <stdlib.h>
+#include "xml_to_xmlrpc.h"
+#include "base64.h"
+
+/* list of tokens used in vocab */
+#define ELEM_ARRAY          "array"
+#define ELEM_BASE64         "base64"
+#define ELEM_BOOLEAN        "boolean"
+#define ELEM_DATA           "data"
+#define ELEM_DATETIME       "dateTime.iso8601"
+#define ELEM_DOUBLE         "double"
+#define ELEM_FAULT          "fault"
+#define ELEM_FAULTCODE      "faultCode"
+#define ELEM_FAULTSTRING    "faultString"
+#define ELEM_I4             "i4"
+#define ELEM_INT            "int"
+#define ELEM_MEMBER         "member"
+#define ELEM_METHODCALL     "methodCall"
+#define ELEM_METHODNAME     "methodName"
+#define ELEM_METHODRESPONSE "methodResponse"
+#define ELEM_NAME           "name"
+#define ELEM_PARAM          "param"
+#define ELEM_PARAMS         "params"
+#define ELEM_STRING         "string"
+#define ELEM_STRUCT         "struct"
+#define ELEM_VALUE          "value"
+
+
+XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
+   if (!current_val) {
+      /* This should only be the case for the first element */
+      current_val = XMLRPC_CreateValueEmpty();
+   }
+
+   if (el->name) {
+      if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */
+          || ((!strcmp(el->name, ELEM_PARAMS)) && 
+              (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call))    /* this "PARAMS" concept is silly.  dave?! */
+          || !strcmp(el->name, ELEM_FAULT)) {  /* so is this "FAULT" nonsense. */
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+         XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
+
+         while (iter) {
+            XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
+            xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
+            XMLRPC_AddValueToVector(current_val, xNextVal);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      } else if (!strcmp(el->name, ELEM_STRUCT)) {
+         xml_element* iter = (xml_element*)Q_Head(&el->children);
+         XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
+
+         while ( iter ) {
+            XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
+            xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
+            XMLRPC_AddValueToVector(current_val, xNextVal);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      } else if (!strcmp(el->name, ELEM_STRING) || 
+                 (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
+         XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
+      } else if (!strcmp(el->name, ELEM_NAME)) {
+         XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
+      } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
+         XMLRPC_SetValueInt(current_val, atoi(el->text.str));
+      } else if (!strcmp(el->name, ELEM_BOOLEAN)) {
+         XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
+      } else if (!strcmp(el->name, ELEM_DOUBLE)) {
+         XMLRPC_SetValueDouble(current_val, atof(el->text.str));
+      } else if (!strcmp(el->name, ELEM_DATETIME)) {
+         XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
+      } else if (!strcmp(el->name, ELEM_BASE64)) {
+         struct buffer_st buf;
+         base64_decode(&buf, el->text.str, el->text.len);
+         XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
+         buffer_delete(&buf);
+      } else {
+         xml_element* iter;
+
+         if (!strcmp(el->name, ELEM_METHODCALL)) {
+            if (request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+            }
+         } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
+            if (request) {
+               XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
+            }
+         } else if (!strcmp(el->name, ELEM_METHODNAME)) {
+            if (request) {
+               XMLRPC_RequestSetMethodName(request, el->text.str);
+            }
+         }
+
+         iter = (xml_element*)Q_Head(&el->children);
+         while ( iter ) {
+            xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, 
+                                                 current_val, iter);
+            iter = (xml_element*)Q_Next(&el->children);
+         }
+      }
+   }
+   return current_val;
+}
+
+XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
+{
+   return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
+}
+
+XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
+{
+   if (request) {
+      return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
+   }
+   return NULL;
+}
+
+xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, 
+                                          XMLRPC_REQUEST_TYPE request_type, int depth) {
+#define BUF_SIZE 512
+   xml_element* root = NULL;
+   if (node) {
+      char buf[BUF_SIZE];
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
+      XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
+      xml_element* elem_val = xml_elem_new();
+
+      /* special case for when root element is not an array */
+      if (depth == 0 && 
+          !(type == xmlrpc_vector && 
+            vtype == xmlrpc_vector_array && 
+            request_type == xmlrpc_request_call) ) {
+         int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
+
+         xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
+         if (next_el) {
+            Q_PushTail(&elem_val->children, next_el);
+         }
+         elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
+      } else {
+         switch (type) {
+         case xmlrpc_string:
+            elem_val->name = strdup(ELEM_STRING);
+            simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
+            break;
+         case xmlrpc_int:
+            elem_val->name = strdup(ELEM_INT);
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_boolean:
+            elem_val->name = strdup(ELEM_BOOLEAN);
+            snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_double:
+            elem_val->name = strdup(ELEM_DOUBLE);
+            snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
+            simplestring_add(&elem_val->text, buf);
+            break;
+         case xmlrpc_datetime:
+            elem_val->name = strdup(ELEM_DATETIME);
+            simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
+            break;
+         case xmlrpc_base64:
+            {
+               struct buffer_st buf;
+               elem_val->name = strdup(ELEM_BASE64);
+               base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
+               simplestring_addn(&elem_val->text, buf.data, buf.offset );
+               buffer_delete(&buf);
+            }
+            break;
+         case xmlrpc_vector:
+            {
+               XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
+               XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
+               xml_element* root_vector_elem = elem_val;
+
+               switch (my_type) {
+               case xmlrpc_vector_array:
+                  {
+                      if(depth == 0) {
+                         elem_val->name = strdup(ELEM_PARAMS);
+                      }
+                      else {
+                         /* Hi my name is Dave and I like to make things as confusing
+                          * as possible, thus I will throw in this 'data' element
+                          * where it absolutely does not belong just so that people
+                          * cannot code arrays and structs in a similar and straight
+                          * forward manner. Have a good day.
+                          *
+                          * GRRRRRRRRR!
+                          */
+                         xml_element* data = xml_elem_new();
+                         data->name = strdup(ELEM_DATA);
+    
+                         elem_val->name = strdup(ELEM_ARRAY);
+                         Q_PushTail(&elem_val->children, data);
+                         root_vector_elem = data;
+                      }
+                  }
+                  break;
+               case xmlrpc_vector_mixed:       /* not officially supported */
+               case xmlrpc_vector_struct:
+                  elem_val->name = strdup(ELEM_STRUCT);
+                  break;
+               default:
+                  break;
+               }
+
+               /* recurse through sub-elements */
+               while ( xIter ) {
+                  xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
+                  if (next_el) {
+                     Q_PushTail(&root_vector_elem->children, next_el);
+                  }
+                  xIter = XMLRPC_VectorNext(node);
+               }
+            }
+            break;
+         default:
+            break;
+         }
+      }
+
+      {
+         XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
+
+         if (depth == 1) {
+            xml_element* value = xml_elem_new();
+            value->name = strdup(ELEM_VALUE);
+
+            /* yet another hack for the "fault" crap */
+            if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
+               root = value;
+            } else {
+               xml_element* param = xml_elem_new();
+               param->name = strdup(ELEM_PARAM);
+
+               Q_PushTail(&param->children, value);
+
+               root = param;
+            }
+            Q_PushTail(&value->children, elem_val);
+         } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
+            xml_element* member = xml_elem_new();
+            xml_element* name = xml_elem_new();
+            xml_element* value = xml_elem_new();
+
+            member->name = strdup(ELEM_MEMBER);
+            name->name = strdup(ELEM_NAME);
+            value->name = strdup(ELEM_VALUE);
+
+            simplestring_add(&name->text, XMLRPC_GetValueID(node));
+
+            Q_PushTail(&member->children, name);
+            Q_PushTail(&member->children, value);
+            Q_PushTail(&value->children, elem_val);
+
+            root = member;
+         } else if (vtype == xmlrpc_vector_array) {
+            xml_element* value = xml_elem_new();
+
+            value->name = strdup(ELEM_VALUE);
+
+            Q_PushTail(&value->children, elem_val);
+
+            root = value;
+         } else if (vtype == xmlrpc_vector_none) {
+            /* no parent.  non-op */
+            root = elem_val;
+         } else {
+            xml_element* value = xml_elem_new();
+
+            value->name = strdup(ELEM_VALUE);
+
+            Q_PushTail(&value->children, elem_val);
+
+            root = value;
+         }
+      }
+   }
+   return root;
+}
+
+xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
+   return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
+}
+
+xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
+   xml_element* wrapper = NULL;
+   if (request) {
+      const char* pStr = NULL;
+      XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
+      XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
+
+      wrapper = xml_elem_new();
+
+      if (request_type == xmlrpc_request_call) {
+         pStr = ELEM_METHODCALL;
+      } else if (request_type == xmlrpc_request_response) {
+         pStr = ELEM_METHODRESPONSE;
+      }
+      if (pStr) {
+         wrapper->name = strdup(pStr);
+      }
+
+      pStr = XMLRPC_RequestGetMethodName(request);
+
+      if (pStr) {
+         xml_element* method = xml_elem_new();
+         method->name = strdup(ELEM_METHODNAME);
+         simplestring_add(&method->text, pStr);
+         Q_PushTail(&wrapper->children, method);
+      }
+      if (xParams) {
+         Q_PushTail(&wrapper->children, 
+                    XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
+      } else {
+         /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
+         xml_element* params = xml_elem_new();
+         params->name = strdup(ELEM_PARAMS);
+         Q_PushTail(&wrapper->children, params);
+      }
+   }
+   return wrapper;
+}
+
diff --git a/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h
new file mode 100644 (file)
index 0000000..234a153
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+#ifndef XML_TO_XMLRPC_H
+ #define XML_TO_XMLRPC_H
+
+#include "time.h" 
+#include "xmlrpc.h"
+
+XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el);
+XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el);
+xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node);
+xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request);
+
+#endif /* XML_TO_XMLRPC_H */
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.c b/ext/xmlrpc/libxmlrpc/xmlrpc.c
new file mode 100644 (file)
index 0000000..f95ac94
--- /dev/null
@@ -0,0 +1,2471 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+static const char rcsid[] = "#(@) $Id$";
+
+
+/****h* ABOUT/xmlrpc
+ * NAME
+ *   XMLRPC_VALUE
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * CREATION DATE
+ *   9/1999 - 10/2000
+ * HISTORY
+ *   09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only.
+ *   06/2000 -- danda -- played with expat-ensor from www.ensor.org.  Cool, but some flaws.
+ *   07/2000 -- danda -- wrote new implementation to be compatible with xmlrpc standard and
+ *                       incorporated some ideas from ensor, most notably the separation of
+ *                       xml dom from xmlrpc api.
+ *   08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc
+ *   08/2000 -- danda -- PHP C extension that uses XMLRPC                     
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ * TODO
+ *   Server method introspection. (Enumerate available methods, describe I/O)
+ * PORTABILITY
+ *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
+ *   about anything with minor mods.
+ * NOTES
+ *   Welcome to XMLRPC.  For more info on the specification and history, see
+ *   http://www.xmlrpc.org.
+ *
+ *   This code aims to be a full-featured C implementation of XMLRPC.  It does not
+ *   have any networking code.  Rather, it is intended to be plugged into apps
+ *   or libraries with existing networking facilities, eg PHP, apache, perl, mozilla, 
+ *   home-brew application servers, etc.
+ *
+ *   Usage Paradigm:
+ *     The user of this library will typically be implementing either an XMLRPC server,
+ *     an XMLRPC client, or both.  The client will use the library to build an in-memory
+ *     representation of a request, and then serialize (encode) that request into XML. The
+ *     client will then send the XML to the server via external mechanism.  The server will
+ *     de-serialize the XML back into an binary representation, call the appropriate registered
+ *     method -- thereby generating a response.  The response will be serialized into XML and
+ *     sent back to the client.  The client will de-serialize it into memory, and can
+ *     iterate through the results via API.
+ *
+ *     Both the request and the response may consist of arbitrarily long, arbitrarily nested
+ *     values.  The values may be one of several types, as defined by XMLRPC_VALUE_TYPE.
+ *
+ *   Features and Architecture:
+ *     - The XML parsing (xml_element.c) is completely independent of the XMLRPC api. In fact,
+ *       it can be used as a standalone dom implementation.
+ *     - Because of this, the same XMLRPC data can be serialized into multiple xml vocabularies.
+ *       It is simply a matter of writing a transport.  So far, two transports have been defined.
+ *       The default xmlrpc vocab (xml_to_xmlrpc.c), and simple-rpc (xml_to_dandarpc.c) which is 
+ *       proprietary, but imho more readable, and nice for proprietary legacy reasons.
+ *     - Various output options, including: xml escaping via CDATA or entity, case folding,
+ *       vocab version, and character encoding.
+ *     - One to One mapping between C structures and actual values, unlike ensor which forces
+ *       one to understand the arcana of the xmlrpc vocab.
+ *     - support for mixed indexed/keyed vector types, making it more compatible with 
+ *       languages such as PHP.
+ *     - quite speedy compared to implementations written in interpreted languages. Also, uses
+ *       intelligent string handling, so not many strlen() calls, etc.
+ *     - comprehensive API for manipulation of values
+ *******/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "queue.h"
+#include "xmlrpc.h"
+#include "expat.h"
+#include "base64.h"
+
+#include "xml_to_xmlrpc.h"
+#include "xml_to_dandarpc.h"
+#include "xml_element.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+
+
+
+/*-*********************
+* Begin Time Functions *
+***********************/
+
+static int date_from_ISO8601(const char *text, time_t *value)
+{
+   struct tm tm;
+   int n;
+   int i;
+   time_t t;
+
+   tm.tm_isdst = -1;
+
+   if(strlen(text) < 17) {
+      return -1;
+   }
+
+   n = 1000;
+   tm.tm_year = 0;
+   for(i = 0; i < 4; i++) {
+      tm.tm_year += (text[i]-'0')*n;
+      n /= 10;
+   }
+   n = 10;
+   tm.tm_mon = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_mon += (text[i+4]-'0')*n;
+      n /= 10;
+   }
+   tm.tm_mon --;
+
+   n = 10;
+   tm.tm_mday = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_mday += (text[i+6]-'0')*n;
+      n /= 10;
+   }
+
+   n = 10;
+   tm.tm_hour = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_hour += (text[i+9]-'0')*n;
+      n /= 10;
+   }
+
+   n = 10;
+   tm.tm_min = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_min += (text[i+12]-'0')*n;
+      n /= 10;
+   }
+
+   n = 10;
+   tm.tm_sec = 0;
+   for(i = 0; i < 2; i++) {
+      tm.tm_sec += (text[i+15]-'0')*n;
+      n /= 10;
+   }
+
+   tm.tm_year -= 1900;
+
+   *value = mktime(&tm);
+
+   return 0;
+
+}
+
+static int date_to_ISO8601(time_t value, char *buf, int length)
+{
+   struct tm *tm;
+   tm = localtime(&value);
+
+   return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm);
+}
+
+/*-*******************
+* End Time Functions *
+*********************/
+
+
+/*-***************************
+* Begin XMLRPC_REQUEST funcs *
+*****************************/
+
+/****f* REQUEST/XMLRPC_RequestNew
+ * NAME
+ *   XMLRPC_RequestNew
+ * SYNOPSIS
+ *   XMLRPC_REQUEST XMLRPC_RequestNew()
+ * FUNCTION
+ *   Creates a new XMLRPC_Request data struct
+ * INPUTS
+ *   none
+ * SEE ALSO
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+XMLRPC_REQUEST XMLRPC_RequestNew() {
+   XMLRPC_REQUEST xRequest = calloc(1, sizeof(STRUCT_XMLRPC_REQUEST));
+   if(xRequest) {
+      simplestring_init(&xRequest->methodName);
+   }
+   return xRequest;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestFree
+ * NAME
+ *   XMLRPC_RequestFree
+ * SYNOPSIS
+ *   void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO)
+ * FUNCTION
+ *   Free XMLRPC Request and all sub-values
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   bFreeIO -- 1 = also free request value data, if any, 0 = ignore.
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) {
+   if(request) {
+      simplestring_free(&request->methodName);
+
+      if(request->io && bFreeIO) {
+         XMLRPC_CleanupValue(request->io);
+      }
+      if(request->error) {
+         XMLRPC_CleanupValue(request->error);
+      }
+      my_free(request);
+   }
+}
+/*******/
+
+/* Set Method Name to call */
+/****f* REQUEST/XMLRPC_RequestSetMethodName
+ * NAME
+ *   XMLRPC_RequestSetMethodName
+ * SYNOPSIS
+ *   const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName)
+ * FUNCTION
+ *   Set name of method to call with this request.
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   methodName -- name of method
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetMethodName ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) {
+   if(request) {
+      simplestring_clear(&request->methodName);
+      simplestring_add(&request->methodName, methodName);
+      return request->methodName.str;
+   }
+   return NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetMethodName
+ * NAME
+ *   XMLRPC_RequestGetMethodName
+ * SYNOPSIS
+ *   const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Get name of method called by this request
+ * INPUTS
+ *   request -- previously allocated request struct
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetMethodName ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) {
+   return request ? request->methodName.str : NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestSetRequestType
+ * NAME
+ *   XMLRPC_RequestSetRequestType
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type)
+ * FUNCTION
+ *   A request struct may be allocated by a caller or by xmlrpc
+ *   in response to a request.  This allows setting the
+ *   request type.
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   type    -- request type [xmlrpc_method_call | xmlrpc_method_response]
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetRequestType ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) {
+   if(request) {
+      request->request_type = type;
+      return request->request_type;
+   }
+   return xmlrpc_request_none;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetRequestType
+ * NAME
+ *   XMLRPC_RequestGetRequestType
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   A request struct may be allocated by a caller or by xmlrpc
+ *   in response to a request.  This allows setting the
+ *   request type.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   type    -- request type [xmlrpc_method_call | xmlrpc_method_response]
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetRequestType ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) {
+   return request ? request->request_type : xmlrpc_request_none;
+}
+/*******/
+
+
+/****f* REQUEST/XMLRPC_RequestSetData
+ * NAME
+ *   XMLRPC_RequestSetData
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data)
+ * FUNCTION
+ *   Associates a block of xmlrpc data with the request.  The
+ *   data is *not* copied.  A pointer is kept.  The caller
+ *   should be careful not to doubly free the data value,
+ *   which may optionally be free'd by XMLRPC_RequestFree().
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   data    -- previously allocated data struct
+ * RESULT
+ *   XMLRPC_VALUE -- pointer to value stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetData ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) {
+   if(request && data) {
+      request->io = XMLRPC_CopyValue(data);
+      return request->io;
+   }
+   return NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetData
+ * NAME
+ *   XMLRPC_RequestGetData
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Returns data associated with request, if any.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   XMLRPC_VALUE -- pointer to value stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetData ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) {
+   return request ? request->io : NULL;
+}
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestSetOutputOptions
+ * NAME
+ *   XMLRPC_RequestSetOutputOptions
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output)
+ * FUNCTION
+ *   Sets output options used for generating XML. The output struct
+ *   is copied, and may be freed by the caller.
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   output  -- output options struct initialized by caller
+ * RESULT
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to value stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestGetOutputOptions ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * SOURCE
+ */
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) {
+   if(request && output) {
+      memcpy(&request->output, output, sizeof(STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS));
+      return &request->output;
+   }
+   return NULL;
+}
+/*******/
+
+
+/****f* REQUEST/XMLRPC_RequestGetOutputOptions
+ * NAME
+ *   XMLRPC_RequestGetOutputOptions
+ * SYNOPSIS
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Gets a pointer to output options used for generating XML.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to options stored, or NULL
+ * SEE ALSO
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestSetOutputOptions ()
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * SOURCE
+ */
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) {
+   return request ? &request->output : NULL;
+}
+/*******/
+
+/*-*************************
+* End XMLRPC_REQUEST funcs *
+***************************/
+
+
+/*-***************************
+* Begin Serializiation funcs *
+*****************************/
+
+/****f* SERIALIZE/XMLRPC_VALUE_ToXML
+ * NAME
+ *   XMLRPC_VALUE_ToXML
+ * SYNOPSIS
+ *   char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val)
+ * FUNCTION
+ *   encode XMLRPC_VALUE into XML buffer.  Note that the generated
+ *   buffer will not contain a methodCall.
+ * INPUTS
+ *   val -- previously allocated XMLRPC_VALUE
+ *   buf_len -- length of returned buffer, if not null
+ * RESULT
+ *   char* -- newly allocated buffer containing XML. 
+ *   It is the caller's responsibility to free it.
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ToXML ()
+ *   XMLRPC_VALUE_FromXML ()
+ *   XMLRPC_Free ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) {
+   xml_element *root_elem = XMLRPC_VALUE_to_xml_element(val);
+   char* pRet = NULL;
+
+   if(root_elem) {
+      pRet = xml_elem_serialize_to_string(root_elem, NULL, buf_len);
+      xml_elem_free(root_elem);
+   }
+   return pRet;
+}
+/*******/
+
+/****f* SERIALIZE/XMLRPC_REQUEST_ToXML
+ * NAME
+ *   XMLRPC_REQUEST_ToXML
+ * SYNOPSIS
+ *   char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   encode XMLRPC_REQUEST into XML buffer
+ * INPUTS
+ *   request -- previously allocated XMLRPC_REQUEST
+ *   buf_len -- size of returned buf, if not null
+ * RESULT
+ *   char* -- newly allocated buffer containing XML. 
+ *   It is the caller's responsibility to free it.
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ToXML ()
+ *   XMLRPC_REQUEST_FromXML ()
+ *   XMLRPC_Free ()
+ *   XMLRPC_VALUE_ToXML ()
+ *   XMLRPC_REQUEST
+ * SOURCE
+ */
+char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) {
+   if(request) {
+      xml_element *root_elem = (request->output.version == xmlrpc_version_simple) ? 
+                                        DANDARPC_REQUEST_to_xml_element(request) :
+                                        XMLRPC_REQUEST_to_xml_element(request);
+      char* pRet = NULL;
+
+      if(root_elem) {
+         pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len);
+         xml_elem_free(root_elem);
+      }
+      return pRet;
+   }
+}
+/*******/
+
+/****f* SERIALIZE/XMLRPC_VALUE_FromXML
+ * NAME
+ *   XMLRPC_VALUE_FromXML
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int le
+ * FUNCTION
+ *   Retrieve XMLRPC_VALUE from XML buffer. Note that this will
+ *   ignore any methodCall.  See XMLRPC_REQUEST_FromXML
+ * INPUTS
+ *   in_buf -- character buffer containing XML
+ *   len    -- length of buffer
+ * RESULT
+ *   XMLRPC_VALUE -- newly allocated data, or NULL if error. Should
+ *   be free'd by caller.
+ * SEE ALSO
+ *   XMLRPC_VALUE_ToXML ()
+ *   XMLRPC_REQUEST_FromXML ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options)
+{
+   XMLRPC_VALUE xResponse = NULL;
+   XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options);
+
+   if(req) {
+      xResponse = req->io;
+      XMLRPC_RequestFree(req, 0);
+   }
+   return xResponse;
+}
+/*******/
+
+/* map parser errors to standard xml-rpc errors */
+static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(error) {
+      XMLRPC_ERROR_CODE code;
+      char buf[1024];
+      snprintf(buf, sizeof(buf), 
+               "error occurred at line %i, column %i, byte index %i", 
+               error->line, error->column,
+               error->byte_index);
+
+      /* expat specific errors */
+      switch(error->parser_code) {
+      case XML_ERROR_UNKNOWN_ENCODING:
+         code = xmlrpc_error_parse_unknown_encoding;
+         break;
+      case XML_ERROR_INCORRECT_ENCODING:
+         code = xmlrpc_error_parse_bad_encoding;
+         break;
+      default:
+         code = xmlrpc_error_parse_xml_syntax;
+         break;
+      }
+      xReturn = XMLRPC_UtilityCreateFault(code, buf);
+   }
+   return xReturn;
+}
+
+/****f* SERIALIZE/XMLRPC_REQUEST_FromXML
+ * NAME
+ *   XMLRPC_REQUEST_FromXML
+ * SYNOPSIS
+ *   XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int le
+ * FUNCTION
+ *   Retrieve XMLRPC_REQUEST from XML buffer
+ * INPUTS
+ *   in_buf -- character buffer containing XML
+ *   len    -- length of buffer
+ * RESULT
+ *   XMLRPC_REQUEST -- newly allocated data, or NULL if error. Should
+ *   be free'd by caller.
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ToXML ()
+ *   XMLRPC_VALUE_FromXML ()
+ *   XMLRPC_REQUEST
+ * SOURCE
+ */
+XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options)
+{
+   XMLRPC_REQUEST request = XMLRPC_RequestNew();
+   STRUCT_XML_ELEM_ERROR error = {0};
+
+   if(request) {
+      xml_element *root_elem = xml_elem_parse_buf(in_buf, len, (in_options ? &in_options->xml_elem_opts : NULL), &error);
+
+      if(root_elem) {
+         if(!strcmp(root_elem->name, "simpleRPC")) {
+            request->output.version = xmlrpc_version_simple;
+            xml_element_to_DANDARPC_REQUEST(request, root_elem);
+         }
+         else {
+            request->output.version = xmlrpc_version_1_0;
+            xml_element_to_XMLRPC_REQUEST(request, root_elem);
+         }
+         xml_elem_free(root_elem);
+      }
+      else {
+         if(error.parser_error) {
+            request->error = map_expat_errors(&error);
+         }
+      }
+   }
+
+   return request;
+}
+/*******/
+
+/*-************************
+* End Serialization Funcs *
+**************************/
+
+
+
+/****f* VALUE/XMLRPC_CreateValueEmpty
+ * NAME
+ *   XMLRPC_CreateValueEmpty
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueEmpty ()
+ * FUNCTION
+ *   Create an XML value to be used/modified elsewhere.
+ * INPUTS
+ * RESULT
+ *   XMLRPC_VALUE.  The new value, or NULL on failure.
+ * SEE ALSO
+ *   XMLRPC_CleanupValue ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueEmpty() {
+   XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE));
+   if(v) {
+      v->type = xmlrpc_empty;
+      simplestring_init(&v->id);
+      simplestring_init(&v->str);
+   }
+   return v;
+}
+
+static const char* get_string(const char* buf, int bDup) {
+   if(bDup) {
+      return strdup(buf);
+   }
+   return buf;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueID_Case
+ * NAME
+ *   XMLRPC_SetValueID_Case
+ * SYNOPSIS
+ *   const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case)
+ * FUNCTION
+ *   Assign an ID (key) to an XMLRPC value.
+ * INPUTS
+ *   value     The xml value who's ID we will set.
+ *   id        The desired new id.
+ *   len       length of id string if known, or 0 if unknown.
+ *   id_case   one of XMLRPC_CASE
+ * RESULT
+ *   const char*  pointer to the newly allocated id string, or NULL
+ * SEE ALSO
+ *   XMLRPC_SetValueID ()
+ *   XMLRPC_GetValueID ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_CASE
+ * SOURCE
+ */
+const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) {
+   const char* pRetval = NULL;
+   if(value) {
+      if(id) {
+         simplestring_clear(&value->id);
+         (len > 0) ? simplestring_addn(&value->id, id, len) :
+                     simplestring_add(&value->id, id);
+
+         /* upper or lower case string in place if required. could be a seperate func. */
+         if(id_case == xmlrpc_case_lower || id_case == xmlrpc_case_upper) {
+            int i;
+            for(i = 0; i < value->id.len; i++) {
+               value->id.str[i] = (id_case == xmlrpc_case_lower) ? tolower(value->id.str[i]) : toupper(value->id.str[i]);
+            }
+         }
+
+         pRetval = value->id.str;
+
+#ifdef XMLRPC_DEBUG_REFCOUNT
+         printf("set value id: %s\n", pRetval);
+#endif 
+      }
+   }
+
+   return pRetval;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueString
+ * NAME
+ *   XMLRPC_SetValueString
+ * SYNOPSIS
+ *   const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len)
+ * FUNCTION
+ *   Assign a string value to an XMLRPC_VALUE, and set it to type xmlrpc_string
+ * INPUTS
+ *   value     The xml value who's ID we will set.
+ *   val        The desired new string val.
+ *   len       length of val string if known, or 0 if unknown.
+ * RESULT
+ *   const char*  pointer to the newly allocated value string, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueString ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) {
+   char *pRetval = NULL;
+   if(value && val) {
+      simplestring_clear(&value->str);
+      (len > 0) ? simplestring_addn(&value->str, val, len) :
+                  simplestring_add(&value->str, val);
+      value->type = xmlrpc_string;
+      pRetval = (char *)value->str.str;
+   }
+
+   return pRetval;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueInt
+ * NAME
+ *   XMLRPC_SetValueInt
+ * SYNOPSIS
+ *   void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val)
+ * FUNCTION
+ *   Assign an int value to an XMLRPC_VALUE, and set it to type xmlrpc_int
+ * INPUTS
+ *   value     The xml value who's ID we will set.
+ *   val        The desired new integer value
+ * RESULT
+ * SEE ALSO
+ *   XMLRPC_GetValueInt ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) {
+   if(value) {
+      value->type = xmlrpc_int;
+      value->i = val;
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueBoolean
+ * NAME
+ *   XMLRPC_SetValueBoolean
+ * SYNOPSIS
+ *   void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val)
+ * FUNCTION
+ *   Assign a boolean value to an XMLRPC_VALUE, and set it to type xmlrpc_boolean
+ * INPUTS
+ *   value     The xml value who's value we will set.
+ *   val        The desired new boolean value. [0 | 1]
+ * RESULT
+ * SEE ALSO
+ *   XMLRPC_GetValueBoolean ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) {
+   if(value) {
+      value->type = xmlrpc_boolean;
+      value->i = val ? 1 : 0;
+   }
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_SetIsVector
+ * NAME
+ *   XMLRPC_SetIsVector
+ * SYNOPSIS
+ *   int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type)
+ * FUNCTION
+ *   Set the XMLRPC_VALUE to be a vector (list) type.  The vector may be one of
+ *   [xmlrpc_array | xmlrpc_struct | xmlrpc_mixed].  An array has only index values.
+ *   A struct has key/val pairs.  Mixed allows both index and key/val combinations. 
+ * INPUTS
+ *   value     The xml value who's vector type we will set
+ *   type      New type of vector as enumerated by XMLRPC_VECTOR_TYPE
+ * RESULT
+ *   int       1 if successful, 0 otherwise
+ * SEE ALSO
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_GetVectorType ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VECTOR_TYPE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) {
+   int bSuccess = 0;
+
+   if(value && value->type != xmlrpc_vector) {
+      value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR));
+      if(value->v) {
+         value->v->q = (queue*)malloc(sizeof(queue));
+         if(value->v->q) {
+            Q_Init(value->v->q);
+            value->v->type = type;
+            value->type = xmlrpc_vector;
+            bSuccess = 1;
+         }
+      }
+   }
+
+   return bSuccess;
+}
+/*******/
+
+/****f* VECTOR/XMLRPC_CreateVector
+ * NAME
+ *   XMLRPC_CreateVector
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type)
+ * FUNCTION
+ *   Create a new vector and optionally set an id.
+ * INPUTS
+ *   id        The id of the vector, or NULL
+ *   type      New type of vector as enumerated by XMLRPC_VECTOR_TYPE
+ * RESULT
+ *   XMLRPC_VALUE  The new vector, or NULL on failure.
+ * SEE ALSO
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_SetIsVector ()
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_GetVectorType ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VECTOR_TYPE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) {
+   XMLRPC_VALUE val = NULL;
+
+   val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_VECTOR *pSIV = NULL;
+
+      if(XMLRPC_SetIsVector(val, type)) {
+         if(id) {
+            const char *pSVI = NULL;
+
+            pSVI = XMLRPC_SetValueID(val, id, 0);
+            if(NULL == pSVI) {
+               val = NULL;
+            }
+         }
+      }
+      else {
+         val = NULL;
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/* Not yet implemented.
+ *
+ * This should use a hash to determine if a given target id has already
+ * been appended.  
+ *
+ * Alternately, it could walk the entire vector, but that could be quite
+ * slow for very large lists.
+ */
+static int isDuplicateEntry(XMLRPC_VALUE target, XMLRPC_VALUE source) {
+   return 0;
+}
+
+/****f* VECTOR/XMLRPC_AddValueToVector
+ * NAME
+ *   XMLRPC_AddValueToVector
+ * SYNOPSIS
+ *   int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source)
+ * FUNCTION
+ *   Add (append) an existing XMLRPC_VALUE to a vector.
+ * INPUTS
+ *   target    The target vector
+ *   source    The source value to append
+ * RESULT
+ *   int       1 if successful, else 0
+ * SEE ALSO
+ *   XMLRPC_AddValuesToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * NOTES
+ *   The function will fail and return 0 if an attempt is made to add
+ *   a value with an ID into a vector of type xmlrpc_vector_array. Such
+ *   values can only be added to xmlrpc_vector_struct.
+ * SOURCE
+ */
+int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) {
+   if(target && source) {
+      if(target->type == xmlrpc_vector && target->v && 
+         target->v->q && target->v->type != xmlrpc_vector_none) {
+
+         /* guard against putting value of unknown type into vector */
+         switch(source->type) {
+            case xmlrpc_empty:
+            case xmlrpc_base64:
+            case xmlrpc_boolean:
+            case xmlrpc_datetime:
+            case xmlrpc_double:
+            case xmlrpc_int:
+            case xmlrpc_string:
+            case xmlrpc_vector:
+               /* Guard against putting a key/val pair into an array vector */
+               if( !(source->id.len && target->v->type == xmlrpc_vector_array) ) {
+                  if(isDuplicateEntry(target, source) || Q_PushTail(target->v->q, XMLRPC_CopyValue(source))) {
+                     return 1;
+                  }
+               }
+               else {
+                  fprintf(stderr, "xmlrpc: attempted to add key/val pair to vector of type array\n");
+               }
+               break;
+            default:
+               fprintf(stderr, "xmlrpc: attempted to add value of unknown type to vector\n");
+               break;
+         }
+      }
+   }
+   return 0;
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_AddValuesToVector
+ * NAME
+ *   XMLRPC_AddValuesToVector
+ * SYNOPSIS
+ *   XMLRPC_AddValuesToVector ( target, val1, val2, val3, val(n), 0 )
+ *   XMLRPC_AddValuesToVector( XMLRPC_VALUE, ... )
+ * FUNCTION
+ *   Add (append) a series of existing XMLRPC_VALUE to a vector.
+ * INPUTS
+ *   target    The target vector
+ *   ...       The source value(s) to append.  The last item *must* be 0.
+ * RESULT
+ *   int       1 if successful, else 0
+ * SEE ALSO
+ *   XMLRPC_AddValuesToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * NOTES
+ *   This function may actually return failure after it has already modified
+ *     or added items to target.  You can not trust the state of target
+ *     if this function returns failure.
+ * SOURCE
+ */
+int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) {
+   int iRetval = 0;
+
+   if(target) {
+      if(target->type == xmlrpc_vector) {
+         XMLRPC_VALUE v = NULL;
+         va_list vl;
+
+         va_start(vl, target);
+
+         do {
+            v = va_arg(vl, XMLRPC_VALUE);
+            if(v) {
+               if(!XMLRPC_AddValueToVector(target, v)) {
+                  iRetval = 0;
+                  break;
+               }
+            }
+         } while(v);
+
+         va_end(vl);
+
+         if(NULL == v) {
+            iRetval = 1;
+         }
+      }
+   }
+   return iRetval;
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_VectorGetValueWithID_Case
+ * NAME
+ *   XMLRPC_VectorGetValueWithID_Case
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case)
+ * FUNCTION
+ *   Get value from vector matching id (key)
+ * INPUTS
+ *   vector    The source vector
+ *   id        The key to find
+ *   id_case   Rule for how to match key
+ * RESULT
+ *   int       1 if successful, else 0
+ * SEE ALSO
+ *   XMLRPC_SetValueID_Case ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_CASE_COMPARISON
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) {
+   if(vector && vector->v && vector->v->q) {
+       q_iter qi = Q_Iter_Head_F(vector->v->q);
+
+       while(qi) {
+          XMLRPC_VALUE xIter = Q_Iter_Get_F(qi);
+          if(xIter && xIter->id.str) {
+             if(id_case == xmlrpc_case_sensitive) {
+                if(!strcmp(xIter->id.str, id)) {
+                   return xIter;
+                }
+             }
+             else if(id_case == xmlrpc_case_insensitive) {
+                if(!strcasecmp(xIter->id.str, id)) {
+                   return xIter;
+                }
+             }
+          }
+          qi = Q_Iter_Next_F(qi);
+       }
+   }
+   return NULL;
+}
+/*******/
+
+
+int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value) {
+   if(vector && vector->v && vector->v->q && value) {
+       q_iter qi = Q_Iter_Head_F(vector->v->q);
+
+       while(qi) {
+          XMLRPC_VALUE xIter = Q_Iter_Get_F(qi);
+          if(xIter == value) {
+             XMLRPC_CleanupValue(xIter);
+             Q_Iter_Del(vector->v->q, qi);
+             return 1;
+          }
+          qi = Q_Iter_Next_F(qi);
+       }
+   }
+   return 0;
+}
+
+
+/****f* VALUE/XMLRPC_CreateValueString
+ * NAME
+ *   XMLRPC_CreateValueString
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len)
+ * FUNCTION
+ *   Create an XMLRPC_VALUE, and assign a string to it
+ * INPUTS
+ *   id        The id of the value, or NULL
+ *   val       The desired new string val.
+ *   len       length of val string if known, or 0 if unknown.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueString ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) {
+   XMLRPC_VALUE value = NULL;
+   if(val) {
+      value = XMLRPC_CreateValueEmpty();
+      if(value) {
+         XMLRPC_SetValueString(value, val, len);
+         if(id) {
+            XMLRPC_SetValueID(value, id, 0);
+         }
+      }
+   }
+   return value;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueInt
+ * NAME
+ *   XMLRPC_CreateValueInt
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i)
+ * FUNCTION
+ *   Create an XMLRPC_VALUE, and assign an int to it
+ * INPUTS
+ *   id        The id of the value, or NULL
+ *   i         The desired new int val.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueInt ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueInt(val, i);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueBoolean
+ * NAME
+ *   XMLRPC_CreateValueBoolean
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i)
+ * FUNCTION
+ *   Create an XMLRPC_VALUE, and assign an int to it
+ * INPUTS
+ *   id        The id of the value, or NULL
+ *   i         The desired new int val.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL
+ * SEE ALSO
+ *   XMLRPC_GetValueBoolean ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_VALUE
+ *   XMLRPC_VALUE_TYPE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueBoolean(val, i);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_CleanupValue
+ * NAME
+ *   XMLRPC_CleanupValue
+ * SYNOPSIS
+ *   void XMLRPC_CleanupValue(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Frees all memory allocated for an XMLRPC_VALUE and any of its children (if a vector)
+ * INPUTS
+ *   value     The id of the value to be cleaned up.
+ * RESULT
+ *   void
+ * NOTES
+ *   Normally this function will be called for the topmost vector, thus free-ing
+ *   all children.  If a child of a vector is free'd first, results are undefined.
+ *   Failure to call this function *will* cause memory leaks.
+ *
+ *   Also, this function is implemented using reference counting.  Thus a value
+ *   may be added and freed from multiple parents so long as a reference is added
+ *   first using XMLRPC_CopyValue()
+ * SEE ALSO
+ *   XMLRPC_RequestFree ()
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_CopyValue()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_CleanupValue(XMLRPC_VALUE value) {
+   if(value) {
+      if(value->iRefCount > 0) {
+         value->iRefCount --;
+      }
+
+#ifdef XMLRPC_DEBUG_REFCOUNT
+      if(value->id.str) {
+         printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount);
+      }
+      else {
+         printf("decremented refcount of 0x%x, now %i\n", value, value->iRefCount);
+      }
+#endif
+
+      if(value->type == xmlrpc_vector) {
+         if(value->v) {
+            if(value->iRefCount == 0) {
+               XMLRPC_VALUE cur = (XMLRPC_VALUE)Q_Head(value->v->q);
+               while( cur ) {
+                  XMLRPC_CleanupValue(cur);
+   
+                  /* Make sure some idiot didn't include a vector as a child of itself
+                   * and thus it would have already free'd these.
+                   */
+                  if(value->v && value->v->q) {
+                     cur = Q_Next(value->v->q);
+                  }
+                  else {
+                     break;
+                  }
+               }
+
+               Q_Destroy(value->v->q);
+               my_free(value->v->q);
+               my_free(value->v);
+            }
+         }
+      }
+
+
+      if(value->iRefCount == 0) {
+
+         /* guard against freeing invalid types */
+         switch(value->type) {
+            case xmlrpc_empty:
+            case xmlrpc_base64:
+            case xmlrpc_boolean:
+            case xmlrpc_datetime:
+            case xmlrpc_double:
+            case xmlrpc_int:
+            case xmlrpc_string:
+            case xmlrpc_vector:
+#ifdef XMLRPC_DEBUG_REFCOUNT
+               if(value->id.str) {
+                  printf("free'd %s\n", value->id.str);
+               }
+               else {
+                  printf("free'd 0x%x\n", value);
+               }
+#endif 
+               simplestring_free(&value->id);
+               simplestring_free(&value->str);
+
+               memset(value, 0, sizeof(STRUCT_XMLRPC_VALUE));
+               my_free(value);
+               break;
+            default:
+               fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n");
+               break;
+         }
+      }
+   }
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueDateTime
+ * NAME
+ *   XMLRPC_SetValueDateTime
+ * SYNOPSIS
+ *   void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time)
+ * FUNCTION
+ *   Assign time value to XMLRPC_VALUE
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   time      The desired new unix time value (time_t)
+ * RESULT
+ *   void
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime ()
+ *   XMLRPC_SetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) {
+   if(value) {
+      char timeBuf[30];
+      value->type = xmlrpc_datetime;
+      value->i = time;
+
+      timeBuf[0] = 0;
+
+      date_to_ISO8601(time, timeBuf, sizeof(timeBuf));
+
+      if(timeBuf[0]) {
+         simplestring_clear(&value->str);
+         simplestring_add(&value->str, timeBuf);
+      }
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CopyValue
+ * NAME
+ *   XMLRPC_CopyValue
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Make a copy of an XMLRPC_VALUE
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ * RESULT
+ *   XMLRPC_VALUE -- address of the copy
+ * SEE ALSO
+ *   XMLRPC_CleanupValue ()
+ * NOTES
+ *   This function is implemented via reference counting, so the
+ *   returned value is going to be the same as the passed in value.
+ *   The value must be freed the same number of times it is copied
+ *   or there will be a memory leak.
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) {
+   if(value) {
+      value->iRefCount ++;
+#ifdef XMLRPC_DEBUG_REFCOUNT
+      if(value->id.str) {
+         printf("incremented refcount of %s\n", value->id.str);
+      }
+#endif
+   }
+   return value;
+}
+/*******/
+
+
+
+/****f* VALUE/XMLRPC_CreateValueDateTime
+ * NAME
+ *   XMLRPC_CreateValueDateTime
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time)
+ * FUNCTION
+ *   Create new datetime value from time_t
+ * INPUTS
+ *   id        id of the new value, or NULL
+ *   time      The desired unix time value (time_t)
+ * RESULT
+ *   void
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime ()
+ *   XMLRPC_SetValueDateTime ()
+ *   XMLRPC_CreateValueDateTime_ISO8601 ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueDateTime(val, time);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueDateTime_ISO8601
+ * NAME
+ *   XMLRPC_SetValueDateTime_ISO8601
+ * SYNOPSIS
+ *   void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s)
+ * FUNCTION
+ *   Set datetime value from IS08601 encoded string
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   s         The desired new time value
+ * RESULT
+ *   void                                
+ * BUGS
+ *   This function currently attempts to convert the time string to a valid unix time
+ *   value before passing it. Behavior when the string is invalid or out of range
+ *   is not well defined, but will probably result in Jan 1, 1970 (0) being passed.
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) {
+   if(value) {
+      time_t time_val = 0;
+      if(s) {
+         date_from_ISO8601(s, &time_val);
+         XMLRPC_SetValueDateTime(value, time_val);
+      }
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601
+ * NAME
+ *   XMLRPC_CreateValueDateTime_ISO8601
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s)
+ * FUNCTION
+ *   Create datetime value from IS08601 encoded string
+ * INPUTS
+ *   id        The id of the new value, or NULL
+ *   s         The desired new time value
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL if no value created.                                
+ * BUGS
+ *   See XMLRPC_SetValueDateTime_ISO8601 ()
+ * SEE ALSO
+ *   XMLRPC_GetValueDateTime_ISO8601 ()
+ *   XMLRPC_SetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueDateTime_ISO8601(val, s);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_SetValueBase64
+ * NAME
+ *   XMLRPC_SetValueBase64
+ * SYNOPSIS
+ *   void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len)
+ * FUNCTION
+ *   Set base64 value.  Base64 is useful for transferring binary data, such as an image.
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   s         The desired new binary value
+ *   len       The length of s, or NULL. If buffer is not null terminated, len *must* be passed.
+ * RESULT
+ *   void                                
+ * NOTES
+ *   Data is set/stored/retrieved as passed in, but is base64 encoded for XML transfer, and
+ *   decoded on the other side.  This is transparent to the caller.
+ * SEE ALSO
+ *   XMLRPC_GetValueBase64 ()
+ *   XMLRPC_CreateValueBase64 ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) {
+   if(value && s) {
+      simplestring_clear(&value->str);
+      (len > 0) ? simplestring_addn(&value->str, s, len) :
+                  simplestring_add(&value->str, s);
+      value->type = xmlrpc_base64;
+   }
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_CreateValueBase64
+ * NAME
+ *   XMLRPC_CreateValueBase64
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len)
+ * FUNCTION
+ *   Create base64 value.  Base64 is useful for transferring binary data, such as an image.
+ * INPUTS
+ *   id        id of the new value, or NULL
+ *   s         The desired new binary value
+ *   len       The length of s, or NULL. If buffer is not null terminated, len *must* be passed.
+ * RESULT
+ *   newly allocated XMLRPC_VALUE, or NULL if error
+ * NOTES
+ *   See XMLRPC_SetValueBase64 ()
+ * SEE ALSO
+ *   XMLRPC_GetValueBase64 ()
+ *   XMLRPC_SetValueBase64 ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueBase64(val, s, len);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetValueDouble
+ * NAME
+ *   XMLRPC_SetValueDouble
+ * SYNOPSIS
+ *   void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val)
+ * FUNCTION
+ *   Set double (floating point) value.
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ *   val       The desired new double value
+ * RESULT
+ *   void                                
+ * SEE ALSO
+ *   XMLRPC_GetValueDouble ()
+ *   XMLRPC_CreateValueDouble ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) {
+   if(value) {
+      value->type = xmlrpc_double;
+      value->d = val;
+   }
+}
+/*******/
+
+/****f* VALUE/XMLRPC_CreateValueDouble
+ * NAME
+ *   XMLRPC_CreateValueDouble
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d)
+ * FUNCTION
+ *   Create double (floating point) value.
+ * INPUTS
+ *   id        id of the newly created value, or NULL
+ *   d         The desired new double value
+ * RESULT
+ *   void                                
+ * SEE ALSO
+ *   XMLRPC_GetValueDouble ()
+ *   XMLRPC_CreateValueDouble ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) {
+   XMLRPC_VALUE val = XMLRPC_CreateValueEmpty();
+   if(val) {
+      XMLRPC_SetValueDouble(val, d);
+      if(id) {
+         XMLRPC_SetValueID(val, id, 0);
+      }
+   }
+   return val;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueString
+ * NAME
+ *   XMLRPC_GetValueString
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueString(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve string value
+ * INPUTS
+ *   value     source XMLRPC_VALUE of type xmlrpc_string
+ * RESULT
+ *   void                                
+ * SEE ALSO
+ *   XMLRPC_SetValueString ()
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+const char* XMLRPC_GetValueString(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_string) ? value->str.str : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueStringLen
+ * NAME
+ *   XMLRPC_GetValueStringLen
+ * SYNOPSIS
+ *   int XMLRPC_GetValueStringLen(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine length of string value
+ * INPUTS
+ *   value     XMLRPC_VALUE of type xmlrpc_string 
+ * RESULT
+ *   length of string, or 0
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_SetValueString ()
+ *   XMLRPC_GetValueString ()
+ * SOURCE
+ */
+int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) {
+    return ((value) ? value->str.len : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueInt
+ * NAME
+ *   XMLRPC_GetValueInt
+ * SYNOPSIS
+ *   int XMLRPC_GetValueInt(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve integer value.
+ * INPUTS
+ *   value     XMLRPC_VALUE of type xmlrpc_int 
+ * RESULT
+ *   integer value or 0 if value is not valid int
+ * NOTES
+ *   use XMLRPC_GetValueType () to be sure if 0 is real return value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueInt ()
+ *   XMLRPC_CreateValueInt ()
+ * SOURCE
+ */
+int XMLRPC_GetValueInt(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_int) ? value->i : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueBoolean
+ * NAME
+ *   XMLRPC_GetValueBoolean
+ * SYNOPSIS
+ *   int XMLRPC_GetValueBoolean(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve boolean value.
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_boolean
+ * RESULT
+ *   boolean value or 0 if value is not valid boolean
+ * NOTES
+ *   use XMLRPC_GetValueType() to be sure if 0 is real value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueBoolean ()
+ *   XMLRPC_CreateValueBoolean ()
+ * SOURCE
+ */
+int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_boolean) ? value->i : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueDouble
+ * NAME
+ *   XMLRPC_GetValueDouble
+ * SYNOPSIS
+ *   double XMLRPC_GetValueDouble(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve double value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_double
+ * RESULT
+ *   double value or 0 if value is not valid double.
+ * NOTES
+ *   use XMLRPC_GetValueType() to be sure if 0 is real value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueDouble ()
+ *   XMLRPC_CreateValueDouble ()
+ * SOURCE
+ */
+double XMLRPC_GetValueDouble(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_double) ? value->d : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueBase64
+ * NAME
+ *   XMLRPC_GetValueBase64
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve binary value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_base64
+ * RESULT
+ *   pointer to binary value or 0 if value is not valid.
+ * SEE ALSO
+ *   XMLRPC_SetValueBase64 ()
+ *   XMLRPC_CreateValueBase64 ()
+ * NOTES
+ *   Call XMLRPC_GetValueStringLen() to retrieve real length of binary data.  strlen()
+ *   will not be accurate, as returned data may contain embedded nulls.
+ * SOURCE
+ */
+const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_base64) ? value->str.str : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueDateTime
+ * NAME
+ *   XMLRPC_GetValueDateTime
+ * SYNOPSIS
+ *   time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve time_t value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_datetime
+ * RESULT
+ *   time_t value or 0 if value is not valid datetime.
+ * NOTES
+ *   use XMLRPC_GetValueType() to be sure if 0 is real value or not
+ * SEE ALSO
+ *   XMLRPC_SetValueDateTime ()
+ *   XMLRPC_GetValueDateTime_ISO8601 ()
+ *   XMLRPC_CreateValueDateTime ()
+ * SOURCE
+ */
+time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) {
+    return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0);
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueDateTime_IOS8601
+ * NAME
+ *   XMLRPC_GetValueDateTime_IOS8601
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueDateTime_IOS8601(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve ISO8601 formatted time value
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_datetime
+ * RESULT
+ *   const char* value or 0 if value is not valid datetime.
+ * SEE ALSO
+ *   XMLRPC_SetValueDateTime_IOS8601 ()
+ *   XMLRPC_GetValueDateTime ()
+ *   XMLRPC_CreateValueDateTime_IOS8601 ()
+ * SOURCE
+ */
+const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) {
+    return ((value && value->type == xmlrpc_datetime) ? value->str.str : 0);
+}
+/*******/
+
+/* Get ID (key) of value or NULL */
+/****f* VALUE/XMLRPC_GetValueID
+ * NAME
+ *   XMLRPC_GetValueID
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueID(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve id (key) of value
+ * INPUTS
+ *   XMLRPC_VALUE of any type
+ * RESULT
+ *   const char* pointer to id of value, or NULL
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_SetValueID()
+ *   XMLRPC_CreateValueEmpty()
+ * SOURCE
+ */
+const char* XMLRPC_GetValueID(XMLRPC_VALUE value) {
+    return (const char*)((value && value->id.len) ? value->id.str : 0);
+}
+/*******/
+
+
+/****f* VECTOR/XMLRPC_VectorSize
+ * NAME
+ *   XMLRPC_VectorSize
+ * SYNOPSIS
+ *   int XMLRPC_VectorSize(XMLRPC_VALUE value)
+ * FUNCTION
+ *   retrieve size of vector
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   count of items in vector
+ * NOTES
+ *   This is a cheap operation even on large vectors.  Vector size is 
+ *   maintained by queue during add/remove ops.
+ * SEE ALSO
+ *   XMLRPC_AddValueToVector ()
+ * SOURCE
+ */
+int XMLRPC_VectorSize(XMLRPC_VALUE value) {
+   int size = 0;
+   if(value && value->type == xmlrpc_vector && value->v) {
+      size = Q_Size(value->v->q);
+   }
+   return size;
+}
+/*******/
+
+/****f* VECTOR/XMLRPC_VectorRewind
+ * NAME
+ *   XMLRPC_VectorRewind
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value)
+ * FUNCTION
+ *   reset vector to first item
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   first XMLRPC_VALUE in list, or NULL if empty or error.
+ * NOTES
+ *   Be careful to rewind any vector passed in to you if you expect to
+ *   iterate through the entire list.
+ * SEE ALSO
+ *   XMLRPC_VectorNext ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(value && value->type == xmlrpc_vector && value->v) {
+      xReturn = (XMLRPC_VALUE)Q_Head(value->v->q);
+   }
+   return xReturn;
+}
+/*******/
+
+/****f* VECTOR/XMLRPC_VectorNext
+ * NAME
+ *   XMLRPC_VectorNext
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Iterate vector to next item in list.
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   Next XMLRPC_VALUE in vector, or NULL if at end.
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_VectorRewind ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(value && value->type == xmlrpc_vector && value->v) {
+      xReturn = (XMLRPC_VALUE)Q_Next(value->v->q);
+   }
+   return xReturn;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueType
+ * NAME
+ *   XMLRPC_GetValueType
+ * SYNOPSIS
+ *   XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine data type of the XMLRPC_VALUE
+ * INPUTS
+ *   XMLRPC_VALUE target of query
+ * RESULT
+ *   data type of value as enumerated by XMLRPC_VALUE_TYPE
+ * NOTES
+ *   all values are of type xmlrpc_empty until set.
+ * SEE ALSO
+ *   XMLRPC_SetValue*
+ *   XMLRPC_CreateValue*
+ *   XMLRPC_Append*
+ * SOURCE
+ */
+XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) {
+   return value ? value->type : xmlrpc_empty;
+}
+/*******/
+
+/* Vector type accessor */
+/****f* VALUE/XMLRPC_GetVectorType
+ * NAME
+ *   XMLRPC_GetVectorType
+ * SYNOPSIS
+ *   XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine vector type of the XMLRPC_VALUE
+ * INPUTS
+ *   XMLRPC_VALUE of type xmlrpc_vector
+ * RESULT
+ *   vector type of value as enumerated by XMLRPC_VECTOR_TYPE
+ * NOTES
+ *   xmlrpc_none is returned if value is not a vector
+ * SEE ALSO
+ *   XMLRPC_SetIsVector ()
+ *   XMLRPC_GetValueType ()
+ * SOURCE
+ */
+XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) {
+   return(value && value->v) ? value->v->type : xmlrpc_none;
+}
+/*******/
+
+
+/*-*******************
+* Begin Server Funcs *
+*********************/
+
+
+/****f* VALUE/XMLRPC_ServerCreate
+ * NAME
+ *   XMLRPC_ServerCreate
+ * SYNOPSIS
+ *   XMLRPC_SERVER XMLRPC_ServerCreate()
+ * FUNCTION
+ *   Allocate/Init XMLRPC Server Resources.
+ * INPUTS
+ *   none
+ * RESULT
+ *   newly allocated XMLRPC_SERVER
+ * NOTES
+ * SEE ALSO
+ *   XMLRPC_ServerDestroy ()
+ *   XMLRPC_GetGlobalServer ()
+ * SOURCE
+ */
+XMLRPC_SERVER XMLRPC_ServerCreate() {
+   XMLRPC_SERVER server = calloc(1, sizeof(STRUCT_XMLRPC_SERVER));
+   if(server) {
+      Q_Init(&server->methodlist);
+      Q_Init(&server->docslist);
+
+      /* register system methods */
+      xsm_register(server);
+   }
+   return server;
+}
+/*******/
+
+/* Return global server.  Not locking! Not Thread Safe! */
+/****f* VALUE/XMLRPC_GetGlobalServer
+ * NAME
+ *   XMLRPC_GetGlobalServer
+ * SYNOPSIS
+ *   XMLRPC_SERVER XMLRPC_GetGlobalServer()
+ * FUNCTION
+ *   Allocates a global (process-wide) server, or returns pointer if pre-existing.
+ * INPUTS
+ *   none
+ * RESULT
+ *   pointer to global server, or 0 if error.
+ * NOTES
+ *   ***WARNING*** This function is not thread safe.  It is included only for the very lazy.
+ *   Multi-threaded programs that use this may experience problems.
+ * BUGS
+ *   There is currently no way to cleanup the global server gracefully.
+ * SEE ALSO
+ *   XMLRPC_ServerCreate ()
+ * SOURCE
+ */
+XMLRPC_SERVER XMLRPC_GetGlobalServer() {
+   static XMLRPC_SERVER xsServer = 0;
+   if(!xsServer) {
+      xsServer = XMLRPC_ServerCreate();
+   }
+   return xsServer;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_ServerDestroy
+ * NAME
+ *   XMLRPC_ServerDestroy
+ * SYNOPSIS
+ *   void XMLRPC_ServerDestroy(XMLRPC_SERVER server)
+ * FUNCTION
+ *   Free Server Resources
+ * INPUTS
+ *   server     The server to be free'd
+ * RESULT
+ *   void
+ * NOTES
+ *   This frees the server struct and any methods that have been added.
+ * SEE ALSO
+ *   XMLRPC_ServerCreate ()
+ * SOURCE
+ */
+void XMLRPC_ServerDestroy(XMLRPC_SERVER server) {
+   if(server) {
+      doc_method* dm = Q_Head(&server->docslist);
+      server_method* sm = Q_Head(&server->methodlist);
+      while( dm ) {
+         my_free(dm);
+         dm = Q_Next(&server->docslist);
+      }
+      while( sm ) {
+         if(sm->name) {
+            my_free(sm->name);
+         }
+         if(sm->desc) {
+            XMLRPC_CleanupValue(sm->desc);
+         }
+         my_free(sm);
+         sm = Q_Next(&server->methodlist);
+      }
+      if(server->xIntrospection) {
+         XMLRPC_CleanupValue(server->xIntrospection);
+      }
+
+      Q_Destroy(&server->methodlist);
+      Q_Destroy(&server->docslist);
+      my_free(server);
+   }
+}
+/*******/
+
+
+/****f* VALUE/XMLRPC_ServerRegisterMethod
+ * NAME
+ *   XMLRPC_ServerRegisterMethod
+ * SYNOPSIS
+ *   void XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb)
+ * FUNCTION
+ *   Register new XMLRPC method with server
+ * INPUTS
+ *   server     The XMLRPC_SERVER to register the method with
+ *   name       public name of the method
+ *   cb         C function that implements the method
+ * RESULT
+ *   int  - 1 if success, else 0
+ * NOTES
+ *   A C function must be registered for every "method" that the server recognizes.  The
+ *   method name is equivalent to <methodCall><name> method name </name></methodCall> in the
+ *   XML syntax.
+ * SEE ALSO
+ *   XMLRPC_ServerFindMethod ()
+ *   XMLRPC_ServerCallMethod ()
+ * SOURCE
+ */
+int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) {
+   if(server && name && cb) {
+
+      server_method* sm = malloc(sizeof(server_method));
+      
+      if(sm) {
+         sm->name = strdup(name);
+         sm->method = cb;
+         sm->desc = NULL;
+
+         return Q_PushTail(&server->methodlist, sm);
+      }
+   }
+   return 0;
+}
+/*******/
+
+inline server_method* find_method(XMLRPC_SERVER server, const char* name) {
+   server_method* sm;
+
+   q_iter qi = Q_Iter_Head_F(&server->methodlist);
+
+   while( qi ) {
+      sm = Q_Iter_Get_F(qi);
+      if(sm && !strcmp(sm->name, name)) {
+         return sm;
+      }
+      qi = Q_Iter_Next_F(qi);
+   }
+   return NULL;
+}
+
+
+const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) {
+    switch(type) {
+       case xmlrpc_none:
+          return "none";
+       case xmlrpc_empty:
+          return "empty";
+       case xmlrpc_base64:
+          return "base64";
+       case xmlrpc_boolean:
+          return "boolean";
+       case xmlrpc_datetime:
+          return "datetime";
+       case xmlrpc_double:
+          return "double";
+       case xmlrpc_int:
+          return "int";
+       case xmlrpc_string:
+          return "string";
+       case xmlrpc_vector:
+          switch(vtype) {
+             case xmlrpc_vector_none:
+                return "none";
+             case xmlrpc_vector_array:
+                return "array";
+             case xmlrpc_vector_mixed:
+                return "mixed vector (struct)";
+             case xmlrpc_vector_struct:
+                return "struct";
+          }
+    }
+}
+
+/****f* VALUE/XMLRPC_ServerFindMethod
+ * NAME
+ *   XMLRPC_ServerFindMethod
+ * SYNOPSIS
+ *   XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName)
+ * FUNCTION
+ *   retrieve C callback associated with a given method name.
+ * INPUTS       
+ *   server     The XMLRPC_SERVER the method is registered with
+ *   callName   the method to find
+ * RESULT
+ *   previously registered XMLRPC_Callback, or NULL
+ * NOTES
+ *   Typically, this is used to determine if a requested method exists, without actually calling it.
+ * SEE ALSO
+ *   XMLRPC_ServerCallMethod ()
+ *   XMLRPC_ServerRegisterMethod ()
+ * SOURCE
+ */
+XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) {
+   if(server && callName) {
+      q_iter qi = Q_Iter_Head_F(&server->methodlist);
+      while( qi ) {
+         server_method* sm = Q_Iter_Get_F(qi);
+         if(sm && !strcmp(sm->name, callName)) {
+            return sm->method;
+         }
+         qi = Q_Iter_Next_F(qi);
+      }
+   }
+   return NULL;
+}
+/*******/
+
+
+/* Call method specified in request */
+/****f* VALUE/XMLRPC_ServerCallMethod
+ * NAME
+ *   XMLRPC_ServerCallMethod
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData)
+ * FUNCTION
+ *
+ * INPUTS
+ *   server     The XMLRPC_SERVER the method is registered with
+ *   request    the request to handle
+ *   userData   any additional data to pass to the C callback, or NULL
+ * RESULT
+ *   XMLRPC_VALUE allocated by the callback, or NULL
+ * NOTES
+ *   It is typically the caller's responsibility to free the returned value.
+ *
+ *   Often the caller will want to serialize the result as XML, via 
+ *   XMLRPC_VALUE_To_XML () or XMLRPC_REQUEST_To_XML ()
+ * SEE ALSO
+ *   XMLRPC_ServerFindMethod ()
+ *   XMLRPC_ServerRegisterMethod ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) {
+   XMLRPC_VALUE xReturn = NULL;
+
+   /* check for error set during request parsing / generation */
+   if(request && request->error) {
+      xReturn = XMLRPC_CopyValue(request->error);
+   }
+   else if(server && request && request->methodName.str) {
+      XMLRPC_Callback cb = XMLRPC_ServerFindMethod(server, request->methodName.str);
+      if(cb) {
+         xReturn = cb(server, request, userData);
+      }
+      else {
+         xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str);
+      }
+   }
+   return xReturn;
+}
+/*******/
+
+/*-*****************
+* End server funcs *
+*******************/
+
+
+/*-***********************************
+* Begin XMLRPC General Options funcs *
+*************************************/
+
+/* For options used by XMLRPC_VALUE funcs that otherwise do not have
+ * parameters for options.  Kind of gross.  :(
+ */
+typedef struct _xmlrpc_options {
+   XMLRPC_CASE id_case;
+   XMLRPC_CASE_COMPARISON id_case_compare;
+} STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS;
+
+static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() {
+   static STRUCT_XMLRPC_OPTIONS options = {
+      xmlrpc_case_exact,
+      xmlrpc_case_sensitive
+   };
+   return &options;
+}
+
+/****f* VALUE/XMLRPC_GetDefaultIdCase
+ * NAME
+ *   XMLRPC_GetDefaultIdCase
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_GetDefaultIdCase()
+ * FUNCTION
+ *   Gets default case options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   none
+ * RESULT
+ *   XMLRPC_CASE
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_SetDefaultIdCase ()
+ * SOURCE
+ */
+XMLRPC_CASE XMLRPC_GetDefaultIdCase() {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   return options->id_case;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetDefaultIdCase
+ * NAME
+ *   XMLRPC_SetDefaultIdCase
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case)
+ * FUNCTION
+ *   Sets default case options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   id_case   case options as enumerated by XMLRPC_CASE
+ * RESULT
+ *   XMLRPC_CASE -- newly set option
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_GetDefaultIdCase ()
+ * SOURCE
+ */
+XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   options->id_case = id_case;
+   return options->id_case;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_GetDefaultIdCaseComparison
+ * NAME
+ *   XMLRPC_GetDefaultIdCaseComparison
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_GetDefaultIdCaseComparison( )
+ * FUNCTION
+ *   Gets default case comparison options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   none
+ * RESULT
+ *   XMLRPC_CASE_COMPARISON default
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_SetDefaultIdCaseComparison ()
+ * SOURCE
+ */
+XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   return options->id_case_compare;
+}
+/*******/
+
+/****f* VALUE/XMLRPC_SetDefaultIdCaseComparison
+ * NAME
+ *   XMLRPC_SetDefaultIdCaseComparison
+ * SYNOPSIS
+ *   XMLRPC_CASE XMLRPC_SetDefaultIdCaseComparison( XMLRPC_CASE_COMPARISON id_case_compare )
+ * FUNCTION
+ *   Gets default case comparison options used by XMLRPC_VALUE funcs
+ * INPUTS
+ *   id_case_compare  case comparison rule to set as default
+ * RESULT
+ *   XMLRPC_CASE_COMPARISON newly set default
+ * BUGS
+ *   Nasty and gross.  Should be server specific, but that requires changing all
+ *  the XMLRPC_VALUE api's.
+ * SEE ALSO
+ *   XMLRPC_GetDefaultIdCaseComparison ()
+ * SOURCE
+ */
+XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case_compare) {
+   XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions();
+   options->id_case_compare = id_case_compare;
+   return options->id_case_compare;
+}
+/*******/
+
+/*-*********************************
+* End XMLRPC General Options funcs *
+***********************************/
+
+
+/*-******************
+* Utility API funcs *
+********************/
+
+/****f* UTILITY/XMLRPC_UtilityCreateFault
+ * NAME
+ *   XMLRPC_UtilityCreateFault
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_UtilityCreateFault( int fault_code, const char* fault_string )
+ * FUNCTION
+ *   generates a struct containing a string member with id "faultString" and an int member
+ *   with id "faultCode". When using the xmlrpc xml serialization, these will be translated
+ *   to <fault><value><struct>... format.
+ * INPUTS
+ *   fault_code     application specific error code. can be 0.
+ *   fault_string   application specific error string.  cannot be null.
+ * RESULT
+ *   XMLRPC_VALUE a newly created struct vector representing the error, or null on error.
+ * NOTES
+ *   This is a utility function. xmlrpc "faults" are not directly represented in this xmlrpc
+ *   API or data structures. It is the author's view, that this API is intended for simple
+ *   data types, and a "fault" is a complex data type consisting of multiple simple data
+ *   types.  This function is provided for convenience only, the same result could be
+ *   achieved directly by the application.
+ *
+ *   This function now supports some "standardized" fault codes, as specified at.
+ *   http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php.
+ *   If one of these fault codes is received, the description string will automatically
+ *   be prefixed with a standard error string and 2 newlines.  
+ *
+ *   The actual transformation between this complex type and the xml "<fault>" element takes
+ *   place in the xmlrpc to xml serialization layer.  This step is not performed when using the
+ *   simplerpc serialization, meaning that there will be no "<fault>" element in that
+ *   serialization. There will simply be a standard struct with 2 child elements.  
+ *   imho, the "<fault>" element is unnecessary and/or out of place as part of the standard API.
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) {
+   XMLRPC_VALUE xOutput = NULL;
+
+   char* string = NULL;
+   simplestring description;
+   simplestring_init(&description);
+
+   switch (fault_code) {
+   case xmlrpc_error_parse_xml_syntax: string = xmlrpc_error_parse_xml_syntax_str; break;
+   case xmlrpc_error_parse_unknown_encoding: string = xmlrpc_error_parse_unknown_encoding_str; break;
+   case xmlrpc_error_parse_bad_encoding: string = xmlrpc_error_parse_bad_encoding_str; break;
+   case xmlrpc_error_invalid_xmlrpc: string = xmlrpc_error_invalid_xmlrpc_str; break;
+   case xmlrpc_error_unknown_method: string = xmlrpc_error_unknown_method_str; break;
+   case xmlrpc_error_invalid_params: string = xmlrpc_error_invalid_params_str; break;
+   case xmlrpc_error_internal_server: string = xmlrpc_error_internal_server_str; break;
+   case xmlrpc_error_application: string = xmlrpc_error_application_str; break;
+   case xmlrpc_error_system: string = xmlrpc_error_system_str; break;
+   case xmlrpc_error_transport: string = xmlrpc_error_transport_str; break;
+   }
+
+   simplestring_add(&description, string);
+
+   if(string && fault_string) {
+      simplestring_add(&description, "\n\n");
+   }
+   simplestring_add(&description, fault_string);
+
+
+   if(description.len) {
+      xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+
+      XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len);
+      XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code);
+   }
+
+   simplestring_free(&description);
+
+   return xOutput;
+}
+/*******/
+
+
+/****f* UTILITY/XMLRPC_Free
+ * NAME
+ *   XMLRPC_Free
+ * SYNOPSIS
+ *   void XMLRPC_Free(void* mem)
+ * FUNCTION
+ *   frees a block of memory allocated by xmlrpc. 
+ * INPUTS
+ *   mem    memory to free
+ * RESULT
+ *   void
+ * NOTES
+ *   Useful for OS's where memory must be free'd
+ *   in the same library in which it is allocated.
+ * SOURCE
+ */
+void XMLRPC_Free(void* mem) {
+   my_free(mem);
+}
+/*******/
+
+
+/****f* UTILITY/XMLRPC_GetVersionString
+ * NAME
+ *   XMLRPC_GetVersionString
+ * SYNOPSIS
+ *   const char* XMLRPC_GetVersionString()
+ * FUNCTION
+ *   returns library version string
+ * INPUTS
+ *   
+ * RESULT
+ *   const char* 
+ * NOTES
+ * SOURCE
+ */
+const char*  XMLRPC_GetVersionString() {
+   return XMLRPC_VERSION_STR;
+}
+/*******/
+
+
+/*-**********************
+* End Utility API funcs *
+************************/
+
+
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.h b/ext/xmlrpc/libxmlrpc/xmlrpc.h
new file mode 100644 (file)
index 0000000..2cfe7cb
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+#ifndef XMLRPC_ALREADY_INCLUDED
+#define XMLRPC_ALREADY_INCLUDED 1
+
+/* includes */
+#include "xml_element.h"
+#include <time.h> /* for time_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* this number, representing the date, must be increased each time the API changes */
+#define XMLRPC_API_NO 20010721
+
+/* this string should be changed with each packaged release */
+#define XMLRPC_VERSION_STR "xmlrpc-epi v. " VERSION
+
+/* where to find more info. shouldn't need to change much */
+#define XMLRPC_HOME_PAGE_STR "http://xmlprc-epi.sourceforge.net/"
+
+
+/****d* VALUE/XMLRPC_VALUE_TYPE
+ * NAME
+ *   XMLRPC_VALUE_TYPE
+ * NOTES
+ *   Defines data types for XMLRPC_VALUE
+ * SEE ALSO
+ *   XMLRPC_VECTOR_TYPE
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+typedef enum _XMLRPC_VALUE_TYPE {
+   xmlrpc_none,                   /* not a value                    */
+   xmlrpc_empty,                  /* empty value, eg NULL           */
+   xmlrpc_base64,                 /* base64 value, eg binary data   */
+   xmlrpc_boolean,                /* boolean  [0 | 1]               */
+   xmlrpc_datetime,               /* datetime [ISO8601 | time_t]    */
+   xmlrpc_double,                 /* double / floating point        */
+   xmlrpc_int,                    /* integer                        */
+   xmlrpc_string,                 /* string                         */
+   xmlrpc_vector                  /* vector, aka list, array        */
+} XMLRPC_VALUE_TYPE;
+/*******/
+
+/****d* VALUE/XMLRPC_VECTOR_TYPE
+ * NAME
+ *   XMLRPC_VECTOR_TYPE
+ * NOTES
+ *   Defines data types for XMLRPC_VECTOR
+ * SEE ALSO
+ *   XMLRPC_VALUE_TYPE
+ *   XMLRPC_REQUEST_TYPE
+ * SOURCE
+ */
+typedef enum _XMLRPC_VECTOR_TYPE {
+   xmlrpc_vector_none,            /* not an array                   */
+   xmlrpc_vector_array,           /* no values may have key names   */
+   xmlrpc_vector_mixed,           /* some values may have key names */
+   xmlrpc_vector_struct           /* all values must have key names */
+} XMLRPC_VECTOR_TYPE;
+/*******/
+
+/****d* VALUE/XMLRPC_REQUEST_TYPE
+ * NAME
+ *   XMLRPC_REQUEST_TYPE
+ * NOTES
+ *   Defines data types for XMLRPC_REQUEST
+ * SEE ALSO
+ *   XMLRPC_VALUE_TYPE
+ *   XMLRPC_VECTOR_TYPE
+ * SOURCE
+ */
+typedef enum _xmlrpc_request_type {
+   xmlrpc_request_none,          /* not a valid request            */
+   xmlrpc_request_call,          /* calling/invoking a method      */
+   xmlrpc_request_response,      /* responding to a method call    */
+} XMLRPC_REQUEST_TYPE;
+/*******/
+
+/****d* VALUE/XMLRPC_ERROR_CODE
+ * NAME
+ *   XMLRPC_ERROR_CODE
+ * NOTES
+ *   All existing error codes
+ * SEE ALSO
+ *   XMLRPC_REQUEST_ERROR
+ * SOURCE
+ */
+typedef enum _xmlrpc_error_code {
+   xmlrpc_error_none                      = 0,              /* not an error                                      */
+   xmlrpc_error_parse_xml_syntax          = -32700,
+   xmlrpc_error_parse_unknown_encoding    = -32701,
+   xmlrpc_error_parse_bad_encoding        = -32702,
+   xmlrpc_error_invalid_xmlrpc            = -32600,
+   xmlrpc_error_unknown_method            = -32601,
+   xmlrpc_error_invalid_params            = -32602,
+   xmlrpc_error_internal_server           = -32603,
+   xmlrpc_error_application               = -32500,
+   xmlrpc_error_system                    = -32400,
+   xmlrpc_error_transport                 = -32300
+} XMLRPC_ERROR_CODE;
+/******/
+
+#define xmlrpc_error_parse_xml_syntax_str       "parse error. not well formed."
+#define xmlrpc_error_parse_unknown_encoding_str "parse error. unknown encoding"
+#define xmlrpc_error_parse_bad_encoding_str     "parse error. invalid character for encoding"
+#define xmlrpc_error_invalid_xmlrpc_str         "server error. xml-rpc not conforming to spec"
+#define xmlrpc_error_unknown_method_str         "server error. method not found."
+#define xmlrpc_error_invalid_params_str         "server error. invalid method parameters"
+#define xmlrpc_error_internal_server_str        "server error. internal xmlrpc library error"
+#define xmlrpc_error_application_str            "application error."
+#define xmlrpc_error_system_str                 "system error."
+#define xmlrpc_error_transport_str              "transport error."
+
+
+
+/****d* VALUE/XMLRPC_VERSION
+ * NAME
+ *   XMLRPC_VERSION
+ * NOTES
+ *   Defines xml vocabulary used for generated xml
+ * SEE ALSO
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ *   XMLRPC_REQUEST_To_XML ()
+ * SOURCE
+ */
+typedef enum _xmlrpc_version {
+   xmlrpc_version_none,          /* not a recognized vocabulary    */ 
+   xmlrpc_version_1_0,           /* xmlrpc 1.0 standard vocab      */ 
+   xmlrpc_version_simple = 2,    /* alt more readable vocab        */ 
+   xmlrpc_version_danda = 2      /* same as simple. legacy         */
+} XMLRPC_VERSION;
+/******/
+
+/****s* VALUE/XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * NAME
+ *   XMLRPC_REQUEST_OUTPUT_OPTIONS
+ * NOTES
+ *   Defines output options for generated xml
+ * SEE ALSO
+ *   XMLRPC_VERSION
+ *   XML_ELEM_OUTPUT_OPTIONS
+ *   XMLRPC_REQUEST_To_XML ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_request_output_options {
+   STRUCT_XML_ELEM_OUTPUT_OPTIONS xml_elem_opts;  /* xml_element specific output options */
+   XMLRPC_VERSION                 version;        /* xml vocabulary to use               */
+} STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS, *XMLRPC_REQUEST_OUTPUT_OPTIONS;
+/******/
+
+/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS
+ * NAME
+ *   XMLRPC_REQUEST_INPUT_OPTIONS
+ * NOTES
+ *   Defines options for reading in xml data
+ * SEE ALSO
+ *   XMLRPC_VERSION
+ *   XML_ELEM_INPUT_OPTIONS
+ *   XMLRPC_REQUEST_From_XML ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_request_input_options {
+   STRUCT_XML_ELEM_INPUT_OPTIONS  xml_elem_opts;  /* xml_element specific output options */
+} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS;
+/******/
+
+/****s* VALUE/XMLRPC_ERROR
+ * NAME
+ *   XMLRPC_ERROR
+ * NOTES
+ *   For the reporting and handling of errors
+ * SOURCE
+ */
+typedef struct _xmlrpc_error {
+   XMLRPC_ERROR_CODE      code;
+   STRUCT_XML_ELEM_ERROR  xml_elem_error;  /* xml_element errors (parser errors) */
+} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR;
+/******/
+
+
+/****d* VALUE/XMLRPC_CASE_COMPARISON
+ * NAME
+ *   XMLRPC_CASE_COMPARISON
+ * NOTES
+ *   Defines case comparison options for XMLRPC_VALUE/VECTOR API's
+ * SEE ALSO
+ *   XMLRPC_CASE
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+typedef enum _xmlrpc_case_comparison {
+   xmlrpc_case_insensitive,      /* use case-insensitive compare */
+   xmlrpc_case_sensitive         /* use case-sensitive compare   */
+} XMLRPC_CASE_COMPARISON;
+/******/
+
+/****d* VALUE/XMLRPC_CASE
+ * NAME
+ *   XMLRPC_CASE
+ * NOTES
+ *   Defines case behavior when setting IDs in XMLRPC_VALUE API's
+ * SEE ALSO
+ *   XMLRPC_CASE_COMPARISON
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+typedef enum _xmlrpc_case {
+   xmlrpc_case_exact,            /* leave case alone             */
+   xmlrpc_case_lower,            /* lower-case id                */
+   xmlrpc_case_upper             /* upper-case id                */
+} XMLRPC_CASE;
+/******/
+
+/* if you don't like these defaults, you can set them with XMLRPC_SetDefaultIdCase*() */
+#define XMLRPC_DEFAULT_ID_CASE              XMLRPC_GetDefaultIdCase()
+#define XMLRPC_DEFAULT_ID_CASE_SENSITIVITY  XMLRPC_GetDefaultIdCaseComparison()
+
+/* opaque (non-public) types. defined locally in xmlrpc.c */
+typedef struct _xmlrpc_request* XMLRPC_REQUEST;
+typedef struct _xmlrpc_server* XMLRPC_SERVER;
+typedef struct _xmlrpc_value* XMLRPC_VALUE;
+
+/****d* VALUE/XMLRPC_Callback
+ * NAME
+ *   XMLRPC_Callback
+ * NOTES
+ *   Function prototype for user defined method handlers (callbacks).
+ * SEE ALSO
+ *   XMLRPC_ServerRegisterMethod ()
+ *   XMLRPC_ServerCallMethod ()
+ *   XMLRPC_REQUEST
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+typedef XMLRPC_VALUE (*XMLRPC_Callback)(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+/******/
+
+/* ID Case Defaults */
+XMLRPC_CASE XMLRPC_GetDefaultIdCase();
+XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case);
+XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison();
+XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case);
+
+/* Vector manipulation */
+int XMLRPC_VectorSize(XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value);
+int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type);
+int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source);
+int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...);
+int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case);
+
+
+/* Create values */
+XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int truth);
+XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len);
+XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time);
+XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s);
+XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double f);
+XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i);
+XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* s, int len);
+XMLRPC_VALUE XMLRPC_CreateValueEmpty();
+XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type);
+
+/* Cleanup values */
+void XMLRPC_CleanupValue(XMLRPC_VALUE value);
+XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value);
+
+/* Set Values */
+void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time);
+void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s);
+void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val);
+void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val);
+void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val);
+const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* s, int len);
+void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len);
+const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case);
+#define XMLRPC_SetValueID(value, id, len) XMLRPC_SetValueID_Case(value, id, len, XMLRPC_DEFAULT_ID_CASE)
+
+/* Get Values */
+const char* XMLRPC_GetValueString(XMLRPC_VALUE value);
+int XMLRPC_GetValueStringLen(XMLRPC_VALUE value);
+int XMLRPC_GetValueInt(XMLRPC_VALUE value);
+int XMLRPC_GetValueBoolean(XMLRPC_VALUE value);
+double XMLRPC_GetValueDouble(XMLRPC_VALUE value);
+const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value);
+time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value);
+const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value);
+const char* XMLRPC_GetValueID(XMLRPC_VALUE value);
+
+/* Type introspection */
+XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v);
+XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v);
+
+/* Parsing and Creating XML */
+XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options);
+XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options);
+char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int *buf_len);
+char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len);
+
+/* Request manipulation funcs */
+const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName);
+const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request);
+XMLRPC_REQUEST XMLRPC_RequestNew();
+void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO);
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output);
+XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request);
+XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data);
+XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request);
+XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type);
+XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request);
+
+/* Server Creation/Destruction; Method Registration and Invocation */
+XMLRPC_SERVER XMLRPC_ServerCreate();
+XMLRPC_SERVER XMLRPC_GetGlobalServer();   /* better to use XMLRPC_ServerCreate if you can */
+void XMLRPC_ServerDestroy(XMLRPC_SERVER server);
+int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb);
+XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName);
+XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData);
+
+#include "xmlrpc_introspection.h"
+
+/* Public Utility funcs */
+XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string);
+void XMLRPC_Free(void* mem);
+const char*  XMLRPC_GetVersionString();
+
+/****d* VALUE/XMLRPC_MACROS
+ * NAME
+ *   Some Helpful Macros
+ * NOTES
+ *   Some macros for making life easier.  Should be self-explanatory.
+ * SEE ALSO
+ *   XMLRPC_AddValueToVector ()
+ *   XMLRPC_VectorGetValueWithID_Case ()
+ *   XMLRPC_VALUE
+ * SOURCE
+ */
+
+/* Append values to vector */
+#define XMLRPC_VectorAppendString(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id, s, len))
+#define XMLRPC_VectorAppendBase64(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id, s, len))
+#define XMLRPC_VectorAppendDateTime(vector, id, time) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time))
+#define XMLRPC_VectorAppendDateTime_ISO8601(vector, id, s) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id, s))
+#define XMLRPC_VectorAppendDouble(vector, id, f) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id, f))
+#define XMLRPC_VectorAppendInt(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id, i))
+#define XMLRPC_VectorAppendBoolean(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id, i))
+
+/* Get named values from vector */
+#define XMLRPC_VectorGetValueWithID(vector, id) XMLRPC_VectorGetValueWithID_Case(vector, id, XMLRPC_DEFAULT_ID_CASE_SENSITIVITY)
+#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id))
+#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id))
+
+/******/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XMLRPC_ALREADY_INCLUDED */
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.m4 b/ext/xmlrpc/libxmlrpc/xmlrpc.m4
new file mode 100644 (file)
index 0000000..18d3606
--- /dev/null
@@ -0,0 +1,16 @@
+CPPFLAGS="$CPPFLAGS -DVERSION=\\\"0.42\\\""
+
+AC_DEFUN(XMLRPC_CHECKS,[       
+
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_LN_S])
+AC_REQUIRE([AC_PROG_RANLIB])
+
+AC_DEFINE(UNDEF_THREADS_HACK,,[ ])
+
+XMLRPC_HEADER_CHECKS
+XMLRPC_TYPE_CHECKS
+XMLRPC_FUNCTION_CHECKS
+
+PHP_FAST_OUTPUT($ext_builddir/libxmlrpc/Makefile)
+])
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c
new file mode 100644 (file)
index 0000000..5b15d4b
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+/****h* ABOUT/xmlrpc_introspection
+ * AUTHOR
+ *   Dan Libby, aka danda  (dan@libby.com)
+ * HISTORY
+ *   4/10/2001 -- danda -- initial introspection support
+ * TODO
+ * NOTES
+ *******/
+
+
+#include "queue.h"
+#include "xmlrpc.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+/* forward declarations for static (non public, non api) funcs */
+static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
+
+
+/*-**********************************
+* Introspection Callbacks (methods) *
+************************************/
+
+/* iterates through a list of structs and finds the one with key "name" matching
+ * needle.  slow, would benefit from a struct key hash.
+ */
+inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) {
+   XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list);
+   while(xIter) {
+      const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name);
+      if(name && !strcmp(name, needle)) {
+         return xIter;
+      }
+      xIter = XMLRPC_VectorNext(list);
+   }
+   return NULL;
+}
+
+
+/* iterates through docs callbacks and calls any that have not yet been called */
+static void check_docs_loaded(XMLRPC_SERVER server, void* userData) {
+   if(server) {
+      q_iter qi = Q_Iter_Head_F(&server->docslist);
+      while( qi ) {
+         doc_method* dm = Q_Iter_Get_F(qi);
+         if(dm && !dm->b_called) {
+            dm->method(server, userData);
+            dm->b_called = 1;
+         }
+         qi = Q_Iter_Next_F(qi);
+      }
+   }
+}
+
+
+/* utility function for xi_system_describe_methods_cb */
+inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) {
+   if(method) {
+      server_method* sm = find_method(server, method);
+      if(sm) {
+         XMLRPC_AddValueToVector(vector, sm->desc);
+      }
+   }
+}
+
+
+
+/* system.describeMethods() callback */
+static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
+   XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+   XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array);
+   XMLRPC_VALUE xTypeList = NULL;
+   int bAll = 1;
+
+   /* lazy loading of introspection data */
+   check_docs_loaded(server, userData);
+
+   xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
+
+   XMLRPC_AddValueToVector(xResponse, xTypeList);
+   XMLRPC_AddValueToVector(xResponse, xMethodList);
+
+   /* check if we have any param */
+   if(xParams) {
+      /* check if string or vector (1 or n) */
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams);
+      if(type == xmlrpc_string) {
+         /* just one.  spit it out. */
+         describe_method(server, xMethodList, XMLRPC_GetValueString(xParams));
+         bAll = 0;
+      }
+      else if(type == xmlrpc_vector) {
+         /* multiple.  spit all out */
+         XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams);
+         while(xIter) {
+            describe_method(server, xMethodList, XMLRPC_GetValueString(xIter));
+            xIter = XMLRPC_VectorNext(xParams);
+         }
+         bAll = 0;
+      }
+   }
+
+   /* otherwise, default to sending all methods */
+   if(bAll) {
+      q_iter qi = Q_Iter_Head_F(&server->methodlist);
+      while( qi ) {
+         server_method* sm = Q_Iter_Get_F(qi);
+         if(sm) {
+            XMLRPC_AddValueToVector(xMethodList, sm->desc);
+         }
+         qi = Q_Iter_Next_F(qi);
+      }
+   }
+   
+   return xResponse;
+}
+
+/* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */
+static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
+
+   q_iter qi = Q_Iter_Head_F(&server->methodlist);
+   while( qi ) {
+      server_method* sm = Q_Iter_Get_F(qi);
+      if(sm) {
+         XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0);
+      }
+      qi = Q_Iter_Next_F(qi);
+   }
+   return xResponse;
+}
+
+/* this complies with system.methodSignature as defined at 
+ * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html 
+ */
+static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
+   XMLRPC_VALUE xResponse = NULL;
+
+   /* lazy loading of introspection data */
+   check_docs_loaded(server, userData);
+
+   if(method) {
+      server_method* sm = find_method(server, method);
+      if(sm && sm->desc) {
+         XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
+         XMLRPC_VALUE xIter, xParams, xSig, xSigIter;
+         const char* type;
+
+         /* array of possible signatures.  */
+         xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
+
+         /* find first signature */
+         xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures);
+         xSigIter = XMLRPC_VectorRewind( xSig );
+
+         /* iterate through sigs */
+         while(xSigIter) {
+            /* first type is the return value */
+            type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind(
+                                                 XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), 
+                                                xi_token_type);
+            XMLRPC_AddValueToVector(xTypesArray, 
+                                    XMLRPC_CreateValueString(NULL, 
+                                                             type ? type : type_to_str(xmlrpc_none, 0), 
+                                    0));
+
+            /* the rest are parameters */
+            xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params);
+            xIter = XMLRPC_VectorRewind(xParams);
+
+            /* iter through params, adding to types array */
+            while(xIter) {
+               XMLRPC_AddValueToVector(xTypesArray,
+                                       XMLRPC_CreateValueString(NULL, 
+                                                                XMLRPC_VectorGetStringWithID(xIter, xi_token_type),
+                                                                0));
+               xIter = XMLRPC_VectorNext(xParams);
+            }
+
+            /* add types for this signature */
+            XMLRPC_AddValueToVector(xResponse, xTypesArray);
+
+            xSigIter = XMLRPC_VectorNext( xSig );
+         }
+      }
+   }
+
+   return xResponse;
+}
+
+/* this complies with system.methodHelp as defined at 
+ * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html 
+ */
+static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
+   const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
+   XMLRPC_VALUE xResponse = NULL;
+
+   /* lazy loading of introspection data */
+   check_docs_loaded(server, userData);
+
+   if(method) {
+      server_method* sm = find_method(server, method);
+      if(sm && sm->desc) {
+         const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose);
+
+         /* returns a documentation string, or empty string */
+         xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0);
+      }
+   }
+
+   return xResponse;
+}
+
+/*-**************************************
+* End Introspection Callbacks (methods) *
+****************************************/
+
+
+/*-************************
+* Introspection Utilities *
+**************************/
+
+/* performs registration of introspection methods */
+void xi_register_system_methods(XMLRPC_SERVER server) {
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb);
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb);
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb);
+   XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb);
+}
+
+/* describe a value (param, return, type) */
+static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) {
+   XMLRPC_VALUE xParam = NULL;
+   if(id || desc) {
+      xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+      XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0);
+      XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0);
+      XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0);
+      if(optional != 2) {
+         XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional);
+      }
+      if(optional == 1 && default_val) {
+         XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0);
+      }
+      XMLRPC_AddValueToVector(xParam, sub_params);
+   }
+   return xParam;
+}
+
+
+/* convert an xml tree conforming to spec <url tbd> to  XMLRPC_VALUE
+ * suitable for use with XMLRPC_ServerAddIntrospectionData
+ */
+XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) {
+   XMLRPC_VALUE xReturn = NULL;
+
+   if(el->name) {
+      const char* name = NULL;
+      const char* type = NULL;
+      const char* basetype = NULL;
+      const char* desc = NULL;
+      const char* def = NULL;
+      int optional = 0;
+      xml_element_attr* attr_iter = Q_Head(&el->attrs);
+
+      /* grab element attributes up front to save redundant while loops */
+      while(attr_iter) {
+         if(!strcmp(attr_iter->key, "name")) {
+            name = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "type")) {
+            type = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "basetype")) {
+            basetype = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "desc")) {
+            desc = attr_iter->val;
+         }
+         else if(!strcmp(attr_iter->key, "optional")) {
+            if(attr_iter->val && !strcmp(attr_iter->val, "yes")) {
+               optional = 1;
+            }
+         }
+         else if(!strcmp(attr_iter->key, "default")) {
+            def = attr_iter->val;
+         }
+         attr_iter = Q_Next(&el->attrs);
+      }
+
+      /* value and typeDescription behave about the same */
+      if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) {
+         XMLRPC_VALUE xSubList = NULL;
+         const char* ptype = !strcmp(el->name, "value") ? type : basetype;
+         if(ptype) {
+            if(Q_Size(&el->children) &&
+               !strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed")) {
+               xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array);
+
+               if(xSubList) {
+                  xml_element* elem_iter = Q_Head(&el->children);
+                  while(elem_iter) {
+                     XMLRPC_AddValueToVector(xSubList, 
+                                             xml_element_to_method_description(elem_iter, err));
+                     elem_iter = Q_Next(&el->children);
+                  }
+               }
+            }
+            xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList);
+         }
+      }
+
+      /* these three kids are about equivalent */
+      else if(!strcmp(el->name, "params") || 
+              !strcmp(el->name, "returns") || 
+              !strcmp(el->name, "signature")) {
+         if(Q_Size(&el->children)) {
+            xml_element* elem_iter = Q_Head(&el->children);
+            xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct);
+
+
+            while(elem_iter) {
+               XMLRPC_AddValueToVector(xReturn, 
+                                       xml_element_to_method_description(elem_iter, err));
+               elem_iter = Q_Next(&el->children);
+            }
+         }
+      }
+
+
+      else if(!strcmp(el->name, "methodDescription")) {
+         xml_element* elem_iter = Q_Head(&el->children);
+         xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+
+         XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0);
+
+         while(elem_iter) {
+            XMLRPC_AddValueToVector(xReturn, 
+                                    xml_element_to_method_description(elem_iter, err));
+            elem_iter = Q_Next(&el->children);
+         }
+      }
+
+      /* items are slightly special */
+      else if(!strcmp(el->name, "item")) {
+         xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len);
+      }
+
+      /* sure.  we'll let any ol element with children through */
+      else if(Q_Size(&el->children)) {
+         xml_element* elem_iter = Q_Head(&el->children);
+         xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed);
+
+         while(elem_iter) {
+            XMLRPC_AddValueToVector(xReturn, 
+                                    xml_element_to_method_description(elem_iter, err));
+            elem_iter = Q_Next(&el->children);
+         }
+      }
+
+      /* or anything at all really, so long as its got some text. 
+       * no reason being all snotty about a spec, right? 
+       */
+      else if(el->name && el->text.len) {
+         xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len);
+      }
+   }
+
+   return xReturn;
+}
+
+/*-****************************
+* End Introspection Utilities *
+******************************/
+
+
+
+/*-******************
+* Introspection API *
+********************/
+
+
+/****f* VALUE/XMLRPC_IntrospectionCreateDescription
+ * NAME
+ *   XMLRPC_IntrospectionCreateDescription
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err)
+ * FUNCTION
+ *   converts raw xml describing types and methods into an
+ *   XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData()
+ * INPUTS
+ *   xml - xml data conforming to introspection spec at <url tbd>
+ *   err - optional pointer to error struct. filled in if error occurs and not NULL.
+ * RESULT
+ *   XMLRPC_VALUE - newly created value, or NULL if fatal error.
+ * BUGS
+ *   Currently does little or no validation of xml.
+ *   Only parse errors are currently reported in err, not structural errors.
+ * SEE ALSO
+ *   XMLRPC_ServerAddIntrospectionData ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) {
+   XMLRPC_VALUE xReturn = NULL;
+   xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL);
+
+   if(root) {
+      xReturn = xml_element_to_method_description(root, err);
+
+      xml_elem_free(root);
+   }
+
+   return xReturn;
+
+}
+/*******/
+
+
+/****f* SERVER/XMLRPC_ServerAddIntrospectionData
+ * NAME
+ *   XMLRPC_ServerAddIntrospectionData
+ * SYNOPSIS
+ *   int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc)
+ * FUNCTION
+ *   updates server with additional introspection data
+ * INPUTS
+ *   server - target server
+ *   desc - introspection data, should be a struct generated by 
+ *          XMLRPC_IntrospectionCreateDescription ()
+ * RESULT
+ *   int - 1 if success, else 0
+ * NOTES
+ *  - function will fail if neither typeList nor methodList key is present in struct.
+ *  - if method or type already exists, it will be replaced.
+ *  - desc is never freed by the server.  caller is responsible for cleanup.
+ * BUGS
+ *   - horribly slow lookups. prime candidate for hash improvements.
+ *   - uglier and more complex than I like to see for API functions.
+ * SEE ALSO
+ *   XMLRPC_ServerAddIntrospectionData ()
+ *   XMLRPC_ServerRegisterIntrospectionCallback ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) {
+   int bSuccess = 0;
+   if(server && desc) {
+      XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList");
+      XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList");
+      XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
+
+      if(xNewMethods) {
+         XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods);
+
+         while(xMethod) {
+            const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name);
+            server_method* sm = find_method(server, name);
+
+            if(sm) {
+               if(sm->desc) {
+                  XMLRPC_CleanupValue(sm->desc);
+               }
+               sm->desc = XMLRPC_CopyValue(xMethod);
+               bSuccess = 1;
+            }
+
+            xMethod = XMLRPC_VectorNext(xNewMethods);
+         }
+      }
+      if(xNewTypes) {
+         if(!xServerTypes) {
+            if(!server->xIntrospection) {
+               server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
+            }
+
+            XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes);
+            bSuccess = 1;
+         }
+         else {
+            XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes);
+            while(xIter) {
+               /* get rid of old values */
+               XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name));
+               if(xPrev) {
+                  XMLRPC_VectorRemoveValue(xServerTypes, xPrev);
+               }
+               XMLRPC_AddValueToVector(xServerTypes, xIter);
+               bSuccess = 1;
+               xIter = XMLRPC_VectorNext(xNewTypes);
+            }
+         }
+      }
+   }
+   return bSuccess;
+}
+/*******/
+
+
+/****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback
+ * NAME
+ *   XMLRPC_ServerRegisterIntrospectionCallback
+ * SYNOPSIS
+ *   int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb)
+ * FUNCTION
+ *   registers a callback for lazy generation of introspection data
+ * INPUTS
+ *   server - target server
+ *   cb - callback that will generate introspection data
+ * RESULT
+ *   int - 1 if success, else 0
+ * NOTES
+ *   parsing xml and generating introspection data is fairly expensive, thus a
+ *   server may wish to wait until this data is actually requested before generating
+ *   it. Any number of callbacks may be registered at any time.  A given callback
+ *   will only ever be called once, the first time an introspection request is
+ *   processed after the time of callback registration.
+ * SEE ALSO
+ *   XMLRPC_ServerAddIntrospectionData ()
+ *   XMLRPC_IntrospectionCreateDescription ()
+ * SOURCE
+ */
+int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) {
+   int bSuccess = 0;
+   if(server && cb) {
+
+      doc_method* dm = calloc(1, sizeof(doc_method));
+      
+      if(dm) {
+         dm->method = cb;
+         dm->b_called = 0;
+
+         if(Q_PushTail(&server->docslist, dm)) {
+            bSuccess = 1;
+         }
+         else {
+            my_free(dm);
+         }
+      }
+   }
+   return 0;
+}
+/*******/
+
+/*-**********************
+* End Introspection API *
+************************/
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h
new file mode 100644 (file)
index 0000000..656e441
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* IMPORTANT!
+ *
+ * only public (official API) things should be in this file. Anything else
+ * should go in <group>_private.h, or in the appropriate .c file.
+ */
+
+
+#ifndef __XI_INTROSPECTION_H
+/*
+ * Avoid include redundancy.
+ */
+#define __XI_INTROSPECTION_H
+
+/*----------------------------------------------------------------------------
+ * xmlrpc_introspection.h
+ *
+ * Purpose:
+ *   define public introspection API
+ * Comments:
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+ #define xi_token_params "params"
+ #define xi_token_returns "returns"
+ #define xi_token_related "related"
+ #define xi_token_sub "sub"
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+ /****d* VALUE/XMLRPC_IntrospectionCallback
+ * NAME
+ *   XMLRPC_IntrospectionCallback
+ * NOTES
+ *   Function prototype for lazy documentation generation (not generated until requested).
+ * SOURCE
+ */
+typedef void (*XMLRPC_IntrospectionCallback)(XMLRPC_SERVER server, void* userData);
+/******/
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR error);
+int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc);
+int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+
+
+#endif /* __XI_INTROSPECTION_H */
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h
new file mode 100644 (file)
index 0000000..7b97fa7
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Dan Libby, Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* IMPORTANT!
+ *
+ * only non-public things should be in this file.  It is fine for any .c file
+ * in xmlrpc/src to include it, but users of the public API should never
+ * include it, and thus *.h files that are part of the public API should
+ * never include it, or they would break if this file is not present.
+ */
+
+
+#ifndef __XI_INTROSPECTION_PRIVATE_H
+/*
+ * Avoid include redundancy.
+ */
+#define __XI_INTROSPECTION_PRIVATE_H
+
+/*----------------------------------------------------------------------------
+ * xmlrpc_introspection_private.h
+ *
+ * Purpose:
+ *   define non-public introspection routines
+ * Comments:
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+#define xi_token_default                    "default"
+#define xi_token_description                "description"
+#define xi_token_name                       "name"
+#define xi_token_optional                   "optional"
+#define xi_token_params                     "params"
+#define xi_token_purpose                    "purpose"
+#define xi_token_returns                    "returns"
+#define xi_token_signatures                 "signatures"
+#define xi_token_type                       "type"
+#define xi_token_version                    "version"
+#define xi_token_empty                      ""
+#define xi_token_system_describe_methods    "system.describeMethods"
+#define xi_token_system_list_methods        "system.listMethods"
+#define xi_token_system_method_help         "system.methodHelp"
+#define xi_token_system_method_signature    "system.methodSignature"
+
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+typedef struct _doc_method {
+   XMLRPC_IntrospectionCallback         method;
+   int                                  b_called;
+} doc_method; 
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+void xi_register_system_methods(XMLRPC_SERVER server);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+
+#endif /* __XI_INTROSPECTION_PRIVATE_H */
+
+
+
+
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_private.h b/ext/xmlrpc/libxmlrpc/xmlrpc_private.h
new file mode 100644 (file)
index 0000000..0b4a078
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* only non-public things should be in this file.  It is fine for any .c file
+ * in xmlrpc/src to include it, but users of the public API should never
+ * include it, and thus *.h files that are part of the public API should
+ * never include it, or they would break if this file is not present.
+ */
+
+#ifndef XMLRPC_PRIVATE_ALREADY_INCLUDED
+/*
+ * Avoid include redundancy.
+ */
+#define XMLRPC_PRIVATE_ALREADY_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*----------------------------------------------------------------------------
+ * xmlrpc_private.h
+ *
+ * Purpose:
+ *   define non-public intra-library routines & data
+ * Comments:
+ */
+
+/*----------------------------------------------------------------------------
+ * Constants
+ */
+
+
+/*----------------------------------------------------------------------------
+ * Includes
+ */
+
+/*----------------------------------------------------------------------------
+ * Structures
+ */
+/* Some of these are typedef'd in xmlrpc.h for public use */
+
+typedef struct _xmlrpc_vector* XMLRPC_VECTOR;
+
+/****s* VALUE/XMLRPC_VALUE
+ * NAME
+ *   XMLRPC_VALUE
+ * NOTES
+ *   A value of variable data type. The most important object in this API.  :)
+ *
+ *  This struct is opaque to callers and should be accessed only via accessor functions.
+ * SEE ALSO
+ *   XMLRPC_REQUEST
+ *   XMLRPC_CreateValueEmpty ()
+ *   XMLRPC_CleanupValue ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_value {
+   XMLRPC_VALUE_TYPE type; /* data type of this value                        */
+   XMLRPC_VECTOR v;        /* vector type specific info                      */
+   simplestring str;       /* string value buffer                            */
+   simplestring id;        /* id of this value.  possibly empty.             */
+   int i;                  /* integer value.                                 */
+   double d;               /* double value                                   */
+   int iRefCount;          /* So we know when we can delete the value      . */
+} STRUCT_XMLRPC_VALUE;
+/******/
+
+/****s* VALUE/XMLRPC_REQUEST
+ * NAME
+ *   XMLRPC_REQUEST
+ * NOTES
+ *   Internal representation of an XML request.
+ *
+ *  This struct is opaque to callers and should be accessed only via accessor functions.
+ *  
+ * SEE ALSO
+ *   XMLRPC_VALUE
+ *   XMLRPC_RequestNew ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_request {
+   XMLRPC_VALUE                         io;           /* data associated with this request */
+   simplestring                         methodName;   /* name of method being called       */
+   XMLRPC_REQUEST_TYPE                  request_type; /* type of request                   */
+   STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS output;       /* xml output options                */
+   XMLRPC_VALUE                         error;        /* error codes                       */
+} STRUCT_XMLRPC_REQUEST;
+/******/
+
+/* Vector type. Used by XMLRPC_VALUE.  Never visible to users of the API. */
+typedef struct _xmlrpc_vector {
+   XMLRPC_VECTOR_TYPE type;                           /* vector type                       */
+   const char* id;                                    /* ??? unused?                       */
+   queue *q;                                          /* list of child values              */
+} STRUCT_XMLRPC_VECTOR;
+/******/
+
+/****s* VALUE/XMLRPC_SERVER
+ * NAME
+ *   XMLRPC_SERVER
+ * NOTES
+ *   internal representation of an xmlrpc server
+ *
+ *  This struct is opaque to callers and should be accessed only via accessor functions.
+ *  
+ * SEE ALSO
+ *   XMLRPC_ServerCreate ()
+ *   XMLRPC_ServerDestroy ()
+ * SOURCE
+ */
+typedef struct _xmlrpc_server {
+   queue methodlist;                                  /* list of callback methods          */
+   queue docslist;                                    /* list of introspection callbacks   */
+   XMLRPC_VALUE xIntrospection;
+} STRUCT_XMLRPC_SERVER;
+/******/
+
+typedef struct _server_method {
+   char*                   name;
+   XMLRPC_VALUE            desc;
+   XMLRPC_Callback         method;
+} server_method;
+
+
+/*----------------------------------------------------------------------------
+ * Globals
+ */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ */
+server_method* find_method(XMLRPC_SERVER server, const char* name);
+const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
+/*----------------------------------------------------------------------------
+ * Macros
+ */
+#define my_free(thing)  if(thing) {free(thing); thing = 0;}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* XMLRPC_PRIVATE_ALREADY_INCLUDED */
+
diff --git a/ext/xmlrpc/php_config.h.in b/ext/xmlrpc/php_config.h.in
new file mode 100644 (file)
index 0000000..2c8a1bf
--- /dev/null
@@ -0,0 +1,11 @@
+/* php_config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if your C compiler doesn't accept -c and -o together.  */
+#undef NO_MINUS_C_MINUS_O
+
+/* Whether you have XMLRPC */
+#undef HAVE_XMLRPC
+
+/* Whether to build xmlrpc as dynamic module */
+#undef COMPILE_DL_XMLRPC
+
diff --git a/ext/xmlrpc/php_xmlrpc.h b/ext/xmlrpc/php_xmlrpc.h
new file mode 100644 (file)
index 0000000..77fbd57
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+  This file is part of, or distributed with, libXMLRPC - a C library for 
+  xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* auto-generated portions of this file are also subject to the php license */
+
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors:                                                             |
+   |                                                                      |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef _PHP_XMLRPC_H
+#define _PHP_XMLRPC_H
+
+/* You should tweak config.m4 so this symbol (or some else suitable)
+   gets defined.
+*/
+#if 1 /* HAVE_XMLRPC */
+
+extern zend_module_entry xmlrpc_module_entry;
+#define phpext_xmlrpc_ptr &xmlrpc_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_XMLRPC_API __declspec(dllexport)
+#else
+#define PHP_XMLRPC_API
+#endif
+
+PHP_MINIT_FUNCTION(xmlrpc);
+PHP_MSHUTDOWN_FUNCTION(xmlrpc);
+PHP_RINIT_FUNCTION(xmlrpc);
+PHP_RSHUTDOWN_FUNCTION(xmlrpc);
+PHP_MINFO_FUNCTION(xmlrpc);
+
+PHP_FUNCTION(xmlrpc_encode);
+PHP_FUNCTION(xmlrpc_decode);
+PHP_FUNCTION(xmlrpc_decode_request);
+PHP_FUNCTION(xmlrpc_encode_request);
+PHP_FUNCTION(xmlrpc_get_type);
+PHP_FUNCTION(xmlrpc_set_type);
+PHP_FUNCTION(xmlrpc_server_create);
+PHP_FUNCTION(xmlrpc_server_destroy);
+PHP_FUNCTION(xmlrpc_server_register_method);
+PHP_FUNCTION(xmlrpc_server_call_method);
+PHP_FUNCTION(xmlrpc_parse_method_descriptions);
+PHP_FUNCTION(xmlrpc_server_add_introspection_data);
+PHP_FUNCTION(xmlrpc_server_register_introspection_callback);
+
+/* Fill in this structure and use entries in it
+   for thread safety instead of using true globals.
+*/
+typedef struct {
+       /* You can use the next one as type if your module registers any
+          resources. Oh, you can of course rename it to something more
+          suitable, add list entry types or remove it if it not needed.
+          It's just an example.
+       */
+        int le_xmlrpc_server;
+} php_xmlrpc_globals;
+
+/* In every function that needs to use variables in php_xmlrpc_globals,
+   do call XMLRPCLS_FETCH(); after declaring other variables used by
+   that function, and always refer to them as XMLRPCG(variable).
+   You are encouraged to rename these macros something shorter, see
+   examples in any other php module directory.
+*/
+
+#ifdef ZTS
+#define XMLRPCG(v) (xmlrpc_globals->v)
+#define XMLRPCLS_FETCH() php_xmlrpc_globals *xmlrpc_globals = ts_resource(gd_xmlrpc_id)
+#else
+#define XMLRPCG(v) (xmlrpc_globals.v)
+#define XMLRPCLS_FETCH()
+#endif
+
+#else
+
+#define phpext_xmlrpc_ptr NULL
+
+#endif
+
+#endif /* _PHP_XMLRPC_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c
new file mode 100644 (file)
index 0000000..589adaa
--- /dev/null
@@ -0,0 +1,1421 @@
+/*
+  This file is part of, or distributed with, libXMLRPC - a C library for 
+  xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2001 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+/* auto-generated portions of this file are also subject to the php license */
+
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors:                                                             |
+   |                                                                      |
+   +----------------------------------------------------------------------+
+ */
+
+#include "php.h"
+#include "php_ini.h"
+#include "php_xmlrpc.h"
+#include "xmlrpc.h"
+
+/* You should tweak config.m4 so this symbol (or some else suitable)
+   gets defined.
+*/
+
+#ifdef ZTS
+int xmlrpc_globals_id;
+#else
+php_xmlrpc_globals xmlrpc_globals;
+#endif
+
+
+/* Every user visible function must have an entry in xmlrpc_functions[].
+*/
+function_entry xmlrpc_functions[] = {
+   PHP_FE(xmlrpc_encode,    NULL) 
+   PHP_FE(xmlrpc_decode,    NULL)
+   PHP_FE(xmlrpc_decode_request, NULL)
+   PHP_FE(xmlrpc_encode_request, NULL)
+   PHP_FE(xmlrpc_get_type,    NULL)
+   PHP_FE(xmlrpc_set_type,    NULL)
+   PHP_FE(xmlrpc_server_create, NULL)
+   PHP_FE(xmlrpc_server_destroy, NULL)
+   PHP_FE(xmlrpc_server_register_method, NULL)
+   PHP_FE(xmlrpc_server_call_method, NULL)
+   PHP_FE(xmlrpc_parse_method_descriptions, NULL)
+   PHP_FE(xmlrpc_server_add_introspection_data, NULL)
+   PHP_FE(xmlrpc_server_register_introspection_callback, NULL)
+   {NULL, NULL, NULL}      /* Must be the last line in xmlrpc_functions[] */
+};
+
+zend_module_entry xmlrpc_module_entry = {
+   "xmlrpc",
+   xmlrpc_functions,
+   PHP_MINIT(xmlrpc),
+   PHP_MSHUTDOWN(xmlrpc),
+   PHP_RINIT(xmlrpc),      /* Replace with NULL if there's nothing to do at request start */
+   PHP_RSHUTDOWN(xmlrpc),  /* Replace with NULL if there's nothing to do at request end */
+   PHP_MINFO(xmlrpc),
+   STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_XMLRPC
+ZEND_GET_MODULE(xmlrpc)
+#endif
+
+/* Remove comments and fill if you need to have entries in php.ini
+PHP_INI_BEGIN()
+PHP_INI_END()
+*/
+
+/*******************************
+* local structures and defines *
+*******************************/
+
+// per server data
+typedef struct _xmlrpc_server_data {
+   pval* method_map;
+   pval* introspection_map;
+   XMLRPC_SERVER server_ptr;
+} xmlrpc_server_data;
+
+
+// how to format output
+typedef struct _php_output_options {
+   int b_php_out;
+   STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
+} php_output_options;
+
+// data passed to C callback
+typedef struct _xmlrpc_callback_data {
+   pval* xmlrpc_method;
+   pval* php_function;
+   pval* caller_params;
+   pval* return_data;
+   xmlrpc_server_data* server;
+   char php_executed;
+} xmlrpc_callback_data;
+
+#define PHP_EXT_VERSION "0.41"
+
+// output options
+#define OUTPUT_TYPE_KEY "output_type"
+#define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
+#define OUTPUT_TYPE_VALUE_PHP "php"
+#define OUTPUT_TYPE_VALUE_XML "xml"
+
+#define VERBOSITY_KEY "verbosity"
+#define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
+#define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
+#define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
+#define VERBOSITY_VALUE_PRETTY "pretty"
+
+#define ESCAPING_KEY "escaping"
+#define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
+#define ESCAPING_VALUE_CDATA "cdata"
+#define ESCAPING_VALUE_NON_ASCII "non-ascii"
+#define ESCAPING_VALUE_NON_PRINT "non-print"
+#define ESCAPING_VALUE_MARKUP "markup"
+
+#define VERSION_KEY "version"
+#define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
+#define VERSION_VALUE_SIMPLE "simple"
+#define VERSION_VALUE_XMLRPC "xmlrpc"
+
+#define ENCODING_KEY "encoding"
+#define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
+#define ENCODING_DEFAULT "iso-8859-1"
+
+// value types
+#define OBJECT_TYPE_ATTR "xmlrpc_type"
+#define OBJECT_VALUE_ATTR "scalar"
+
+
+
+/***********************
+* forward declarations *
+***********************/
+XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue);
+static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
+
+/*********************
+* startup / shutdown *
+*********************/
+
+static void destroy_server_data(xmlrpc_server_data *server) {
+   if(server) {
+      XMLRPC_ServerDestroy(server->server_ptr);
+
+      zval_dtor(server->method_map);
+      FREE_ZVAL(server->method_map);
+
+      zval_dtor(server->introspection_map);
+      FREE_ZVAL(server->introspection_map);
+
+      efree(server);
+   }
+}
+
+/* called when server is being destructed. either when xmlrpc_server_destroy
+ * is called, or when request ends.
+ */
+static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc) {
+   if(rsrc && rsrc->ptr) {
+      destroy_server_data((xmlrpc_server_data*)rsrc->ptr);
+   }
+}
+
+/* module init */
+PHP_MINIT_FUNCTION(xmlrpc)
+{
+/* Remove comments if you have entries in php.ini
+        REGISTER_INI_ENTRIES();
+*/
+   XMLRPCG(le_xmlrpc_server) = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
+
+   return SUCCESS;
+}
+
+/* module shutdown */
+PHP_MSHUTDOWN_FUNCTION(xmlrpc)
+{
+/* Remove comments if you have entries in php.ini
+        UNREGISTER_INI_ENTRIES();
+*/
+   return SUCCESS;
+}
+
+/* Remove if there's nothing to do at request start */
+PHP_RINIT_FUNCTION(xmlrpc)
+{
+   return SUCCESS;
+}
+
+/* Remove if there's nothing to do at request end */
+PHP_RSHUTDOWN_FUNCTION(xmlrpc)
+{
+   return SUCCESS;
+}
+
+/* display info in phpinfo() */
+PHP_MINFO_FUNCTION(xmlrpc)
+{
+   php_info_print_table_start();
+   php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
+   php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
+   php_info_print_table_row(2, "author", "Dan Libby");
+   php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
+   php_info_print_table_row(2, "open sourced by", "Epinions.com");
+   php_info_print_table_end();
+   
+       /*
+   DISPLAY_INI_ENTRIES();
+       */
+}
+
+/*******************
+* random utilities *
+*******************/
+
+/* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
+ * Could easily be further generalized to work with objects.
+ */
+static int add_long(pval* list, char* id, int num) {
+   if(id) return add_assoc_long(list, id, num);
+   else   return add_next_index_long(list, num);
+}
+
+static int add_double(pval* list, char* id, double num) {
+   if(id) return add_assoc_double(list, id, num);
+   else   return add_next_index_double(list, num);
+}
+
+static int add_string(pval* list, char* id, char* string, int duplicate) {
+   if(id) return add_assoc_string(list, id, string, duplicate);
+   else   return add_next_index_string(list, string, duplicate);
+}
+
+static int add_stringl(pval* list, char* id, char* string, uint length, int duplicate) {
+   if(id) return add_assoc_stringl(list, id, string, length, duplicate);
+   else   return add_next_index_stringl(list, string, length, duplicate);
+}
+
+static int add_pval(pval* list, const char* id, pval** val) {
+   if(list && val) {
+      if(id) return zend_hash_update(list->value.ht, (char*)id, strlen(id)+1, (void *) val, sizeof(pval **), NULL);
+      else   return zend_hash_next_index_insert(list->value.ht, (void *) val, sizeof(pval **), NULL); 
+   }
+}
+
+#if ZEND_MODULE_API_NO >= 20001222
+#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
+#else
+#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index)
+#endif 
+
+
+/*************************
+* input / output options *
+*************************/
+
+/* parse an array (user input) into output options suitable for use by xmlrpc engine
+ * and determine whether to return data as xml or php vars
+ */
+static void set_output_options(php_output_options* options, pval* output_opts) {
+
+   if(options) {
+
+      /* defaults */
+      options->b_php_out = 0;
+      options->xmlrpc_out.version = xmlrpc_version_1_0;
+      options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
+      options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
+      options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
+
+     if(output_opts && output_opts->type == IS_ARRAY) {
+        pval** val;
+
+        /* verbosity of generated xml */
+        if(zend_hash_find(output_opts->value.ht, 
+                          OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_PHP)) {
+                 options->b_php_out = 1;
+              }
+              else if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_XML)) {
+                 options->b_php_out = 0;
+              }
+           }
+        }
+
+        /* verbosity of generated xml */
+        if(zend_hash_find(output_opts->value.ht, 
+                          VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NO_WHITE_SPACE)) {
+                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
+              }
+              else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NEWLINES_ONLY)) {
+                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
+              }
+              else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_PRETTY)) {
+                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
+              }
+           }
+        }
+
+        /* version of xml to output */
+        if(zend_hash_find(output_opts->value.ht, 
+                          VERSION_KEY, VERSION_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, VERSION_VALUE_XMLRPC)) {
+                 options->xmlrpc_out.version = xmlrpc_version_1_0;
+              }
+              else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SIMPLE)) {
+                 options->xmlrpc_out.version = xmlrpc_version_simple;
+              }
+           }
+        }
+
+        /* encoding code set */
+        if(zend_hash_find(output_opts->value.ht, 
+                          ENCODING_KEY, ENCODING_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           if((*val)->type == IS_STRING) {
+              options->xmlrpc_out.xml_elem_opts.encoding = estrdup((*val)->value.str.val);
+           }
+        }
+
+        /* escaping options */
+        if(zend_hash_find(output_opts->value.ht, 
+                          ESCAPING_KEY, ESCAPING_KEY_LEN + 1, 
+                          (void**)&val) == SUCCESS) {
+           /* multiple values allowed.  check if array */
+           if((*val)->type == IS_ARRAY) {
+              pval** iter_val;
+              zend_hash_internal_pointer_reset((*val)->value.ht);
+              options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
+              while(1) {
+                 if(zend_hash_get_current_data((*val)->value.ht, (void**)&iter_val) == SUCCESS) {
+                    if((*iter_val)->type == IS_STRING && (*iter_val)->value.str.val) {
+                       if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_CDATA)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
+                       }
+                       else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
+                       }
+                       else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
+                       }
+                       else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_MARKUP)) {
+                          options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
+                       }
+                    }
+                 }
+                 else {
+                    break;
+                 }
+
+                 zend_hash_move_forward((*val)->value.ht);
+              }
+           }
+           /* else, check for single value */
+           else if((*val)->type == IS_STRING) {
+              if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_CDATA)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
+              }
+              else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
+              }
+              else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
+              }
+              else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_MARKUP)) {
+                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
+              }
+           }
+        }
+     }
+   }
+}
+
+
+/******************
+* encode / decode *
+******************/
+
+/* php arrays have no distinction between array and struct types.
+ * they even allow mixed.  Thus, we determine the type by iterating
+ * through the entire array and figuring out each element.
+ * room for some optimation here if we stop after a specific # of elements.
+ */
+static XMLRPC_VECTOR_TYPE determine_vector_type(HashTable *ht) {
+    int bArray = 0, bStruct = 0, bMixed = 0;
+    unsigned long num_index;
+    char* my_key;
+
+    zend_hash_internal_pointer_reset(ht);
+    while(1) {
+       int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
+       if(res == HASH_KEY_IS_LONG) {
+           if(bStruct) {
+               bMixed = 1;
+               break;
+           }
+           bArray = 1;
+       }
+       else if(res == HASH_KEY_NON_EXISTANT) {
+          break;
+       }
+       else if(res == HASH_KEY_IS_STRING) {
+           if(bArray) {
+               bMixed = 1;
+               break;
+           }
+           bStruct = 1;
+       }
+
+       zend_hash_move_forward(ht);
+    }
+    return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
+}
+
+/* recursively convert php values into xmlrpc values */
+static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int depth) {
+   XMLRPC_VALUE xReturn = NULL;
+   if(in_val) {
+      pval* val = NULL;
+      XMLRPC_VALUE_TYPE type = get_pval_xmlrpc_type(in_val, &val);
+      if(val) {
+         switch(type) {
+            case xmlrpc_base64:
+               if(val->type == IS_NULL) {
+                  xReturn = XMLRPC_CreateValueBase64(key, "", 1);
+               }
+               else {
+                  xReturn = XMLRPC_CreateValueBase64(key, val->value.str.val, val->value.str.len);
+               }
+               break;
+            case xmlrpc_datetime:
+               convert_to_string(val);
+               xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, val->value.str.val);
+               break;
+            case xmlrpc_boolean:
+               convert_to_boolean(val);
+               xReturn = XMLRPC_CreateValueBoolean(key, val->value.lval);
+               break;
+            case xmlrpc_int:
+               convert_to_long(val);
+               xReturn = XMLRPC_CreateValueInt(key, val->value.lval);
+               break;
+            case xmlrpc_double:
+               convert_to_double(val);
+               xReturn = XMLRPC_CreateValueDouble(key, val->value.dval);
+               break;
+            case xmlrpc_string:
+               convert_to_string(val);
+               xReturn = XMLRPC_CreateValueString(key, val->value.str.val, val->value.str.len);
+               break;
+            case xmlrpc_vector:
+               {
+                  unsigned long num_index;
+                  pval** pIter;
+                  char* my_key;
+
+                  convert_to_array(val);
+
+                  xReturn = XMLRPC_CreateVector(key, determine_vector_type(val->value.ht));
+
+                  zend_hash_internal_pointer_reset(val->value.ht);
+                  while(1) {
+                     int res = my_zend_hash_get_current_key(val->value.ht, &my_key, &num_index);
+                     if(res == HASH_KEY_IS_LONG) {
+                        if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) {
+                           XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++));
+                        }
+                     }
+                     else if(res == HASH_KEY_NON_EXISTANT) {
+                        break;
+                     }
+                     else if(res == HASH_KEY_IS_STRING) {
+                        if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) {
+                           XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++));
+                        }
+                     }
+
+                     zend_hash_move_forward(val->value.ht);
+                  }
+               }
+               break;
+            default:
+               break;
+         }
+      }
+   }
+   return xReturn;
+}
+
+static XMLRPC_VALUE PHP_to_XMLRPC(pval* root_val) {
+   return PHP_to_XMLRPC_worker(NULL, root_val, 0);
+}
+
+/* recursively convert xmlrpc values into php values */
+static pval* XMLRPC_to_PHP(XMLRPC_VALUE el) {
+   pval* elem = NULL;
+   char* pBuf;
+   const char* pStr;
+
+   if(el) {
+      XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
+
+      MAKE_STD_ZVAL(elem); /* init. very important.  spent a frustrating day finding this out. */
+
+      switch(type) {
+         case xmlrpc_empty:
+            elem->type = IS_NULL;
+            break;
+         case xmlrpc_string:
+            pStr = XMLRPC_GetValueString(el);
+            if(pStr) {
+               elem->value.str.len = XMLRPC_GetValueStringLen(el);
+               elem->value.str.val = estrndup(pStr, elem->value.str.len);
+               elem->type = IS_STRING;
+            }
+            break;
+         case xmlrpc_int:
+            elem->value.lval = XMLRPC_GetValueInt(el);
+            elem->type = IS_LONG;
+            break;
+         case xmlrpc_boolean:
+            elem->value.lval = XMLRPC_GetValueBoolean(el);
+            elem->type = IS_BOOL;
+            break;
+         case xmlrpc_double:
+            elem->value.dval = XMLRPC_GetValueDouble(el);
+            elem->type = IS_DOUBLE;
+            break;
+         case xmlrpc_datetime:
+            elem->value.str.len = XMLRPC_GetValueStringLen(el);
+            elem->value.str.val = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), elem->value.str.len);
+            elem->type = IS_STRING;
+            break;
+         case xmlrpc_base64:
+            pStr = XMLRPC_GetValueBase64(el);
+            if(pStr) {
+               elem->value.str.len = XMLRPC_GetValueStringLen(el);
+               elem->value.str.val = estrndup(pStr, elem->value.str.len);
+               elem->type = IS_STRING;
+            }
+            break;
+         case xmlrpc_vector:
+            if(array_init(elem) == SUCCESS) {
+               XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
+
+               while( xIter ) {
+                  pval* val = XMLRPC_to_PHP(xIter);
+                  if(val) {
+                     add_pval(elem, XMLRPC_GetValueID(xIter), &val);
+                  }
+                  xIter = XMLRPC_VectorNext(el);
+               }
+            }
+            break;
+         default:
+            break;
+      }
+      set_pval_xmlrpc_type(elem, type);
+   }
+   return elem;
+}
+
+/* {{{ proto string xmlrpc_encode_request(string method, mixed params)
+   generate xml for a method request */
+PHP_FUNCTION(xmlrpc_encode_request) {
+   XMLRPC_REQUEST xRequest = NULL;
+   pval* method, *vals, *out_opts;
+   char* outBuf;
+   php_output_options out;
+
+   if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || 
+       getParameters(ht, ARG_COUNT(ht), &method, &vals, &out_opts) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0);
+
+   if(return_value_used) {
+      xRequest = XMLRPC_RequestNew();
+
+      if(xRequest) {
+         XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
+         if(method->type == IS_NULL) {
+            XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
+         }
+         else {
+            XMLRPC_RequestSetMethodName(xRequest, method->value.str.val);
+            XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
+         }
+         if(vals->type != IS_NULL) {
+             XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals));
+         }
+
+         outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
+         if(outBuf) {
+            RETVAL_STRING(outBuf, 1);
+            free(outBuf);
+         }
+         XMLRPC_RequestFree(xRequest, 1);
+      }
+   }
+}
+
+/* {{{ proto string xmlrpc_encode(mixed value)
+   generate xml for a PHP value */
+PHP_FUNCTION(xmlrpc_encode)
+{
+   XMLRPC_VALUE xOut = NULL;
+   pval* arg1, *out_opts;
+   php_output_options out;
+   char* outBuf;
+
+   if( !(ARG_COUNT(ht) == 1)  || 
+       getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   if( return_value_used ) {
+      /* convert native php type to xmlrpc type */
+      xOut = PHP_to_XMLRPC(arg1);
+
+      /* generate raw xml from xmlrpc data */
+      outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
+
+      if(xOut) {
+         if(outBuf) {
+            RETVAL_STRING(outBuf, 1);
+            free(outBuf);
+         }
+         /* cleanup */
+         XMLRPC_CleanupValue(xOut);
+      }
+   }
+}
+
+
+/* }}} */
+
+pval* decode_request_worker(pval* xml_in, pval* encoding_in, pval* method_name_out) {
+   pval* retval = NULL;
+   XMLRPC_REQUEST response;
+   STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {0};
+   opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in->value.str.val) : ENCODING_DEFAULT;
+
+   /* generate XMLRPC_REQUEST from raw xml */
+   response = XMLRPC_REQUEST_FromXML(xml_in->value.str.val, xml_in->value.str.len, &opts);
+   if(response) {
+      /* convert xmlrpc data to native php types */
+      retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
+
+      if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
+         if(method_name_out) {
+            convert_to_string(method_name_out);
+            method_name_out->type = IS_STRING;
+            method_name_out->value.str.val = estrdup(XMLRPC_RequestGetMethodName(response));
+            method_name_out->value.str.len = strlen(method_name_out->value.str.val);
+         }
+      }
+
+      /* dust, sweep, and mop */
+      XMLRPC_RequestFree(response, 1);
+   }
+   return retval;
+}
+
+/* {{{ proto array xmlrpc_decode_request(string xml, string& method, [string encoding])
+   decode xml into native php types */
+PHP_FUNCTION(xmlrpc_decode_request)
+{
+   pval* xml, *method, *encoding = NULL;
+
+   if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &xml, &method, &encoding) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+#if ZEND_MODULE_API_NO < 20010901
+   if (!ParameterPassedByReference(ht,2)) {
+       zend_error(E_WARNING,"second argument to xmlrpc_decode_request() passed by value, expecting reference");
+   }
+#endif
+
+   convert_to_string(xml);
+   convert_to_string(method);
+   if(ARG_COUNT(ht) == 3) {
+      convert_to_string(encoding);
+   }
+
+   if(return_value_used) {
+      pval* retval = decode_request_worker(xml, encoding, method);
+      if(retval) {
+         *return_value = *retval;
+         zval_copy_ctor(return_value);
+      }
+   }
+}
+/* }}} */
+
+
+/* {{{ proto array xmlrpc_decode(string xml, [string encoding])
+   decode xml into native php types */
+PHP_FUNCTION(xmlrpc_decode)
+{
+   pval* arg1, *arg2 = NULL;
+
+   if( !(ARG_COUNT(ht) == 1 || ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg1, &arg2) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   convert_to_string(arg1);
+   if(ARG_COUNT(ht) == 2) {
+      convert_to_string(arg2);
+   }
+
+   if(return_value_used) {
+      pval* retval = decode_request_worker(arg1, arg2, NULL);
+      if(retval) {
+         *return_value = *retval;
+         zval_copy_ctor(return_value);
+      }
+   }
+}
+/* }}} */
+
+
+/*************************
+* server related methods *
+*************************/
+
+/* {{{ proto handle xmlrpc_server_create()
+   create an xmlrpc server */
+PHP_FUNCTION(xmlrpc_server_create) {
+   if(ARG_COUNT(ht) != 0) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   if(return_value_used) {
+      pval *method_map, *introspection_map;
+      MAKE_STD_ZVAL(method_map);
+      MAKE_STD_ZVAL(introspection_map);
+
+      if(array_init(method_map) == SUCCESS && array_init(introspection_map) == SUCCESS) {
+         /* allocate server data.  free'd in destroy_server_data() */
+         xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
+
+         if(server) {
+            server->method_map = method_map;
+            server->introspection_map = introspection_map;
+            server->server_ptr = XMLRPC_ServerCreate();
+
+            XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
+
+            /* store for later use */
+            ZEND_REGISTER_RESOURCE(return_value,server, XMLRPCG(le_xmlrpc_server));
+         }
+      }
+   }
+}
+
+/* {{{ proto void xmlrpc_server_destroy(handle server)
+   destroy server resources */
+PHP_FUNCTION(xmlrpc_server_destroy) {
+   pval* arg1;
+   int bSuccess = FAILURE;
+
+   if(ARG_COUNT(ht) != 1 || getParameters(ht, 1, &arg1) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   if(arg1->type == IS_RESOURCE) {
+      int type;
+
+      xmlrpc_server_data *server = zend_list_find(arg1->value.lval, &type);
+
+      if(server && type == XMLRPCG(le_xmlrpc_server)) {
+         bSuccess = zend_list_delete(arg1->value.lval);
+
+         /* called by hashtable destructor
+          * destroy_server_data(server);
+          */
+      }
+   }
+   RETVAL_LONG(bSuccess == SUCCESS);
+}
+
+           
+/* called by xmlrpc C engine as method handler for all registered methods.
+ * it then calls the corresponding PHP function to handle the method.
+ */
+static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) {
+   pval *retval_ptr;
+   xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
+   pval* xmlrpc_params;
+   pval* callback_params[3];
+
+   /* convert xmlrpc to native php types */
+   xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
+
+   /* setup data hoojum */
+   callback_params[0] = pData->xmlrpc_method;
+   callback_params[1] = xmlrpc_params;
+   callback_params[2] = pData->caller_params;
+
+   /* Use same C function for all methods */
+
+   /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
+   call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params);
+
+   pData->php_executed = 1;
+}
+
+/* called by the C server when it first receives an introspection request.  We pass this on to
+ * our PHP listeners, if any
+ */
+static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) {
+   pval *retval_ptr, **php_function;
+   pval* callback_params[1];
+   xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
+
+   MAKE_STD_ZVAL(retval_ptr);
+   retval_ptr->type = IS_NULL;
+
+   /* setup data hoojum */
+   callback_params[0] = pData->caller_params;
+
+   /* loop through and call all registered callbacks */
+   zend_hash_internal_pointer_reset(pData->server->introspection_map->value.ht);
+   while(1) {
+      if(zend_hash_get_current_data(pData->server->introspection_map->value.ht, 
+                                    (void**)&php_function) == SUCCESS) {
+
+         /* php func prototype: function string user_func($user_params) */
+         if(call_user_function(CG(function_table), NULL, *php_function, 
+                               retval_ptr, 1, callback_params) == SUCCESS) {
+            XMLRPC_VALUE xData;
+            STRUCT_XMLRPC_ERROR err = {0};
+
+            /* return value should be a string */
+            convert_to_string(retval_ptr);
+
+            xData = XMLRPC_IntrospectionCreateDescription(retval_ptr->value.str.val, &err);
+
+            if(xData) {
+               if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
+                  zend_error(E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", (*php_function)->value.str.val);
+               }
+               XMLRPC_CleanupValue(xData);
+            }
+            else {
+               /* could not create description */
+               if(err.xml_elem_error.parser_code) {
+                  zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to add introspection data returned from %s()", 
+                             err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, (*php_function)->value.str.val);
+               }
+               else {
+                  zend_error(E_WARNING, "Unable to add introspection data returned from %s()", 
+                             (*php_function)->value.str.val);
+               }
+            }
+         }
+         else {
+            /* user func failed */
+            zend_error(E_WARNING, "Error calling user introspection callback: %s()", (*php_function)->value.str.val);
+         }
+      }
+      else {
+         break;
+      }
+
+      zend_hash_move_forward(pData->server->introspection_map->value.ht);
+   }
+
+   /* so we don't call the same callbacks ever again */
+   zend_hash_clean(pData->server->introspection_map->value.ht);
+}
+
+/* {{{ proto boolean xmlrpc_server_register_method(handle server, string method_name, string function)
+   register a php function to handle method matching method_name */
+PHP_FUNCTION(xmlrpc_server_register_method) {
+
+   pval* method_key, *method_name, *handle, *method_name_save;
+   int type;
+   xmlrpc_server_data* server;
+
+   /* get some params.  should be 3 */
+   if( !(ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &handle, &method_key, &method_name) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if(type == XMLRPCG(le_xmlrpc_server)) {
+      /* register with C engine. every method just calls our standard callback, 
+       * and it then dispatches to php as necessary
+       */
+      if(XMLRPC_ServerRegisterMethod(server->server_ptr, method_key->value.str.val, php_xmlrpc_callback)) {
+         /* save for later use */
+         MAKE_STD_ZVAL(method_name_save);
+         *method_name_save = *method_name;
+         zval_copy_ctor(method_name_save);
+
+         /* register our php method */
+         add_pval(server->method_map, method_key->value.str.val, &method_name_save);
+
+         RETURN_BOOL(1);
+      }
+   }
+   RETURN_BOOL(0);
+}
+
+
+/* {{{ proto boolean xmlrpc_server_register_introspection_callback(handle server, string function)
+   register a php function to generate documentation */
+PHP_FUNCTION(xmlrpc_server_register_introspection_callback) {
+
+   pval* method_key, *method_name, *handle, *method_name_save;
+   int type;
+   xmlrpc_server_data* server;
+
+   /* get some params.  should be 2 */
+   if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &method_name) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if(type == XMLRPCG(le_xmlrpc_server)) {
+      {
+         /* save for later use */
+         MAKE_STD_ZVAL(method_name_save);
+         *method_name_save = *method_name;
+         zval_copy_ctor(method_name_save);
+
+         /* register our php method */
+         add_pval(server->introspection_map, NULL, &method_name_save);
+
+         RETURN_BOOL(1);
+      }
+   }
+   RETURN_BOOL(0);
+}
+
+
+/* this function is itchin for a re-write */
+
+/* {{{ proto mixed xmlrpc_server_call_method(handle server, string xml, mixed user_data, [array output_options])
+   parse xml request and call method */
+PHP_FUNCTION(xmlrpc_server_call_method) {
+   xmlrpc_callback_data data = {0};
+   XMLRPC_REQUEST xRequest;
+   STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
+   xmlrpc_server_data* server;
+   pval *rawxml, *caller_params, *handle, *output_opts;
+   int type;
+   php_output_options out;
+
+   /* get params. 3 or 4 params ok */
+   if(ARG_COUNT(ht) == 4) {
+      if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params, &output_opts) != SUCCESS) {
+         WRONG_PARAM_COUNT;
+      }
+
+      /* user output options */
+      set_output_options(&out, output_opts);
+   }
+   else if(ARG_COUNT(ht) == 3) {
+      if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params) != SUCCESS) {
+         WRONG_PARAM_COUNT;
+      }
+      /* user output options */
+      set_output_options(&out, NULL);
+   }
+   else {
+      WRONG_PARAM_COUNT;
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if(type == XMLRPCG(le_xmlrpc_server)) {
+      /* HACK: use output encoding for now */
+      input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
+
+      /* generate an XMLRPC_REQUEST from the raw xml input */
+      xRequest = XMLRPC_REQUEST_FromXML(rawxml->value.str.val, rawxml->value.str.len, &input_opts);
+
+      if(xRequest) {
+
+         /* check if we have a method name -- indicating success and all manner of good things */
+         if(XMLRPC_RequestGetMethodName(xRequest)) {
+            pval** php_function, *returned = NULL;
+            XMLRPC_VALUE xAnswer = NULL;
+            MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important.  spent a frustrating day finding this out. */
+            MAKE_STD_ZVAL(data.return_data);
+            data.return_data->type = IS_NULL;  /* in case value is never init'd, we don't dtor to think it is a string or something */
+            data.xmlrpc_method->type = IS_NULL;
+
+            /* setup some data to pass to the callback function */
+            data.xmlrpc_method->value.str.val = estrdup(XMLRPC_RequestGetMethodName(xRequest));
+            data.xmlrpc_method->value.str.len = strlen(data.xmlrpc_method->value.str.val);
+            data.xmlrpc_method->type = IS_STRING;
+            data.caller_params = caller_params;
+            data.php_executed = 0;
+            data.server = server;
+
+            /* check if the called method has been previous registered */
+            if(zend_hash_find(server->method_map->value.ht, 
+                              data.xmlrpc_method->value.str.val, 
+                              data.xmlrpc_method->value.str.len + 1, 
+                              (void**)&php_function) == SUCCESS) {
+
+               data.php_function = *php_function;
+            }
+
+           /* We could just call the php method directly ourselves at this point, but we do this 
+            * with a C callback in case the xmlrpc library ever implements some cool usage stats,
+            * or somesuch.
+            */
+           xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
+           if(xAnswer) {
+               if(out.b_php_out) {
+                  zval_dtor(data.return_data);
+                  FREE_ZVAL(data.return_data);
+                  data.return_data = XMLRPC_to_PHP(xAnswer);
+               }
+           }
+           else if(data.php_executed) {
+               if(!out.b_php_out) {
+                   xAnswer = PHP_to_XMLRPC(data.return_data);
+               }
+           }
+
+           /* should we return data as xml? */
+           if(!out.b_php_out) {
+              XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
+              if(xResponse) {
+                 char* outBuf = 0;
+                 int buf_len = 0;
+
+                 /* set some required request hoojum */
+                 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
+                 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
+                 XMLRPC_RequestSetData(xResponse, xAnswer);
+
+                 /* generate xml */
+                 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
+                 if(outBuf) {
+                    RETVAL_STRINGL(outBuf, buf_len, 1);
+                    free(outBuf);
+                 }
+                 /* cleanup after ourselves.  what a sty! */
+                 XMLRPC_RequestFree(xResponse, 0);
+              }
+           }
+           /* or as native php types? */
+           else {
+              *return_value = *data.return_data;
+              zval_copy_ctor(return_value);
+           }
+
+            /* cleanup after ourselves.  what a sty! */
+            zval_dtor(data.xmlrpc_method);
+            FREE_ZVAL(data.xmlrpc_method);
+            zval_dtor(data.return_data);
+            FREE_ZVAL(data.return_data);
+
+            if(xAnswer) {
+               XMLRPC_CleanupValue(xAnswer);
+            }
+         }
+
+         XMLRPC_RequestFree(xRequest, 1);
+      }
+   }
+}
+
+
+/* {{{ proto int xmlrpc_server_add_introspection_data(handle server, array desc)
+   add introspection documentation  */
+PHP_FUNCTION(xmlrpc_server_add_introspection_data) {
+
+   pval *method, *handle, *desc;
+   int type;
+   xmlrpc_server_data* server;
+
+   /* get some params.  should be 2 */
+   if ( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &desc) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   server = zend_list_find(handle->value.lval, &type);
+
+   if (type == XMLRPCG(le_xmlrpc_server)) {
+      XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc);
+      if (xDesc) {
+         int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
+         XMLRPC_CleanupValue(xDesc);
+         RETURN_LONG(retval);
+      }
+   }
+   RETURN_LONG(0);
+}
+
+
+/* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
+   decode xml into a list of method descriptions */
+PHP_FUNCTION(xmlrpc_parse_method_descriptions)
+{
+   pval* arg1, *arg2, *retval;
+
+   if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   convert_to_string(arg1);
+
+   if(return_value_used) {
+      STRUCT_XMLRPC_ERROR err = {0};
+      XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1->value.str.val, &err);
+      if(xVal) {
+         retval = XMLRPC_to_PHP(xVal);
+
+         if(retval) {
+            *return_value = *retval;
+            zval_copy_ctor(return_value);
+         }
+         /* dust, sweep, and mop */
+         XMLRPC_CleanupValue(xVal);
+      }
+      else {
+         /* could not create description */
+         if(err.xml_elem_error.parser_code) {
+            zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to create introspection data", 
+                       err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
+         }
+         else {
+            zend_error(E_WARNING, "Invalid xml structure. Unable to create introspection data");
+         }
+
+         zend_error(E_WARNING, "xml parse error.  no method description created");
+      }
+   }
+}
+
+
+/************
+* type data *
+************/
+
+#define XMLRPC_TYPE_COUNT 9
+#define XMLRPC_VECTOR_TYPE_COUNT 4
+#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
+
+/* return a string matching a given xmlrpc type */
+static const char** get_type_str_mapping() {
+   static const char* str_mapping[TYPE_STR_MAP_SIZE];
+   static int first = 1;
+   if(first) {
+      /* warning. do not add/delete without changing size define */
+      str_mapping[xmlrpc_none] = "none";
+      str_mapping[xmlrpc_empty] = "empty";
+      str_mapping[xmlrpc_base64] = "base64";
+      str_mapping[xmlrpc_boolean] = "boolean";
+      str_mapping[xmlrpc_datetime] = "datetime";
+      str_mapping[xmlrpc_double] = "double";
+      str_mapping[xmlrpc_int] = "int";
+      str_mapping[xmlrpc_string] = "string";
+      str_mapping[xmlrpc_vector] = "vector";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
+      str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
+      first = 0;
+   }
+   return (const char**)str_mapping;
+}
+
+/* map an xmlrpc type to a string */
+const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) {
+   const char** str_mapping = get_type_str_mapping();
+   if(vtype == xmlrpc_vector_none) {
+      return str_mapping[type];
+   }
+   else {
+      return str_mapping[XMLRPC_TYPE_COUNT + vtype];
+   }
+}
+
+/* map a string to an xmlrpc type */
+XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) {
+   const char** str_mapping = get_type_str_mapping();
+   int i;
+
+   if(str) {
+      for(i = 0; i < XMLRPC_TYPE_COUNT; i++) {
+         if(!strcmp(str_mapping[i], str)) {
+            return (XMLRPC_VALUE_TYPE)i;
+         }
+      }
+   }
+   return xmlrpc_none;
+}
+
+/* map a string to an xmlrpc vector type */
+XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) {
+   const char** str_mapping = get_type_str_mapping();
+   int i;
+
+   if(str) {
+      for(i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
+         if(!strcmp(str_mapping[i], str)) {
+            return (XMLRPC_VECTOR_TYPE)(i - XMLRPC_TYPE_COUNT);
+         }
+      }
+   }
+   return xmlrpc_none;
+}
+
+
+/* set a given value to a particular type. 
+ * note: this only works on strings, and only for date and base64,
+ *       which do not have native php types. black magic lies herein.
+ */
+int set_pval_xmlrpc_type(pval* value, XMLRPC_VALUE_TYPE type) {
+   int bSuccess = FAILURE;
+
+   /* we only really care about strings because they can represent
+    * base64 and datetime.  all other types have corresponding php types
+    */
+   if(value->type == IS_STRING) {
+      if(type == xmlrpc_base64 || type == xmlrpc_datetime) {
+         const char* typestr = xmlrpc_type_as_str(type, xmlrpc_vector_none);
+         pval* type;
+
+         MAKE_STD_ZVAL(type);
+
+         type->type = IS_STRING;
+         type->value.str.val = estrdup(typestr);
+         type->value.str.len = strlen(typestr);
+
+         convert_to_object(value);
+         bSuccess = zend_hash_update(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
+      }
+   }
+   
+   return bSuccess;
+}
+
+/* return xmlrpc type of a php value */
+XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue) {
+   XMLRPC_VALUE_TYPE type = xmlrpc_none;
+
+   if(value) {
+      switch(value->type) {
+         case IS_NULL:
+            type = xmlrpc_base64;
+            break;
+   #ifndef BOOL_AS_LONG
+
+   /* Right thing to do, but it breaks some legacy code. */
+         case IS_BOOL:
+            type = xmlrpc_boolean;
+            break;
+   #else
+         case IS_BOOL:
+   #endif
+         case IS_LONG:
+         case IS_RESOURCE:
+            type = xmlrpc_int;
+            break;
+         case IS_DOUBLE:
+            type = xmlrpc_double;
+            break;
+         case IS_CONSTANT:
+            type = xmlrpc_string;
+            break;
+         case IS_STRING:
+            type = xmlrpc_string;
+            break;
+         case IS_ARRAY:
+         case IS_CONSTANT_ARRAY:
+            type = xmlrpc_vector;
+            break;
+         case IS_OBJECT:
+         {
+            pval** attr;
+            type = xmlrpc_vector;
+
+            if(zend_hash_find(value->value.obj.properties, 
+                              OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), 
+                              (void**)&attr) == SUCCESS) {
+               if((*attr)->type == IS_STRING) {
+                  type = xmlrpc_str_as_type((*attr)->value.str.val);
+               }
+            }
+            break;
+         }
+      }
+
+      /* if requested, return an unmolested (magic removed) copy of the value */
+      if(newvalue) {
+         pval** val;
+         if( (type == xmlrpc_base64 && value->type != IS_NULL) || type == xmlrpc_datetime) {
+            if(zend_hash_find(value->value.obj.properties, 
+                           OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), 
+                           (void**)&val) == SUCCESS) {
+               *newvalue = *val;
+            }
+         }
+         else {
+            *newvalue = value;
+         }
+      }
+   }
+
+   return type;
+}
+
+
+/* {{{ proto bool xmlrpc_set_type(string value, string type)
+   set xmlrpc type, base64 or datetime, for a php string value */
+PHP_FUNCTION(xmlrpc_set_type) {
+   pval* arg, *type;
+   XMLRPC_VALUE_TYPE vtype;
+
+   if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg, &type) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+#if ZEND_MODULE_API_NO < 20010901
+   if (!ParameterPassedByReference(ht,1)) {
+       zend_error(E_WARNING,"first argument to xmlrpc_set_type() passed by value, expecting reference");
+   }
+#endif
+
+   convert_to_string(type);
+   vtype = xmlrpc_str_as_type(type->value.str.val);
+   if(vtype != xmlrpc_none) {
+      if(set_pval_xmlrpc_type(arg, vtype) == SUCCESS) {
+         RETURN_TRUE;
+      }
+   }
+   else {
+      zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type->value.str.val);
+   }
+   RETURN_FALSE;
+}
+
+/* {{{ proto string xmlrpc_get_type(mixed value)
+   get xmlrpc type for a php value. especially useful for base64 and datetime strings */
+PHP_FUNCTION(xmlrpc_get_type) {
+   pval* arg;
+   XMLRPC_VALUE_TYPE type;
+   XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
+
+   if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg) == FAILURE) {
+      WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
+   }
+
+   type = get_pval_xmlrpc_type(arg, 0);
+   if(type == xmlrpc_vector) {
+      vtype = determine_vector_type(arg->value.ht);
+   }
+   
+   RETURN_STRING((char*)xmlrpc_type_as_str(type, vtype), 1);
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+