diff options
| author | Dan Libby <danda@php.net> | 2001-09-06 04:13:30 +0000 | 
|---|---|---|
| committer | Dan Libby <danda@php.net> | 2001-09-06 04:13:30 +0000 | 
| commit | 6883b9211652163ef3f50f448db2ddd8fae6ff99 (patch) | |
| tree | dd1897cb279181689cd41c92318cfb8b30963a05 /ext/xmlrpc/libxmlrpc/xmlrpc.c | |
| parent | 903c831be5cd65e5070182a74f5336eba168807d (diff) | |
| download | php-git-6883b9211652163ef3f50f448db2ddd8fae6ff99.tar.gz | |
adding xmlrpc extension, per Stig's request
Diffstat (limited to 'ext/xmlrpc/libxmlrpc/xmlrpc.c')
| -rw-r--r-- | ext/xmlrpc/libxmlrpc/xmlrpc.c | 2471 | 
1 files changed, 2471 insertions, 0 deletions
diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.c b/ext/xmlrpc/libxmlrpc/xmlrpc.c new file mode 100644 index 0000000000..f95ac949b3 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc.c @@ -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 * +************************/ + + + + +  | 
