Merge from trunk
[plcapi.git] / trunk / php / xmlrpc / libxmlrpc / xmlrpc.c
diff --git a/trunk/php/xmlrpc/libxmlrpc/xmlrpc.c b/trunk/php/xmlrpc/libxmlrpc/xmlrpc.c
new file mode 100644 (file)
index 0000000..d5f3f7b
--- /dev/null
@@ -0,0 +1,2963 @@
+/*
+  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
+ *   $Log: xmlrpc.c,v $
+ *   Revision 1.6  2004/04/27 17:33:59  iliaa
+ *   Removed C++ style comments.
+ *
+ *   Revision 1.5  2003/12/16 21:00:21  sniper
+ *   Fix some compile warnings (patch by Joe Orton)
+ *
+ *   Revision 1.4  2002/07/05 04:43:53  danda
+ *   merged in updates from SF project.  bring php repository up to date with xmlrpc-epi version 0.51
+ *
+ *   Revision 1.22  2002/03/09 23:15:44  danda
+ *   add fault interrogation funcs
+ *
+ *   Revision 1.21  2002/03/09 22:27:41  danda
+ *   win32 build patches contributed by Jeff Lawson
+ *
+ *   Revision 1.20  2002/02/13 20:58:50  danda
+ *   patch to make source more windows friendly, contributed by Jeff Lawson
+ *
+ *   Revision 1.19  2001/10/12 23:25:54  danda
+ *   default to writing xmlrpc
+ *
+ *   Revision 1.18  2001/09/29 21:58:05  danda
+ *   adding cvs log to history section
+ *
+ *   10/15/2000 -- danda -- adding robodoc documentation
+ *   08/2000 -- danda -- PHP C extension that uses XMLRPC                     
+ *   08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc
+ *   09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only.
+ *   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.
+ *   06/2000 -- danda -- played with expat-ensor from www.ensor.org.  Cool, but some flaws.
+ * TODO
+ * 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
+ *******/
+
+
+#ifdef _WIN32
+#include "xmlrpc_win32.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <ctype.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_to_soap.h"
+#include "xml_element.h"
+#include "xmlrpc_private.h"
+#include "xmlrpc_introspection_private.h"
+#include "system_methods_private.h"
+
+
+
+/*-*********************
+* Begin Time Functions *
+***********************/
+
+static int date_from_ISO8601 (const char *text, time_t * value) {
+   struct tm tm;
+   int n;
+   int i;
+       char buf[18];
+
+       if (strchr (text, '-')) {
+               char *p = (char *) text, *p2 = buf;
+               while (p && *p) {
+                       if (*p != '-') {
+                               *p2 = *p;
+                               p2++;
+                       }
+                       p++;
+               }
+               text = buf;
+       }
+
+
+   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);
+#if 0  /* TODO: soap seems to favor this method. xmlrpc the latter. */
+       return strftime (buf, length, "%Y-%m-%dT%H:%M:%SZ", tm);
+#else
+   return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm);
+#endif
+}
+
+/*-*******************
+* 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) {
+               if (request->io) {
+                       XMLRPC_CleanupValue (request->io);
+               }
+      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_RequestSetError
+ * NAME
+ *   XMLRPC_RequestSetError
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_RequestSetError(XMLRPC_REQUEST request, XMLRPC_VALUE error)
+ * FUNCTION
+ *   Associates a block of xmlrpc data, representing an error
+ *   condition, with the request. 
+ * INPUTS
+ *   request -- previously allocated request struct
+ *   error   -- previously allocated error code or struct
+ * RESULT
+ *   XMLRPC_VALUE -- pointer to value stored, or NULL
+ * NOTES
+ *   This is a private function for usage by internals only.
+ * SEE ALSO
+ *   XMLRPC_RequestGetError ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_RequestSetError (XMLRPC_REQUEST request, XMLRPC_VALUE error) {
+       if (request && error) {
+               if (request->error) {
+                       XMLRPC_CleanupValue (request->error);
+               }
+               request->error = XMLRPC_CopyValue (error);
+               return request->error;
+       }
+       return NULL;
+}
+
+/*******/
+
+/****f* REQUEST/XMLRPC_RequestGetError
+ * NAME
+ *   XMLRPC_RequestGetError
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_RequestGetError(XMLRPC_REQUEST request)
+ * FUNCTION
+ *   Returns error data associated with request, if any.
+ * INPUTS
+ *   request -- previously allocated request struct
+ * RESULT
+ *   XMLRPC_VALUE -- pointer to error value stored, or NULL
+ * NOTES
+ *   This is a private function for usage by internals only.
+ * SEE ALSO
+ *   XMLRPC_RequestSetError ()
+ *   XMLRPC_RequestFree ()
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_RequestGetError (XMLRPC_REQUEST request) {
+       return request ? request->error : 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) {
+      char* pRet = NULL;
+       if (request) {
+               xml_element *root_elem = NULL;
+               if (request->output.version == xmlrpc_version_simple) {
+                       root_elem = DANDARPC_REQUEST_to_xml_element (request);
+               }
+               else if (request->output.version == xmlrpc_version_1_0 ||
+                                       request->output.version == xmlrpc_version_none) {
+                       root_elem = XMLRPC_REQUEST_to_xml_element (request);
+               }
+               else if (request->output.version == xmlrpc_version_soap_1_1) {
+                       root_elem = SOAP_REQUEST_to_xml_element (request);
+               }
+
+      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 %ld, column %ld, byte index %ld", 
+                                        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 if (!strcmp (root_elem->name, "SOAP-ENV:Envelope")) {
+                               request->output.version = xmlrpc_version_soap_1_1;
+                               xml_element_to_SOAP_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) {
+                               XMLRPC_RequestSetError (request, 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) {
+#ifdef XMLRPC_DEBUG_REFCOUNT
+               printf ("calloc'd 0x%x\n", v);
+#endif
+      v->type = xmlrpc_empty;
+      simplestring_init(&v->id);
+      simplestring_init(&v->str);
+   }
+   return v;
+}
+
+/*******/
+
+/****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) {
+               /* we can change the type so long as nothing is currently stored. */
+               if(value->type == xmlrpc_vector) {
+                       if(value->v) {
+                               if(!Q_Size(value->v->q)) {
+                                       value->v->type = type;
+                               }
+                       }
+               }
+               else {
+      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) {
+      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_nil:
+            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 (reference) of an XMLRPC_VALUE
+ * INPUTS
+ *   value     The target XMLRPC_VALUE
+ * RESULT
+ *   XMLRPC_VALUE -- address of the copy
+ * SEE ALSO
+ *   XMLRPC_CleanupValue ()
+ *   XMLRPC_DupValueNew ()
+ * 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, now %i\n", value->id.str,
+                                         value->iRefCount);
+               }
+               else {
+                       printf ("incremented refcount of 0x%x, now %i\n", value,
+                                         value->iRefCount);
+      }
+#endif
+   }
+   return value;
+}
+
+/*******/
+
+
+/****f* VALUE/XMLRPC_DupValueNew
+ * NAME
+ *   XMLRPC_DupValueNew
+ * SYNOPSIS
+ *   XMLRPC_VALUE XMLRPC_DupValueNew(XMLRPC_VALUE value)
+ * FUNCTION
+ *   Make a duplicate (non reference) of an XMLRPC_VALUE with newly allocated mem.
+ * INPUTS
+ *   value     The source XMLRPC_VALUE to duplicate
+ * RESULT
+ *   XMLRPC_VALUE -- address of the duplicate value
+ * SEE ALSO
+ *   XMLRPC_CleanupValue ()
+ *   XMLRPC_CopyValue ()
+ * NOTES
+ *   Use this when function when you need to modify the contents of
+ *   the copied value seperately from the original.
+ *   
+ *   this function is recursive, thus the value and all of its children
+ *   (if any) will be duplicated.
+ * SOURCE
+ */
+XMLRPC_VALUE XMLRPC_DupValueNew (XMLRPC_VALUE xSource) {
+       XMLRPC_VALUE xReturn = NULL;
+       if (xSource) {
+               xReturn = XMLRPC_CreateValueEmpty ();
+               if (xSource->id.len) {
+                       XMLRPC_SetValueID (xReturn, xSource->id.str, xSource->id.len);
+               }
+
+               switch (xSource->type) {
+               case xmlrpc_int:
+               case xmlrpc_boolean:
+                       XMLRPC_SetValueInt (xReturn, xSource->i);
+                       break;
+               case xmlrpc_string:
+               case xmlrpc_base64:
+                       XMLRPC_SetValueString (xReturn, xSource->str.str, xSource->str.len);
+                       break;
+               case xmlrpc_datetime:
+                       XMLRPC_SetValueDateTime (xReturn, xSource->i);
+                       break;
+               case xmlrpc_double:
+                       XMLRPC_SetValueDouble (xReturn, xSource->d);
+                       break;
+               case xmlrpc_vector:
+                       {
+                               q_iter qi = Q_Iter_Head_F (xSource->v->q);
+                               XMLRPC_SetIsVector (xReturn, xSource->v->type);
+
+                               while (qi) {
+                                       XMLRPC_VALUE xIter = Q_Iter_Get_F (qi);
+                                       XMLRPC_AddValueToVector (xReturn, XMLRPC_DupValueNew (xIter));
+                                       qi = Q_Iter_Next_F (qi);
+                               }
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return xReturn;
+}
+
+/*******/
+
+
+
+/****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.
+ *   Deprecated for public use.  See XMLRPC_GetValueTypeEasy
+ * SEE ALSO
+ *   XMLRPC_SetValue*
+ *   XMLRPC_CreateValue*
+ *   XMLRPC_Append*
+ *   XMLRPC_GetValueTypeEasy ()
+ * 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.
+ *   xmlrpc_none if not a value.
+ * NOTES
+ *   xmlrpc_none is returned if value is not a vector
+ *   Deprecated for public use.  See XMLRPC_GetValueTypeEasy
+ * SEE ALSO
+ *   XMLRPC_SetIsVector ()
+ *   XMLRPC_GetValueType ()
+ *   XMLRPC_GetValueTypeEasy ()
+ * SOURCE
+ */
+XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) {
+   return(value && value->v) ? value->v->type : xmlrpc_none;
+}
+
+/*******/
+
+/****f* VALUE/XMLRPC_GetValueTypeEasy
+ * NAME
+ *   XMLRPC_GetValueTypeEasy
+ * SYNOPSIS
+ *   XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE value)
+ * FUNCTION
+ *   determine data type of the XMLRPC_VALUE. includes vector types.
+ * INPUTS
+ *   XMLRPC_VALUE target of query
+ * RESULT
+ *   data type of value as enumerated by XMLRPC_VALUE_TYPE_EASY
+ *   xmlrpc_type_none if not a value.
+ * NOTES
+ *   all values are of type xmlrpc_type_empty until set. 
+ * SEE ALSO
+ *   XMLRPC_SetValue*
+ *   XMLRPC_CreateValue*
+ *   XMLRPC_Append*
+ * SOURCE
+ */
+XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy (XMLRPC_VALUE value) {
+       if (value) {
+               switch (value->type) {
+               case xmlrpc_vector:
+                       switch (value->v->type) {
+                       case xmlrpc_vector_none:
+                               return xmlrpc_type_none;
+                       case xmlrpc_vector_struct:
+                               return xmlrpc_type_struct;
+                       case xmlrpc_vector_mixed:
+                               return xmlrpc_type_mixed;
+                       case xmlrpc_vector_array:
+                               return xmlrpc_type_array;
+                       }
+               default:
+                       /* evil cast, but we know they are the same */
+                       return(XMLRPC_VALUE_TYPE_EASY) value->type;
+               }
+       }
+       return 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;
+}
+
+/*******/
+
+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_nil:
+          return "nil";
+       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";
+          }
+    }
+    return "unknown";
+}
+
+/****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) {
+               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 *
+***********************************/
+
+
+/*-******************
+* Fault 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* FAULT/XMLRPC_ValueIsFault
+ * NAME
+ *   XMLRPC_ValueIsFault
+ * SYNOPSIS
+ *   int XMLRPC_ValueIsFault (XMLRPC_VALUE value)
+ * FUNCTION
+ *   Determines if a value encapsulates a fault "object"
+ * INPUTS
+ *   value  any XMLRPC_VALUE
+ * RESULT
+ *   1 if it is a fault, else 0
+ * SEE ALSO
+ *   XMLRPC_ResponseIsFault ()
+ * SOURCE
+ */
+int XMLRPC_ValueIsFault (XMLRPC_VALUE value) {
+   if( XMLRPC_VectorGetValueWithID(value, "faultCode") &&
+       XMLRPC_VectorGetValueWithID(value, "faultString") ) {
+      return 1;
+   }
+   return 0;
+}
+/*******/
+
+
+/****f* FAULT/XMLRPC_ResponseIsFault
+ * NAME
+ *   XMLRPC_ResponseIsFault
+ * SYNOPSIS
+ *   int XMLRPC_ResponseIsFault (XMLRPC_REQUEST response)
+ * FUNCTION
+ *   Determines if a response contains an encapsulated fault "object"
+ * INPUTS
+ *   value  any XMLRPC_REQUEST. typically of type xmlrpc_request_response
+ * RESULT
+ *   1 if it contains a fault, else 0
+ * SEE ALSO
+ *   XMLRPC_ValueIsFault ()
+ * SOURCE
+ */
+int XMLRPC_ResponseIsFault(XMLRPC_REQUEST response) {
+   return XMLRPC_ValueIsFault( XMLRPC_RequestGetData(response) );
+}
+
+/*******/
+
+/****f* FAULT/XMLRPC_GetValueFaultCode
+ * NAME
+ *   XMLRPC_GetValueFaultCode
+ * SYNOPSIS
+ *   int XMLRPC_GetValueFaultCode (XMLRPC_VALUE value)
+ * FUNCTION
+ *   returns fault code from a struct, if any
+ * INPUTS
+ *   value  XMLRPC_VALUE of type xmlrpc_vector_struct.
+ * RESULT
+ *   fault code, else 0.
+ * BUGS
+ *   impossible to distinguish faultCode == 0 from faultCode not present.
+ * SEE ALSO
+ *   XMLRPC_GetResponseFaultCode ()
+ * SOURCE
+ */
+int XMLRPC_GetValueFaultCode (XMLRPC_VALUE value) {
+   return XMLRPC_VectorGetIntWithID(value, "faultCode");
+}
+
+/*******/
+
+/****f* FAULT/XMLRPC_GetResponseFaultCode
+ * NAME
+ *   XMLRPC_GetResponseFaultCode
+ * SYNOPSIS
+ *   int XMLRPC_GetResponseFaultCode(XMLRPC_REQUEST response)
+ * FUNCTION
+ *   returns fault code from a response, if any
+ * INPUTS
+ *   response  XMLRPC_REQUEST. typically of type xmlrpc_request_response.
+ * RESULT
+ *   fault code, else 0.
+ * BUGS
+ *   impossible to distinguish faultCode == 0 from faultCode not present.
+ * SEE ALSO
+ *   XMLRPC_GetValueFaultCode ()
+ * SOURCE
+ */
+int XMLRPC_GetResponseFaultCode(XMLRPC_REQUEST response) {
+   return XMLRPC_GetValueFaultCode( XMLRPC_RequestGetData(response) );
+}
+
+/*******/
+
+
+/****f* FAULT/XMLRPC_GetValueFaultString
+ * NAME
+ *   XMLRPC_GetValueFaultString
+ * SYNOPSIS
+ *   const char* XMLRPC_GetValueFaultString (XMLRPC_VALUE value)
+ * FUNCTION
+ *   returns fault string from a struct, if any
+ * INPUTS
+ *   value  XMLRPC_VALUE of type xmlrpc_vector_struct.
+ * RESULT
+ *   fault string, else 0.
+ * SEE ALSO
+ *   XMLRPC_GetResponseFaultString ()
+ * SOURCE
+ */
+const char* XMLRPC_GetValueFaultString (XMLRPC_VALUE value) {
+   return XMLRPC_VectorGetStringWithID(value, "faultString");
+}
+
+/*******/
+
+/****f* FAULT/XMLRPC_GetResponseFaultString
+ * NAME
+ *   XMLRPC_GetResponseFaultString
+ * SYNOPSIS
+ *   const char* XMLRPC_GetResponseFaultString (XMLRPC_REQUEST response)
+ * FUNCTION
+ *   returns fault string from a response, if any
+ * INPUTS
+ *   response  XMLRPC_REQUEST. typically of type xmlrpc_request_response.
+ * RESULT
+ *   fault string, else 0.
+ * SEE ALSO
+ *   XMLRPC_GetValueFaultString ()
+ * SOURCE
+ */
+const char* XMLRPC_GetResponseFaultString (XMLRPC_REQUEST response) {
+   return XMLRPC_GetValueFaultString( XMLRPC_RequestGetData(response) );
+}
+
+/*******/
+
+
+/*-******************
+* Utility API funcs *
+********************/
+
+
+/****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 *
+************************/