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