From 194e17cd114719f2aa03ea3f305019088ddda6f5 Mon Sep 17 00:00:00 2001 From: Dan Libby Date: Tue, 30 Oct 2001 02:33:13 +0000 Subject: [PATCH] patches to sync with sourceforge project, version 0.50. primary change: adds support for SOAP v 1.1 --- ext/rpc/xmlrpc/libxmlrpc/Makefile.in | 5 +- ext/rpc/xmlrpc/libxmlrpc/queue.c | 2 +- ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c | 2 +- ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.c | 670 +++++++++++++++++++++ ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.h | 44 ++ ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c | 60 +- ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c | 432 +++++++++++-- ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h | 40 +- ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h | 1 - ext/rpc/xmlrpc/xmlrpc-epi-php.c | 63 +- ext/xmlrpc/libxmlrpc/Makefile.in | 5 +- ext/xmlrpc/libxmlrpc/queue.c | 2 +- ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c | 2 +- ext/xmlrpc/libxmlrpc/xml_to_soap.c | 670 +++++++++++++++++++++ ext/xmlrpc/libxmlrpc/xml_to_soap.h | 44 ++ ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c | 60 +- ext/xmlrpc/libxmlrpc/xmlrpc.c | 432 +++++++++++-- ext/xmlrpc/libxmlrpc/xmlrpc.h | 40 +- ext/xmlrpc/libxmlrpc/xmlrpc_private.h | 1 - ext/xmlrpc/xmlrpc-epi-php.c | 63 +- 20 files changed, 2454 insertions(+), 184 deletions(-) create mode 100644 ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.c create mode 100644 ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.h create mode 100644 ext/xmlrpc/libxmlrpc/xml_to_soap.c create mode 100644 ext/xmlrpc/libxmlrpc/xml_to_soap.h diff --git a/ext/rpc/xmlrpc/libxmlrpc/Makefile.in b/ext/rpc/xmlrpc/libxmlrpc/Makefile.in index 44257c4882..5c9d4f4aa9 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/Makefile.in +++ b/ext/rpc/xmlrpc/libxmlrpc/Makefile.in @@ -2,8 +2,9 @@ LTLIBRARY_NAME = libxmlrpc.la LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \ xmlrpc_introspection.c encodings.c system_methods.c \ - xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c + xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c \ + xml_to_soap.c -DEFS = -DVERSION="0.41" +DEFS = -DVERSION="0.50" include $(top_srcdir)/build/dynlib.mk diff --git a/ext/rpc/xmlrpc/libxmlrpc/queue.c b/ext/rpc/xmlrpc/libxmlrpc/queue.c index 25d62b96a7..7ae4c46078 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/queue.c +++ b/ext/rpc/xmlrpc/libxmlrpc/queue.c @@ -476,7 +476,7 @@ void *Q_Next(queue *q) if(!q) return NULL; - if(q->cursor->next == NULL) + if(!q->cursor || q->cursor->next == NULL) return NULL; q->cursor = (node *)q->cursor->next; diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c index 0d827a5fb0..773eca5ecc 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c @@ -113,7 +113,7 @@ XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLR XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_ARRAY)) { - XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array); } else if(!strcmp(type, ATTR_STRUCT)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.c new file mode 100644 index 0000000000..6aae027313 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.c @@ -0,0 +1,670 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) +*/ + + +/************************************************************************ +* TODO: * +* - [SOAP-ENC:position] read sparse arrays (and write?) * +* - [SOAP-ENC:offset] read partially transmitted arrays (and write?) * +* - read "flattened" multi-dimensional arrays. (don't bother writing) * +* * +* BUGS: * +* - does not read schema. thus only knows soap pre-defined types. * +* - references (probably) do not work. untested. * +* - does not expose SOAP-ENV:Header to application at all. * +* - does not use namespaces correctly, thus: * +* - namespaces are hard-coded in comparison tokens * +* - if a sender uses another namespace identifer, it will break * +************************************************************************/ + + +static const char rcsid[] = "#(@) $Id:"; + +#include +#include +#include "xml_to_soap.h" +#include "base64.h" + +/* list of tokens used in vocab */ +#define TOKEN_ANY "xsd:ur-type" +#define TOKEN_ARRAY "SOAP-ENC:Array" +#define TOKEN_ARRAY_TYPE "SOAP-ENC:arrayType" +#define TOKEN_BASE64 "SOAP-ENC:base64" +#define TOKEN_BOOLEAN "xsd:boolean" +#define TOKEN_DATETIME "xsd:timeInstant" +#define TOKEN_DOUBLE "xsd:double" +#define TOKEN_FLOAT "xsd:float" +#define TOKEN_ID "id" +#define TOKEN_INT "xsd:int" +#define TOKEN_NULL "xsi:null" +#define TOKEN_STRING "xsd:string" +#define TOKEN_STRUCT "xsd:struct" +#define TOKEN_TYPE "xsi:type" +#define TOKEN_FAULT "SOAP-ENV:Fault" +#define TOKEN_MUSTUNDERSTAND "SOAP-ENV:mustUnderstand" +#define TOKEN_ACTOR "SOAP-ENV:actor" +#define TOKEN_ACTOR_NEXT "http://schemas.xmlsoap.org/soap/actor/next" + +#define TOKEN_XMLRPC_FAULTCODE "faultCode" +#define TOKEN_XMLRPC_FAULTSTRING "faultString" +#define TOKEN_SOAP_FAULTCODE "faultcode" +#define TOKEN_SOAP_FAULTSTRING "faultstring" +#define TOKEN_SOAP_FAULTDETAILS "details" +#define TOKEN_SOAP_FAULTACTOR "actor" + + +// determine if a string represents a soap type, as used in +// element names +static inline int is_soap_type(const char* soap_type) { + return(strstr(soap_type, "SOAP-ENC:") || strstr(soap_type, "xsd:")) ? 1 : 0; +} + +/* utility func to generate a new attribute. possibly should be in xml_element.c?? */ +static xml_element_attr* new_attr(const char* key, const char* val) { + xml_element_attr* attr = malloc(sizeof(xml_element_attr)); + if (attr) { + attr->key = key ? strdup(key) : NULL; + attr->val = val ? strdup(val) : NULL; + } + return attr; +} + +struct array_info { + char kids_type[30]; + unsigned long size; + /* ... ? */ +}; + + +/* parses soap arrayType attribute to generate an array_info structure. + * TODO: should deal with sparse, flattened, & multi-dimensional arrays + */ +static struct array_info* parse_array_type_info(const char* array_type) { + struct array_info* ai = NULL; + if (array_type) { + ai = (struct array_info*)calloc(1, sizeof(struct array_info)); + if (ai) { + char buf[128], *p; + snprintf(buf, sizeof(buf), "%s", array_type); + p = strchr(buf, '['); + if (p) { + *p = 0; + } + strcpy(ai->kids_type, buf); + } + } + return ai; +} + +/* performs heuristics on an xmlrpc_vector_array to determine + * appropriate soap arrayType string. + */ +static const char* get_array_soap_type(XMLRPC_VALUE node) { + XMLRPC_VALUE_TYPE_EASY type = xmlrpc_type_none; + + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + int loopCount = 0; + const char* soapType = TOKEN_ANY; + + type = XMLRPC_GetValueTypeEasy(xIter); + xIter = XMLRPC_VectorNext(node); + + while (xIter) { + /* 50 seems like a decent # of loops. That will likely + * cover most cases. Any more and we start to sacrifice + * performance. + */ + if ( (XMLRPC_GetValueTypeEasy(xIter) != type) || loopCount >= 50) { + type = xmlrpc_type_none; + break; + } + loopCount ++; + + xIter = XMLRPC_VectorNext(node); + } + switch (type) { + case xmlrpc_type_none: + soapType = TOKEN_ANY; + break; + case xmlrpc_type_empty: + soapType = TOKEN_NULL; + break; + case xmlrpc_type_int: + soapType = TOKEN_INT; + break; + case xmlrpc_type_double: + soapType = TOKEN_DOUBLE; + break; + case xmlrpc_type_boolean: + soapType = TOKEN_BOOLEAN; + break; + case xmlrpc_type_string: + soapType = TOKEN_STRING; + break; + case xmlrpc_type_base64: + soapType = TOKEN_BASE64; + break; + case xmlrpc_type_datetime: + soapType = TOKEN_DATETIME; + break; + case xmlrpc_type_struct: + soapType = TOKEN_STRUCT; + break; + case xmlrpc_type_array: + soapType = TOKEN_ARRAY; + break; + case xmlrpc_type_mixed: + soapType = TOKEN_STRUCT; + break; + } + return soapType; +} + +/* determines wether a node is a fault or not, and of which type: + * 0 = not a fault, + * 1 = xmlrpc style fault + * 2 = soap style fault. + */ +static inline int get_fault_type(XMLRPC_VALUE node) { + if (XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTCODE) && + XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTSTRING)) { + return 1; + } + else if (XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTCODE) && + XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTSTRING)) { + return 2; + } + return 0; +} + +/* input: an XMLRPC_VALUE representing a fault struct in xml-rpc style. + * output: an XMLRPC_VALUE representing a fault struct in soap style, + * with xmlrpc codes mapped to soap codes, and all other values preserved. + * note that the returned value is a completely new value, and must be freed. + * the input value is untouched. + */ +static XMLRPC_VALUE gen_fault_xmlrpc(XMLRPC_VALUE node, xml_element* el_target) { + XMLRPC_VALUE xDup = XMLRPC_DupValueNew(node); + XMLRPC_VALUE xCode = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTCODE); + XMLRPC_VALUE xStr = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTSTRING); + + XMLRPC_SetValueID(xCode, TOKEN_SOAP_FAULTCODE, 0); + XMLRPC_SetValueID(xStr, TOKEN_SOAP_FAULTSTRING, 0); + + /* rough mapping of xmlrpc fault codes to soap codes */ + switch (XMLRPC_GetValueInt(xCode)) { + case -32700: // "parse error. not well formed", + case -32701: // "parse error. unsupported encoding" + case -32702: // "parse error. invalid character for encoding" + case -32600: // "server error. invalid xml-rpc. not conforming to spec." + case -32601: // "server error. requested method not found" + case -32602: // "server error. invalid method parameters" + XMLRPC_SetValueString(xCode, "SOAP-ENV:Client", 0); + break; + case -32603: // "server error. internal xml-rpc error" + case -32500: // "application error" + case -32400: // "system error" + case -32300: // "transport error + XMLRPC_SetValueString(xCode, "SOAP-ENV:Server", 0); + break; + } + return xDup; +} + +// returns a new XMLRPC_VALUE representing a soap fault, comprised of a struct with four keys. +static XMLRPC_VALUE gen_soap_fault(const char* fault_code, const char* fault_string, + const char* actor, const char* details) { + XMLRPC_VALUE xReturn = XMLRPC_CreateVector(TOKEN_FAULT, xmlrpc_vector_struct); + XMLRPC_AddValuesToVector(xReturn, + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTCODE, fault_code, 0), + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTSTRING, fault_string, 0), + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTACTOR, actor, 0), + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTDETAILS, details, 0), + NULL); + return xReturn; +} + +/* translates xml soap dom to native data structures. recursive. */ +XMLRPC_VALUE xml_element_to_SOAP_REQUEST_worker(XMLRPC_REQUEST request, + XMLRPC_VALUE xParent, + struct array_info* parent_array, + XMLRPC_VALUE xCurrent, + xml_element* el, + int depth) { + XMLRPC_REQUEST_TYPE rtype = xmlrpc_request_none; + + // no current element on first call + if (!xCurrent) { + xCurrent = XMLRPC_CreateValueEmpty(); + } + + // increment recursion depth guage + depth ++; + + // safety first. must have a valid element + if (el && el->name) { + const char* id = NULL; + const char* type = NULL, *arrayType=NULL, *actor = NULL; + xml_element_attr* attr_iter = Q_Head(&el->attrs); + int b_must_understand = 0; + + // in soap, types may be specified in either element name -or- with xsi:type attribute. + if (is_soap_type(el->name)) { + type = el->name; + } + // if our parent node, by definition a vector, is not an array, then + // our element name must be our key identifier. + else if (XMLRPC_GetVectorType(xParent) != xmlrpc_vector_array) { + id = el->name; + if(!strcmp(id, "item")) { + } + } + + // iterate through element attributes, pick out useful stuff. + while (attr_iter) { + // element's type + if (!strcmp(attr_iter->key, TOKEN_TYPE)) { + type = attr_iter->val; + } + // array type + else if (!strcmp(attr_iter->key, TOKEN_ARRAY_TYPE)) { + arrayType = attr_iter->val; + } + // must understand, sometimes present in headers. + else if (!strcmp(attr_iter->key, TOKEN_MUSTUNDERSTAND)) { + b_must_understand = strchr(attr_iter->val, '1') ? 1 : 0; + } + // actor, used in conjuction with must understand. + else if (!strcmp(attr_iter->key, TOKEN_ACTOR)) { + actor = attr_iter->val; + } + attr_iter = Q_Next(&el->attrs); + } + + // check if caller says we must understand something in a header. + if (b_must_understand) { + // is must understand actually indended for us? + // BUG: spec says we should also determine if actor is our URL, but + // we do not have that information. + if (!actor || !strcmp(actor, TOKEN_ACTOR_NEXT)) { + // TODO: implement callbacks or other mechanism for applications + // to "understand" these headers. For now, we just bail if we + // get a mustUnderstand header intended for us. + XMLRPC_RequestSetError(request, + gen_soap_fault("SOAP-ENV:MustUnderstand", + "SOAP Must Understand Error", + "", "")); + return xCurrent; + } + } + + // set id (key) if one was found. + if (id) { + XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); + } + + // according to soap spec, + // depth 1 = Envelope, 2 = Header, Body & Fault, 3 = methodcall or response. + if (depth == 3) { + const char* methodname = el->name; + char* p = NULL; + + // BUG: we determine request or response type using presence of "Response" in element name. + // According to spec, this is only recommended, not required. Apparently, implementations + // are supposed to know the type of action based on state, which strikes me as a bit lame. + // Anyway, we don't have that state info, thus we use Response as a heuristic. + rtype = +#ifdef strcasestr + strcasestr(el->name, "response") ? xmlrpc_request_response : xmlrpc_request_call; +#else + strstr(el->name, "esponse") ? xmlrpc_request_response : xmlrpc_request_call; +#endif + XMLRPC_RequestSetRequestType(request, rtype); + + // Get methodname. strip xml namespace crap. + p = strchr(el->name, ':'); + if (p) { + methodname = p + 1; + } + if (rtype == xmlrpc_request_call) { + XMLRPC_RequestSetMethodName(request, methodname); + } + } + + + // Next, we begin to convert actual values. + // if no children, then must be a scalar value. + if (!Q_Size(&el->children)) { + if (!type && parent_array && parent_array->kids_type[0]) { + type = parent_array->kids_type; + } + if (!type || !strcmp(type, TOKEN_STRING)) { + XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); + } + else if (!strcmp(type, TOKEN_INT)) { + XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); + } + else if (!strcmp(type, TOKEN_BOOLEAN)) { + XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); + } + else if (!strcmp(type, TOKEN_DOUBLE) || + !strcmp(type, TOKEN_FLOAT)) { + XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); + } + else if (!strcmp(type, TOKEN_NULL)) { + // already an empty val. do nothing. + } + else if (!strcmp(type, TOKEN_DATETIME)) { + XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); + } + else if (!strcmp(type, TOKEN_BASE64)) { + struct buffer_st buf; + base64_decode(&buf, el->text.str, el->text.len); + XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset); + buffer_delete(&buf); + } + } + // Element has children, thus a vector, or "compound type" in soap-speak. + else { + struct array_info* ai = NULL; + xml_element* iter = (xml_element*)Q_Head(&el->children); + + if (!type || !strcmp(type, TOKEN_STRUCT)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); + } + else if (!strcmp(type, TOKEN_ARRAY) || arrayType != NULL) { + // determine magic associated with soap array type. + // this is passed down as we recurse, so our children have access to the info. + ai = parse_array_type_info(arrayType); // alloc'ed ai free'd below. + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array); + } + else { + // mixed is probably closest thing we have to compound type. + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + } + // Recurse, adding values as we go. Check for error during recursion + // and if found, bail. this short-circuits us out of the recursion. + while ( iter && !XMLRPC_RequestGetError(request) ) { + XMLRPC_VALUE xNext = NULL; + // top level elements don't actually represent values, so we just pass the + // current value along until we are deep enough. + if ( depth <= 2 || + (rtype == xmlrpc_request_response && depth <= 3) ) { + xml_element_to_SOAP_REQUEST_worker(request, NULL, ai, xCurrent, iter, depth); + } + // ready to do some actual de-serialization. create a new empty value and + // pass that along to be init'd, then add it to our current vector. + else { + xNext = XMLRPC_CreateValueEmpty(); + xml_element_to_SOAP_REQUEST_worker(request, xCurrent, ai, xNext, iter, depth); + XMLRPC_AddValueToVector(xCurrent, xNext); + } + iter = (xml_element*)Q_Next(&el->children); + } + // cleanup + if (ai) { + free(ai); + } + } + } + return xCurrent; +} + +// Convert soap xml dom to XMLRPC_VALUE, sans request info. untested. +XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el) +{ + return xml_element_to_SOAP_REQUEST_worker(NULL, NULL, NULL, NULL, el, 0); +} + +// Convert soap xml dom to XMLRPC_REQUEST +XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el) +{ + if (request) { + return XMLRPC_RequestSetData(request, xml_element_to_SOAP_REQUEST_worker(request, NULL, NULL, NULL, el, 0)); + } + return NULL; +} + + +/* translates data structures to soap/xml. recursive */ +xml_element* SOAP_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { +#define BUF_SIZE 128 + xml_element* elem_val = NULL; + if (node) { + int bFreeNode = 0; /* sometimes we may need to free 'node' variable */ + char buf[BUF_SIZE]; + XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(node); + char* pName = NULL, *pAttrType = NULL; + + // create our return value element + elem_val = xml_elem_new(); + + switch (type) { + case xmlrpc_type_struct: + case xmlrpc_type_mixed: + case xmlrpc_type_array: + if (type == xmlrpc_type_array) { + // array's are _very_ special in soap. + // TODO: Should handle sparse/partial arrays here. + + // determine soap array type. + const char* type = get_array_soap_type(node); + xml_element_attr* attr_array_type = NULL; + + // specify array kids type and array size. + snprintf(buf, sizeof(buf), "%s[%i]", type, XMLRPC_VectorSize(node)); + attr_array_type = new_attr(TOKEN_ARRAY_TYPE, buf); + + Q_PushTail(&elem_val->attrs, attr_array_type); + + pAttrType = TOKEN_ARRAY; + } + // check for fault, which is a rather special case. + // (can't these people design anything consistent/simple/elegant?) + else if (type == xmlrpc_type_struct) { + int fault_type = get_fault_type(node); + if (fault_type) { + if (fault_type == 1) { + // gen fault from xmlrpc style fault codes + // notice that we get a new node, which must be freed herein. + node = gen_fault_xmlrpc(node, elem_val); + bFreeNode = 1; + } + pName = TOKEN_FAULT; + } + } + + { + /* recurse through sub-elements */ + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + while ( xIter ) { + xml_element* next_el = SOAP_to_xml_element_worker(request, xIter); + if (next_el) { + Q_PushTail(&elem_val->children, next_el); + } + xIter = XMLRPC_VectorNext(node); + } + } + + break; + + // handle scalar types + case xmlrpc_type_empty: + pAttrType = TOKEN_NULL; + break; + case xmlrpc_type_string: + pAttrType = TOKEN_STRING; + simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); + break; + case xmlrpc_type_int: + pAttrType = TOKEN_INT; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_type_boolean: + pAttrType = TOKEN_BOOLEAN; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_type_double: + pAttrType = TOKEN_DOUBLE; + snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_type_datetime: + { + time_t tt = XMLRPC_GetValueDateTime(node); + struct tm *tm = localtime (&tt); + pAttrType = TOKEN_DATETIME; + if(strftime (buf, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", tm)) { + simplestring_add(&elem_val->text, buf); + } + } + break; + case xmlrpc_type_base64: + { + struct buffer_st buf; + pAttrType = TOKEN_BASE64; + base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); + simplestring_addn(&elem_val->text, buf.data, buf.offset ); + buffer_delete(&buf); + } + break; + break; + default: + break; + } + + // determining element's name is a bit tricky, due to soap semantics. + if (!pName) { + // if the value's type is known... + if (pAttrType) { + // see if it has an id (key). If so, use that as name, + // and type as an attribute. + pName = (char*)XMLRPC_GetValueID(node); + if (pName) { + Q_PushTail(&elem_val->attrs, new_attr(TOKEN_TYPE, pAttrType)); + } + + // otherwise, use the type as the name. + else { + pName = pAttrType; + } + } + // if the value's type is not known... (a rare case?) + else { + // see if it has an id (key). otherwise, default to generic "item" + pName = (char*)XMLRPC_GetValueID(node); + if (!pName) { + pName = "item"; + } + } + } + elem_val->name = strdup(pName); + + // cleanup + if (bFreeNode) { + XMLRPC_CleanupValue(node); + } + } + return elem_val; +} + +// convert XMLRPC_VALUE to soap xml dom. untested. +xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node) { + return SOAP_to_xml_element_worker(NULL, node); +} + +// convert XMLRPC_REQUEST to soap xml dom. +xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request) { + xml_element* root = xml_elem_new(); + + // safety first. + if (root) { + xml_element* body = xml_elem_new(); + root->name = strdup("SOAP-ENV:Envelope"); + + /* silly namespace stuff */ + Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/")); + Q_PushTail(&root->attrs, new_attr("xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance")); + Q_PushTail(&root->attrs, new_attr("xmlns:xsd", "http://www.w3.org/1999/XMLSchema")); + Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/")); + Q_PushTail(&root->attrs, new_attr("xmlns:si", "http://soapinterop.org/xsd")); + Q_PushTail(&root->attrs, new_attr("xmlns:ns6", "http://testuri.org")); + Q_PushTail(&root->attrs, new_attr("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/")); + + //Q_PushHead(&root->attrs, new_attr("xmlns:ks", "http://kitchen.sink.org/soap/everything/under/sun")); + // JUST KIDDING!! :-) ----> ------------------------------------------------- + + if (body) { + // go ahead and serialize first... + xml_element* el_serialized = + SOAP_to_xml_element_worker(request, + XMLRPC_RequestGetData(request)); + + /* check for fault, in which case, there is no intermediate element */ + if (el_serialized && !strcmp(el_serialized->name, TOKEN_FAULT)) { + Q_PushTail(&body->children, el_serialized); + } + // usual case: not a fault. Add Response element in between. + else { + xml_element* rpc = xml_elem_new(); + + if (rpc) { + const char* methodname = XMLRPC_RequestGetMethodName(request); + XMLRPC_REQUEST_TYPE rtype = XMLRPC_RequestGetRequestType(request); + + // if we are making a request, we want to use the methodname as is. + if (rtype == xmlrpc_request_call) { + if (methodname) { + rpc->name = strdup(methodname); + } + } + // if it's a response, we append "Response". Also, given xmlrpc-epi + // API/architecture, it's likely that we don't have a methodname for + // the response, so we have to check that. + else { + char buf[128]; + snprintf(buf, sizeof(buf), "%s%s", + methodname ? methodname : "", + "Response"); + + rpc->name = strdup(buf); + } + + // add serialized data to method call/response. + // add method call/response to body element + if (rpc->name) { + if(el_serialized) { + if(Q_Size(&el_serialized->children) && rtype == xmlrpc_request_call) { + xml_element* iter = (xml_element*)Q_Head(&el_serialized->children); + while(iter) { + Q_PushTail(&rpc->children, iter); + iter = (xml_element*)Q_Next(&el_serialized->children); + } + xml_elem_free_non_recurse(el_serialized); + } + else { + Q_PushTail(&rpc->children, el_serialized); + } + } + + Q_PushTail(&body->children, rpc); + } + else { + // no method name?! + // TODO: fault here...? + } + } + } + body->name = strdup("SOAP-ENV:Body"); + Q_PushTail(&root->children, body); + } + } + + return root; +} + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.h b/ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.h new file mode 100644 index 0000000000..9ae9308b22 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.h @@ -0,0 +1,44 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +#ifndef XML_TO_SOAP_H + #define XML_TO_SOAP_H + +#include "xmlrpc.h" + +XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el); +XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el); +xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node); +xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request); + +#endif /* XML_TO_XMLRPC_H */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c index 1cd3d9ac2b..02f3d430cd 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c @@ -82,7 +82,8 @@ XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } - } else if (!strcmp(el->name, ELEM_STRUCT)) { + } + else if (!strcmp(el->name, ELEM_STRUCT)) { xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); @@ -92,36 +93,46 @@ XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } - } else if (!strcmp(el->name, ELEM_STRING) || + } + else if (!strcmp(el->name, ELEM_STRING) || (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { XMLRPC_SetValueString(current_val, el->text.str, el->text.len); - } else if (!strcmp(el->name, ELEM_NAME)) { + } + else if (!strcmp(el->name, ELEM_NAME)) { XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); - } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { + } + else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { XMLRPC_SetValueInt(current_val, atoi(el->text.str)); - } else if (!strcmp(el->name, ELEM_BOOLEAN)) { + } + else if (!strcmp(el->name, ELEM_BOOLEAN)) { XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); - } else if (!strcmp(el->name, ELEM_DOUBLE)) { + } + else if (!strcmp(el->name, ELEM_DOUBLE)) { XMLRPC_SetValueDouble(current_val, atof(el->text.str)); - } else if (!strcmp(el->name, ELEM_DATETIME)) { + } + else if (!strcmp(el->name, ELEM_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); - } else if (!strcmp(el->name, ELEM_BASE64)) { + } + else if (!strcmp(el->name, ELEM_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); buffer_delete(&buf); - } else { + } + else { xml_element* iter; if (!strcmp(el->name, ELEM_METHODCALL)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } - } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { + } + else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } - } else if (!strcmp(el->name, ELEM_METHODNAME)) { + } + else if (!strcmp(el->name, ELEM_METHODNAME)) { if (request) { XMLRPC_RequestSetMethodName(request, el->text.str); } @@ -173,8 +184,10 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA Q_PushTail(&elem_val->children, next_el); } elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); - } else { + } + else { switch (type) { + case xmlrpc_empty: // treat null value as empty string in xmlrpc. case xmlrpc_string: elem_val->name = strdup(ELEM_STRING); simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); @@ -270,7 +283,8 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA /* yet another hack for the "fault" crap */ if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { root = value; - } else { + } + else { xml_element* param = xml_elem_new(); param->name = strdup(ELEM_PARAM); @@ -279,7 +293,8 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA root = param; } Q_PushTail(&value->children, elem_val); - } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { + } + else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { xml_element* member = xml_elem_new(); xml_element* name = xml_elem_new(); xml_element* value = xml_elem_new(); @@ -295,7 +310,8 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA Q_PushTail(&value->children, elem_val); root = member; - } else if (vtype == xmlrpc_vector_array) { + } + else if (vtype == xmlrpc_vector_array) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); @@ -303,10 +319,12 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA Q_PushTail(&value->children, elem_val); root = value; - } else if (vtype == xmlrpc_vector_none) { + } + else if (vtype == xmlrpc_vector_none) { /* no parent. non-op */ root = elem_val; - } else { + } + else { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); @@ -335,13 +353,15 @@ xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { if (request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; - } else if (request_type == xmlrpc_request_response) { + } + else if (request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } if (pStr) { wrapper->name = strdup(pStr); } + if(request_type == xmlrpc_request_call) { pStr = XMLRPC_RequestGetMethodName(request); if (pStr) { @@ -350,10 +370,12 @@ xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } + } if (xParams) { Q_PushTail(&wrapper->children, XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); - } else { + } + else { /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ xml_element* params = xml_elem_new(); params->name = strdup(ELEM_PARAMS); diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c index f95ac949b3..596ca622d3 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c @@ -109,6 +109,7 @@ static const char rcsid[] = "#(@) $Id$"; #include "xml_to_xmlrpc.h" #include "xml_to_dandarpc.h" +#include "xml_to_soap.h" #include "xml_element.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" @@ -119,12 +120,25 @@ static const char rcsid[] = "#(@) $Id$"; * Begin Time Functions * ***********************/ -static int date_from_ISO8601(const char *text, time_t *value) -{ +static int date_from_ISO8601 (const char *text, time_t * value) { struct tm tm; int n; int i; time_t t; + char buf[18]; + + if (strchr (text, '-')) { + char *p = (char *) text, *p2 = buf; + while (p && *p) { + if (*p != '-') { + *p2 = *p; + p2++; + } + p++; + } + text = buf; + } + tm.tm_isdst = -1; @@ -182,12 +196,14 @@ static int date_from_ISO8601(const char *text, time_t *value) } -static int date_to_ISO8601(time_t value, char *buf, int length) -{ +static int date_to_ISO8601 (time_t value, char *buf, int length) { struct tm *tm; tm = localtime(&value); - +#if 0 // TODO: soap seems to favor this method. xmlrpc the latter. + return strftime (buf, length, "%Y-%m-%dT%H:%M:%SZ", tm); +#else return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm); +#endif } /*-******************* @@ -219,6 +235,7 @@ XMLRPC_REQUEST XMLRPC_RequestNew() { } return xRequest; } + /*******/ /****f* REQUEST/XMLRPC_RequestFree @@ -249,6 +266,7 @@ void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) { my_free(request); } } + /*******/ /* Set Method Name to call */ @@ -276,6 +294,7 @@ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* meth } return NULL; } + /*******/ /****f* REQUEST/XMLRPC_RequestGetMethodName @@ -296,6 +315,7 @@ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* meth const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { return request ? request->methodName.str : NULL; } + /*******/ /****f* REQUEST/XMLRPC_RequestSetRequestType @@ -317,13 +337,15 @@ const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { * XMLRPC_REQUEST_TYPE * SOURCE */ -XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) { +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 @@ -349,6 +371,7 @@ XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_ XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { return request ? request->request_type : xmlrpc_request_none; } + /*******/ @@ -377,11 +400,15 @@ XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { */ XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { if(request && data) { + if (request->io) { + XMLRPC_CleanupValue (request->io); + } request->io = XMLRPC_CopyValue(data); return request->io; } return NULL; } + /*******/ /****f* REQUEST/XMLRPC_RequestGetData @@ -406,8 +433,66 @@ XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { return request ? request->io : NULL; } + +/*******/ + +/****f* REQUEST/XMLRPC_RequestSetError + * NAME + * XMLRPC_RequestSetError + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestSetError(XMLRPC_REQUEST request, XMLRPC_VALUE error) + * FUNCTION + * Associates a block of xmlrpc data, representing an error + * condition, with the request. + * INPUTS + * request -- previously allocated request struct + * error -- previously allocated error code or struct + * RESULT + * XMLRPC_VALUE -- pointer to value stored, or NULL + * NOTES + * This is a private function for usage by internals only. + * SEE ALSO + * XMLRPC_RequestGetError () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestSetError (XMLRPC_REQUEST request, XMLRPC_VALUE error) { + if (request && error) { + if (request->error) { + XMLRPC_CleanupValue (request->error); + } + request->error = XMLRPC_CopyValue (error); + return request->error; + } + return NULL; +} + +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetError + * NAME + * XMLRPC_RequestGetError + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestGetError(XMLRPC_REQUEST request) + * FUNCTION + * Returns error data associated with request, if any. + * INPUTS + * request -- previously allocated request struct + * RESULT + * XMLRPC_VALUE -- pointer to error value stored, or NULL + * NOTES + * This is a private function for usage by internals only. + * SEE ALSO + * XMLRPC_RequestSetError () + * XMLRPC_RequestFree () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestGetError (XMLRPC_REQUEST request) { + return request ? request->error : NULL; +} + /*******/ + /****f* REQUEST/XMLRPC_RequestSetOutputOptions * NAME * XMLRPC_RequestSetOutputOptions @@ -431,11 +516,13 @@ XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { */ 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)); + memcpy (&request->output, output, + sizeof (STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS)); return &request->output; } return NULL; } + /*******/ @@ -461,6 +548,7 @@ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST requ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) { return request ? &request->output : NULL; } + /*******/ /*-************************* @@ -503,6 +591,7 @@ char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { } return pRet; } + /*******/ /****f* SERIALIZE/XMLRPC_REQUEST_ToXML @@ -527,19 +616,30 @@ char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { * 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 (request) { + xml_element *root_elem = NULL; + if (request->output.version == xmlrpc_version_simple) { + root_elem = DANDARPC_REQUEST_to_xml_element (request); + } + else if (request->output.version == xmlrpc_version_1_0) { + root_elem = XMLRPC_REQUEST_to_xml_element (request); + } + else if (request->output.version == xmlrpc_version_soap_1_1) { + root_elem = SOAP_REQUEST_to_xml_element (request); + } if(root_elem) { - pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len); + pRet = + xml_elem_serialize_to_string (root_elem, + &request->output.xml_elem_opts, + buf_len); xml_elem_free(root_elem); } - return pRet; } + return pRet; } + /*******/ /****f* SERIALIZE/XMLRPC_VALUE_FromXML @@ -562,8 +662,7 @@ char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) { * XMLRPC_VALUE * SOURCE */ -XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) -{ +XMLRPC_VALUE XMLRPC_VALUE_FromXML (const char *in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) { XMLRPC_VALUE xResponse = NULL; XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options); @@ -573,6 +672,7 @@ XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_IN } return xResponse; } + /*******/ /* map parser errors to standard xml-rpc errors */ @@ -583,8 +683,7 @@ static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { char buf[1024]; snprintf(buf, sizeof(buf), "error occurred at line %i, column %i, byte index %i", - error->line, error->column, - error->byte_index); + error->line, error->column, error->byte_index); /* expat specific errors */ switch(error->parser_code) { @@ -622,19 +721,26 @@ static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { * XMLRPC_REQUEST * SOURCE */ -XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) -{ +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); + xml_element *root_elem = + xml_elem_parse_buf (in_buf, len, + (in_options ? &in_options->xml_elem_opts : NULL), + &error); if(root_elem) { if(!strcmp(root_elem->name, "simpleRPC")) { request->output.version = xmlrpc_version_simple; xml_element_to_DANDARPC_REQUEST(request, root_elem); } + else if (!strcmp (root_elem->name, "SOAP-ENV:Envelope")) { + request->output.version = xmlrpc_version_soap_1_1; + xml_element_to_SOAP_REQUEST (request, root_elem); + } else { request->output.version = xmlrpc_version_1_0; xml_element_to_XMLRPC_REQUEST(request, root_elem); @@ -643,13 +749,14 @@ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUES } else { if(error.parser_error) { - request->error = map_expat_errors(&error); + XMLRPC_RequestSetError (request, map_expat_errors (&error)); } } } return request; } + /*******/ /*-************************ @@ -676,6 +783,9 @@ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUES XMLRPC_VALUE XMLRPC_CreateValueEmpty() { XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE)); if(v) { +#ifdef XMLRPC_DEBUG_REFCOUNT + printf ("calloc'd 0x%x\n", v); +#endif v->type = xmlrpc_empty; simplestring_init(&v->id); simplestring_init(&v->str); @@ -689,6 +799,7 @@ static const char* get_string(const char* buf, int bDup) { } return buf; } + /*******/ /****f* VALUE/XMLRPC_SetValueID_Case @@ -724,7 +835,12 @@ const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, 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]); + value->id.str[i] = + (id_case == + xmlrpc_case_lower) ? tolower (value->id. + str[i]) : toupper (value-> + id. + str[i]); } } @@ -738,6 +854,7 @@ const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, return pRetval; } + /*******/ @@ -772,6 +889,7 @@ const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) return pRetval; } + /*******/ /****f* VALUE/XMLRPC_SetValueInt @@ -797,6 +915,7 @@ void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) { value->i = val; } } + /*******/ /****f* VALUE/XMLRPC_SetValueBoolean @@ -822,6 +941,7 @@ void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { value->i = val ? 1 : 0; } } + /*******/ @@ -850,7 +970,16 @@ void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { int bSuccess = 0; - if(value && value->type != xmlrpc_vector) { + if (value) { + // we can change the type so long as nothing is currently stored. + if(value->type == xmlrpc_vector) { + if(value->v) { + if(!Q_Size(value->v->q)) { + value->v->type = type; + } + } + } + else { value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR)); if(value->v) { value->v->q = (queue*)malloc(sizeof(queue)); @@ -862,9 +991,11 @@ int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { } } } + } return bSuccess; } + /*******/ /****f* VECTOR/XMLRPC_CreateVector @@ -912,6 +1043,7 @@ XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) { } return val; } + /*******/ @@ -966,22 +1098,26 @@ int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) { 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))) { + 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"); + 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"); + fprintf (stderr, + "xmlrpc: attempted to add value of unknown type to vector\n"); break; } } } return 0; } + /*******/ @@ -1026,7 +1162,8 @@ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { break; } } - } while(v); + } + while (v); va_end(vl); @@ -1037,6 +1174,7 @@ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { } return iRetval; } + /*******/ @@ -1059,7 +1197,8 @@ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { * XMLRPC_CASE_COMPARISON * SOURCE */ -XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) { +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); @@ -1082,6 +1221,7 @@ XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* i } return NULL; } + /*******/ @@ -1136,6 +1276,7 @@ XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) } return value; } + /*******/ /****f* VALUE/XMLRPC_CreateValueInt @@ -1167,6 +1308,7 @@ XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) { } return val; } + /*******/ /****f* VALUE/XMLRPC_CreateValueBoolean @@ -1198,6 +1340,7 @@ XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) { } return val; } + /*******/ @@ -1235,10 +1378,12 @@ void XMLRPC_CleanupValue(XMLRPC_VALUE value) { #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { - printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount); + 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); + printf ("decremented refcount of 0x%x, now %i\n", value, + value->iRefCount); } #endif @@ -1295,12 +1440,14 @@ void XMLRPC_CleanupValue(XMLRPC_VALUE value) { my_free(value); break; default: - fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n"); + fprintf (stderr, + "xmlrpc: attempted to free value of invalid type\n"); break; } } } } + /*******/ @@ -1339,6 +1486,7 @@ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { } } } + /*******/ /****f* VALUE/XMLRPC_CopyValue @@ -1347,13 +1495,14 @@ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { * SYNOPSIS * XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) * FUNCTION - * Make a copy of an XMLRPC_VALUE + * Make a copy (reference) of an XMLRPC_VALUE * INPUTS * value The target XMLRPC_VALUE * RESULT * XMLRPC_VALUE -- address of the copy * SEE ALSO * XMLRPC_CleanupValue () + * XMLRPC_DupValueNew () * NOTES * This function is implemented via reference counting, so the * returned value is going to be the same as the passed in value. @@ -1366,12 +1515,83 @@ XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) { value->iRefCount ++; #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { - printf("incremented refcount of %s\n", value->id.str); + printf ("incremented refcount of %s, now %i\n", value->id.str, + value->iRefCount); + } + else { + printf ("incremented refcount of 0x%x, now %i\n", value, + value->iRefCount); } #endif } return value; } + +/*******/ + + +/****f* VALUE/XMLRPC_DupValueNew + * NAME + * XMLRPC_DupValueNew + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_DupValueNew(XMLRPC_VALUE value) + * FUNCTION + * Make a duplicate (non reference) of an XMLRPC_VALUE with newly allocated mem. + * INPUTS + * value The source XMLRPC_VALUE to duplicate + * RESULT + * XMLRPC_VALUE -- address of the duplicate value + * SEE ALSO + * XMLRPC_CleanupValue () + * XMLRPC_CopyValue () + * NOTES + * Use this when function when you need to modify the contents of + * the copied value seperately from the original. + * + * this function is recursive, thus the value and all of its children + * (if any) will be duplicated. + * SOURCE + */ +XMLRPC_VALUE XMLRPC_DupValueNew (XMLRPC_VALUE xSource) { + XMLRPC_VALUE xReturn = NULL; + if (xSource) { + xReturn = XMLRPC_CreateValueEmpty (); + if (xSource->id.len) { + XMLRPC_SetValueID (xReturn, xSource->id.str, xSource->id.len); + } + + switch (xSource->type) { + case xmlrpc_int: + case xmlrpc_boolean: + XMLRPC_SetValueInt (xReturn, xSource->i); + break; + case xmlrpc_string: + case xmlrpc_base64: + XMLRPC_SetValueString (xReturn, xSource->str.str, xSource->str.len); + break; + case xmlrpc_datetime: + XMLRPC_SetValueDateTime (xReturn, xSource->i); + break; + case xmlrpc_double: + XMLRPC_SetValueDouble (xReturn, xSource->d); + break; + case xmlrpc_vector: + { + q_iter qi = Q_Iter_Head_F (xSource->v->q); + XMLRPC_SetIsVector (xReturn, xSource->v->type); + + while (qi) { + XMLRPC_VALUE xIter = Q_Iter_Get_F (qi); + XMLRPC_AddValueToVector (xReturn, XMLRPC_DupValueNew (xIter)); + qi = Q_Iter_Next_F (qi); + } + } + break; + } + } + return xReturn; +} + /*******/ @@ -1405,6 +1625,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) { } return val; } + /*******/ @@ -1440,6 +1661,7 @@ void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) { } } } + /*******/ /****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601 @@ -1473,6 +1695,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) { } return val; } + /*******/ @@ -1506,6 +1729,7 @@ void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) { value->type = xmlrpc_base64; } } + /*******/ @@ -1540,6 +1764,7 @@ XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) { } return val; } + /*******/ /****f* VALUE/XMLRPC_SetValueDouble @@ -1566,6 +1791,7 @@ void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) { value->d = val; } } + /*******/ /****f* VALUE/XMLRPC_CreateValueDouble @@ -1596,6 +1822,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { } return val; } + /*******/ /****f* VALUE/XMLRPC_GetValueString @@ -1618,6 +1845,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_string) ? value->str.str : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueStringLen @@ -1640,6 +1868,7 @@ const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { return ((value) ? value->str.len : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueInt @@ -1663,6 +1892,7 @@ int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { int XMLRPC_GetValueInt(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_int) ? value->i : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueBoolean @@ -1686,6 +1916,7 @@ int XMLRPC_GetValueInt(XMLRPC_VALUE value) { int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_boolean) ? value->i : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueDouble @@ -1709,6 +1940,7 @@ int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_double) ? value->d : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueBase64 @@ -1733,6 +1965,7 @@ double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_base64) ? value->str.str : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueDateTime @@ -1757,6 +1990,7 @@ const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueDateTime_IOS8601 @@ -1779,6 +2013,7 @@ time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { 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 */ @@ -1802,6 +2037,7 @@ const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) { const char* XMLRPC_GetValueID(XMLRPC_VALUE value) { return (const char*)((value && value->id.len) ? value->id.str : 0); } + /*******/ @@ -1830,6 +2066,7 @@ int XMLRPC_VectorSize(XMLRPC_VALUE value) { } return size; } + /*******/ /****f* VECTOR/XMLRPC_VectorRewind @@ -1857,6 +2094,7 @@ XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) { } return xReturn; } + /*******/ /****f* VECTOR/XMLRPC_VectorNext @@ -1882,6 +2120,7 @@ XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { } return xReturn; } + /*******/ /****f* VALUE/XMLRPC_GetValueType @@ -1897,15 +2136,18 @@ XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { * data type of value as enumerated by XMLRPC_VALUE_TYPE * NOTES * all values are of type xmlrpc_empty until set. + * Deprecated for public use. See XMLRPC_GetValueTypeEasy * SEE ALSO * XMLRPC_SetValue* * XMLRPC_CreateValue* * XMLRPC_Append* + * XMLRPC_GetValueTypeEasy () * SOURCE */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { return value ? value->type : xmlrpc_empty; } + /*******/ /* Vector type accessor */ @@ -1919,20 +2161,69 @@ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT - * vector type of value as enumerated by XMLRPC_VECTOR_TYPE + * vector type of value as enumerated by XMLRPC_VECTOR_TYPE. + * xmlrpc_none if not a value. * NOTES * xmlrpc_none is returned if value is not a vector + * Deprecated for public use. See XMLRPC_GetValueTypeEasy * SEE ALSO * XMLRPC_SetIsVector () * XMLRPC_GetValueType () + * XMLRPC_GetValueTypeEasy () * SOURCE */ XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) { return(value && value->v) ? value->v->type : xmlrpc_none; } + +/*******/ + +/****f* VALUE/XMLRPC_GetValueTypeEasy + * NAME + * XMLRPC_GetValueTypeEasy + * SYNOPSIS + * XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE value) + * FUNCTION + * determine data type of the XMLRPC_VALUE. includes vector types. + * INPUTS + * XMLRPC_VALUE target of query + * RESULT + * data type of value as enumerated by XMLRPC_VALUE_TYPE_EASY + * xmlrpc_type_none if not a value. + * NOTES + * all values are of type xmlrpc_type_empty until set. + * SEE ALSO + * XMLRPC_SetValue* + * XMLRPC_CreateValue* + * XMLRPC_Append* + * SOURCE + */ +XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy (XMLRPC_VALUE value) { + if (value) { + switch (value->type) { + case xmlrpc_vector: + switch (value->v->type) { + case xmlrpc_vector_none: + return xmlrpc_type_none; + case xmlrpc_vector_struct: + return xmlrpc_type_struct; + case xmlrpc_vector_mixed: + return xmlrpc_type_mixed; + case xmlrpc_vector_array: + return xmlrpc_type_array; + } + default: + /* evil cast, but we know they are the same */ + return(XMLRPC_VALUE_TYPE_EASY) value->type; + } + } + return xmlrpc_none; +} + /*******/ + /*-******************* * Begin Server Funcs * *********************/ @@ -1966,6 +2257,7 @@ XMLRPC_SERVER XMLRPC_ServerCreate() { } return server; } + /*******/ /* Return global server. Not locking! Not Thread Safe! */ @@ -1996,6 +2288,7 @@ XMLRPC_SERVER XMLRPC_GetGlobalServer() { } return xsServer; } + /*******/ /****f* VALUE/XMLRPC_ServerDestroy @@ -2042,6 +2335,7 @@ void XMLRPC_ServerDestroy(XMLRPC_SERVER server) { my_free(server); } } + /*******/ @@ -2082,6 +2376,7 @@ int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_C } return 0; } + /*******/ inline server_method* find_method(XMLRPC_SERVER server, const char* name) { @@ -2164,6 +2459,7 @@ XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callNa } return NULL; } + /*******/ @@ -2199,17 +2495,21 @@ XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST reques 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); + else if (server && request) { + XMLRPC_Callback cb = + XMLRPC_ServerFindMethod (server, request->methodName.str); if(cb) { xReturn = cb(server, request, userData); } else { - xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str); + xReturn = + XMLRPC_UtilityCreateFault (xmlrpc_error_unknown_method, + request->methodName.str); } } return xReturn; } + /*******/ /*-***************** @@ -2227,7 +2527,8 @@ XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST reques typedef struct _xmlrpc_options { XMLRPC_CASE id_case; XMLRPC_CASE_COMPARISON id_case_compare; -} STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; +} +STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() { static STRUCT_XMLRPC_OPTIONS options = { @@ -2259,6 +2560,7 @@ XMLRPC_CASE XMLRPC_GetDefaultIdCase() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case; } + /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCase @@ -2284,6 +2586,7 @@ XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) { options->id_case = id_case; return options->id_case; } + /*******/ /****f* VALUE/XMLRPC_GetDefaultIdCaseComparison @@ -2308,6 +2611,7 @@ XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case_compare; } + /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCaseComparison @@ -2333,6 +2637,7 @@ XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON options->id_case_compare = id_case_compare; return options->id_case_compare; } + /*******/ /*-********************************* @@ -2385,16 +2690,36 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) 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; + 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); @@ -2408,7 +2733,8 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) if(description.len) { xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); - XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len); + XMLRPC_VectorAppendString (xOutput, "faultString", description.str, + description.len); XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code); } @@ -2416,6 +2742,7 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) return xOutput; } + /*******/ @@ -2438,6 +2765,7 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) void XMLRPC_Free(void* mem) { my_free(mem); } + /*******/ @@ -2458,14 +2786,10 @@ void XMLRPC_Free(void* mem) { const char* XMLRPC_GetVersionString() { return XMLRPC_VERSION_STR; } + /*******/ /*-********************** * End Utility API funcs * ************************/ - - - - - diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h index c7223adf2e..bcfa46fadc 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h @@ -43,7 +43,7 @@ extern "C" { /* allow version to be specified via compile line define */ #ifndef XMLRPC_LIB_VERSION - #define XMLRPC_LIB_VERSION "0.41" + #define XMLRPC_LIB_VERSION "0.50" #endif /* this number, representing the date, must be increased each time the API changes */ @@ -61,6 +61,7 @@ extern "C" { * XMLRPC_VALUE_TYPE * NOTES * Defines data types for XMLRPC_VALUE + * Deprecated for public use. See XMLRPC_VALUE_TYPE_EASY * SEE ALSO * XMLRPC_VECTOR_TYPE * XMLRPC_REQUEST_TYPE @@ -83,7 +84,8 @@ typedef enum _XMLRPC_VALUE_TYPE { * NAME * XMLRPC_VECTOR_TYPE * NOTES - * Defines data types for XMLRPC_VECTOR + * Defines data types for XMLRPC_VECTOR. + * Deprecated for public use. See XMLRPC_VALUE_TYPE_EASY * SEE ALSO * XMLRPC_VALUE_TYPE * XMLRPC_REQUEST_TYPE @@ -97,6 +99,33 @@ typedef enum _XMLRPC_VECTOR_TYPE { } XMLRPC_VECTOR_TYPE; /*******/ +/****d* VALUE/XMLRPC_VALUE_TYPE_EASY + * NAME + * XMLRPC_VALUE_TYPE_EASY + * NOTES + * Defines data types for XMLRPC_VALUE, including vector types. + * SEE ALSO + * XMLRPC_VECTOR_TYPE + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +typedef enum _XMLRPC_VALUE_TYPE_EASY { + xmlrpc_type_none, /* not a value */ + xmlrpc_type_empty, /* empty value, eg NULL */ + xmlrpc_type_base64, /* base64 value, eg binary data */ + xmlrpc_type_boolean, /* boolean [0 | 1] */ + xmlrpc_type_datetime, /* datetime [ISO8601 | time_t] */ + xmlrpc_type_double, /* double / floating point */ + xmlrpc_type_int, /* integer */ + xmlrpc_type_string, /* string */ +/* -- IMPORTANT: identical to XMLRPC_VALUE_TYPE to this point. -- */ + xmlrpc_type_array, /* vector array */ + xmlrpc_type_mixed, /* vector mixed */ + xmlrpc_type_struct /* vector struct */ +} XMLRPC_VALUE_TYPE_EASY; +/*******/ + + /****d* VALUE/XMLRPC_REQUEST_TYPE * NAME * XMLRPC_REQUEST_TYPE @@ -165,7 +194,8 @@ typedef enum _xmlrpc_version { xmlrpc_version_none, /* not a recognized vocabulary */ xmlrpc_version_1_0, /* xmlrpc 1.0 standard vocab */ xmlrpc_version_simple = 2, /* alt more readable vocab */ - xmlrpc_version_danda = 2 /* same as simple. legacy */ + xmlrpc_version_danda = 2, /* same as simple. legacy */ + xmlrpc_version_soap_1_1 = 3 /* SOAP. version 1.1 */ } XMLRPC_VERSION; /******/ @@ -303,7 +333,10 @@ XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type); /* Cleanup values */ void XMLRPC_CleanupValue(XMLRPC_VALUE value); + +/* Copy values */ XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_DupValueNew(XMLRPC_VALUE xSource); /* Set Values */ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time); @@ -329,6 +362,7 @@ const char* XMLRPC_GetValueID(XMLRPC_VALUE value); /* Type introspection */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v); +XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE v); XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v); /* Parsing and Creating XML */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h index 0b4a078ee9..65c6b136a6 100644 --- a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h @@ -122,7 +122,6 @@ typedef struct _xmlrpc_request { /* Vector type. Used by XMLRPC_VALUE. Never visible to users of the API. */ typedef struct _xmlrpc_vector { XMLRPC_VECTOR_TYPE type; /* vector type */ - const char* id; /* ??? unused? */ queue *q; /* list of child values */ } STRUCT_XMLRPC_VECTOR; /******/ diff --git a/ext/rpc/xmlrpc/xmlrpc-epi-php.c b/ext/rpc/xmlrpc/xmlrpc-epi-php.c index 8cd552c1f4..c73fe67413 100644 --- a/ext/rpc/xmlrpc/xmlrpc-epi-php.c +++ b/ext/rpc/xmlrpc/xmlrpc-epi-php.c @@ -52,13 +52,22 @@ +----------------------------------------------------------------------+ */ +/********************************************************************** +* BUGS: * +* - when calling a php user function, there appears to be no way to * +* distinguish between a return value of null, and no return value * +* at all. The xml serialization layer(s) will then return a value * +* of null, when the right thing may be no value at all. (SOAP) * +**********************************************************************/ + #include "php.h" #include "ext/standard/info.h" #include "php_ini.h" #include "php_xmlrpc.h" +#include "php_config.h" #include "xmlrpc.h" -#define PHP_EXT_VERSION "0.41" +#define PHP_EXT_VERSION "0.50" /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. @@ -125,6 +134,7 @@ typedef struct _xmlrpc_server_data { // how to format output typedef struct _php_output_options { int b_php_out; + int b_auto_version; STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; } php_output_options; @@ -161,6 +171,8 @@ typedef struct _xmlrpc_callback_data { #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1) #define VERSION_VALUE_SIMPLE "simple" #define VERSION_VALUE_XMLRPC "xmlrpc" +#define VERSION_VALUE_SOAP11 "soap 1.1" +#define VERSION_VALUE_AUTO "auto" #define ENCODING_KEY "encoding" #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1) @@ -327,6 +339,7 @@ static void set_output_options(php_output_options* options, pval* output_opts) { /* defaults */ options->b_php_out = 0; + options->b_auto_version = 1; options->xmlrpc_out.version = xmlrpc_version_1_0; options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT; options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; @@ -335,7 +348,7 @@ static void set_output_options(php_output_options* options, pval* output_opts) { if(output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) { pval** val; - /* verbosity of generated xml */ + /* type of output (xml/php) */ if(zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**)&val) == SUCCESS) { @@ -371,12 +384,19 @@ static void set_output_options(php_output_options* options, pval* output_opts) { VERSION_KEY, VERSION_KEY_LEN + 1, (void**)&val) == SUCCESS) { if(Z_TYPE_PP(val) == IS_STRING) { + options->b_auto_version = 0; if(!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) { options->xmlrpc_out.version = xmlrpc_version_1_0; } else if(!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) { options->xmlrpc_out.version = xmlrpc_version_simple; } + else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) { + options->xmlrpc_out.version = xmlrpc_version_soap_1_1; + } + else { // if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { + options->b_auto_version = 1; + } } } @@ -493,7 +513,8 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int dept switch(type) { case xmlrpc_base64: if(Z_TYPE_P(val) == IS_NULL) { - xReturn = XMLRPC_CreateValueBase64(key, "", 1); + xReturn = XMLRPC_CreateValueEmpty(); + XMLRPC_SetValueID(xReturn, key, 0); } else { xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val)); @@ -645,6 +666,7 @@ PHP_FUNCTION(xmlrpc_encode_request) { set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0); + if(return_value_used) { xRequest = XMLRPC_RequestNew(); @@ -874,6 +896,8 @@ static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRe call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC); pData->php_executed = 1; + + return NULL; } /* called by the C server when it first receives an introspection request. We pass this on to @@ -1052,19 +1076,21 @@ PHP_FUNCTION(xmlrpc_server_call_method) { xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(rawxml), Z_STRLEN_P(rawxml), &input_opts); if(xRequest) { - - /* check if we have a method name -- indicating success and all manner of good things */ - if(XMLRPC_RequestGetMethodName(xRequest)) { - pval** php_function; + const char* methodname = XMLRPC_RequestGetMethodName(xRequest); + pval** php_function; XMLRPC_VALUE xAnswer = NULL; MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */ MAKE_STD_ZVAL(data.return_data); Z_TYPE_P(data.return_data) = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ Z_TYPE_P(data.xmlrpc_method) = IS_NULL; - /* setup some data to pass to the callback function */ - Z_STRVAL_P(data.xmlrpc_method) = estrdup(XMLRPC_RequestGetMethodName(xRequest)); - Z_STRLEN_P(data.xmlrpc_method) = strlen(Z_STRVAL_P(data.xmlrpc_method)); + if (!methodname) { + methodname = ""; + } + + /* setup some data to pass to the callback function */ + Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname); + Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname); Z_TYPE_P(data.xmlrpc_method) = IS_STRING; data.caller_params = caller_params; data.php_executed = 0; @@ -1104,10 +1130,26 @@ PHP_FUNCTION(xmlrpc_server_call_method) { char* outBuf = 0; int buf_len = 0; + /* automagically determine output serialization type from request type */ + if (out.b_auto_version) { + XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest); + if (opts) { + out.xmlrpc_out.version = opts->version; + } + } + + /* automagically determine output serialization type from request type */ + if (out.b_auto_version) { + XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest); + if (opts) { + out.xmlrpc_out.version = opts->version; + } + } /* set some required request hoojum */ XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); XMLRPC_RequestSetData(xResponse, xAnswer); + XMLRPC_RequestSetMethodName(xResponse, methodname); /* generate xml */ outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len); @@ -1134,7 +1176,6 @@ PHP_FUNCTION(xmlrpc_server_call_method) { if(xAnswer) { XMLRPC_CleanupValue(xAnswer); } - } XMLRPC_RequestFree(xRequest, 1); } diff --git a/ext/xmlrpc/libxmlrpc/Makefile.in b/ext/xmlrpc/libxmlrpc/Makefile.in index 44257c4882..5c9d4f4aa9 100644 --- a/ext/xmlrpc/libxmlrpc/Makefile.in +++ b/ext/xmlrpc/libxmlrpc/Makefile.in @@ -2,8 +2,9 @@ LTLIBRARY_NAME = libxmlrpc.la LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \ xmlrpc_introspection.c encodings.c system_methods.c \ - xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c + xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c \ + xml_to_soap.c -DEFS = -DVERSION="0.41" +DEFS = -DVERSION="0.50" include $(top_srcdir)/build/dynlib.mk diff --git a/ext/xmlrpc/libxmlrpc/queue.c b/ext/xmlrpc/libxmlrpc/queue.c index 25d62b96a7..7ae4c46078 100644 --- a/ext/xmlrpc/libxmlrpc/queue.c +++ b/ext/xmlrpc/libxmlrpc/queue.c @@ -476,7 +476,7 @@ void *Q_Next(queue *q) if(!q) return NULL; - if(q->cursor->next == NULL) + if(!q->cursor || q->cursor->next == NULL) return NULL; q->cursor = (node *)q->cursor->next; diff --git a/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c index 0d827a5fb0..773eca5ecc 100644 --- a/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c +++ b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c @@ -113,7 +113,7 @@ XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLR XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_ARRAY)) { - XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array); } else if(!strcmp(type, ATTR_STRUCT)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); diff --git a/ext/xmlrpc/libxmlrpc/xml_to_soap.c b/ext/xmlrpc/libxmlrpc/xml_to_soap.c new file mode 100644 index 0000000000..6aae027313 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_to_soap.c @@ -0,0 +1,670 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) +*/ + + +/************************************************************************ +* TODO: * +* - [SOAP-ENC:position] read sparse arrays (and write?) * +* - [SOAP-ENC:offset] read partially transmitted arrays (and write?) * +* - read "flattened" multi-dimensional arrays. (don't bother writing) * +* * +* BUGS: * +* - does not read schema. thus only knows soap pre-defined types. * +* - references (probably) do not work. untested. * +* - does not expose SOAP-ENV:Header to application at all. * +* - does not use namespaces correctly, thus: * +* - namespaces are hard-coded in comparison tokens * +* - if a sender uses another namespace identifer, it will break * +************************************************************************/ + + +static const char rcsid[] = "#(@) $Id:"; + +#include +#include +#include "xml_to_soap.h" +#include "base64.h" + +/* list of tokens used in vocab */ +#define TOKEN_ANY "xsd:ur-type" +#define TOKEN_ARRAY "SOAP-ENC:Array" +#define TOKEN_ARRAY_TYPE "SOAP-ENC:arrayType" +#define TOKEN_BASE64 "SOAP-ENC:base64" +#define TOKEN_BOOLEAN "xsd:boolean" +#define TOKEN_DATETIME "xsd:timeInstant" +#define TOKEN_DOUBLE "xsd:double" +#define TOKEN_FLOAT "xsd:float" +#define TOKEN_ID "id" +#define TOKEN_INT "xsd:int" +#define TOKEN_NULL "xsi:null" +#define TOKEN_STRING "xsd:string" +#define TOKEN_STRUCT "xsd:struct" +#define TOKEN_TYPE "xsi:type" +#define TOKEN_FAULT "SOAP-ENV:Fault" +#define TOKEN_MUSTUNDERSTAND "SOAP-ENV:mustUnderstand" +#define TOKEN_ACTOR "SOAP-ENV:actor" +#define TOKEN_ACTOR_NEXT "http://schemas.xmlsoap.org/soap/actor/next" + +#define TOKEN_XMLRPC_FAULTCODE "faultCode" +#define TOKEN_XMLRPC_FAULTSTRING "faultString" +#define TOKEN_SOAP_FAULTCODE "faultcode" +#define TOKEN_SOAP_FAULTSTRING "faultstring" +#define TOKEN_SOAP_FAULTDETAILS "details" +#define TOKEN_SOAP_FAULTACTOR "actor" + + +// determine if a string represents a soap type, as used in +// element names +static inline int is_soap_type(const char* soap_type) { + return(strstr(soap_type, "SOAP-ENC:") || strstr(soap_type, "xsd:")) ? 1 : 0; +} + +/* utility func to generate a new attribute. possibly should be in xml_element.c?? */ +static xml_element_attr* new_attr(const char* key, const char* val) { + xml_element_attr* attr = malloc(sizeof(xml_element_attr)); + if (attr) { + attr->key = key ? strdup(key) : NULL; + attr->val = val ? strdup(val) : NULL; + } + return attr; +} + +struct array_info { + char kids_type[30]; + unsigned long size; + /* ... ? */ +}; + + +/* parses soap arrayType attribute to generate an array_info structure. + * TODO: should deal with sparse, flattened, & multi-dimensional arrays + */ +static struct array_info* parse_array_type_info(const char* array_type) { + struct array_info* ai = NULL; + if (array_type) { + ai = (struct array_info*)calloc(1, sizeof(struct array_info)); + if (ai) { + char buf[128], *p; + snprintf(buf, sizeof(buf), "%s", array_type); + p = strchr(buf, '['); + if (p) { + *p = 0; + } + strcpy(ai->kids_type, buf); + } + } + return ai; +} + +/* performs heuristics on an xmlrpc_vector_array to determine + * appropriate soap arrayType string. + */ +static const char* get_array_soap_type(XMLRPC_VALUE node) { + XMLRPC_VALUE_TYPE_EASY type = xmlrpc_type_none; + + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + int loopCount = 0; + const char* soapType = TOKEN_ANY; + + type = XMLRPC_GetValueTypeEasy(xIter); + xIter = XMLRPC_VectorNext(node); + + while (xIter) { + /* 50 seems like a decent # of loops. That will likely + * cover most cases. Any more and we start to sacrifice + * performance. + */ + if ( (XMLRPC_GetValueTypeEasy(xIter) != type) || loopCount >= 50) { + type = xmlrpc_type_none; + break; + } + loopCount ++; + + xIter = XMLRPC_VectorNext(node); + } + switch (type) { + case xmlrpc_type_none: + soapType = TOKEN_ANY; + break; + case xmlrpc_type_empty: + soapType = TOKEN_NULL; + break; + case xmlrpc_type_int: + soapType = TOKEN_INT; + break; + case xmlrpc_type_double: + soapType = TOKEN_DOUBLE; + break; + case xmlrpc_type_boolean: + soapType = TOKEN_BOOLEAN; + break; + case xmlrpc_type_string: + soapType = TOKEN_STRING; + break; + case xmlrpc_type_base64: + soapType = TOKEN_BASE64; + break; + case xmlrpc_type_datetime: + soapType = TOKEN_DATETIME; + break; + case xmlrpc_type_struct: + soapType = TOKEN_STRUCT; + break; + case xmlrpc_type_array: + soapType = TOKEN_ARRAY; + break; + case xmlrpc_type_mixed: + soapType = TOKEN_STRUCT; + break; + } + return soapType; +} + +/* determines wether a node is a fault or not, and of which type: + * 0 = not a fault, + * 1 = xmlrpc style fault + * 2 = soap style fault. + */ +static inline int get_fault_type(XMLRPC_VALUE node) { + if (XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTCODE) && + XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTSTRING)) { + return 1; + } + else if (XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTCODE) && + XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTSTRING)) { + return 2; + } + return 0; +} + +/* input: an XMLRPC_VALUE representing a fault struct in xml-rpc style. + * output: an XMLRPC_VALUE representing a fault struct in soap style, + * with xmlrpc codes mapped to soap codes, and all other values preserved. + * note that the returned value is a completely new value, and must be freed. + * the input value is untouched. + */ +static XMLRPC_VALUE gen_fault_xmlrpc(XMLRPC_VALUE node, xml_element* el_target) { + XMLRPC_VALUE xDup = XMLRPC_DupValueNew(node); + XMLRPC_VALUE xCode = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTCODE); + XMLRPC_VALUE xStr = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTSTRING); + + XMLRPC_SetValueID(xCode, TOKEN_SOAP_FAULTCODE, 0); + XMLRPC_SetValueID(xStr, TOKEN_SOAP_FAULTSTRING, 0); + + /* rough mapping of xmlrpc fault codes to soap codes */ + switch (XMLRPC_GetValueInt(xCode)) { + case -32700: // "parse error. not well formed", + case -32701: // "parse error. unsupported encoding" + case -32702: // "parse error. invalid character for encoding" + case -32600: // "server error. invalid xml-rpc. not conforming to spec." + case -32601: // "server error. requested method not found" + case -32602: // "server error. invalid method parameters" + XMLRPC_SetValueString(xCode, "SOAP-ENV:Client", 0); + break; + case -32603: // "server error. internal xml-rpc error" + case -32500: // "application error" + case -32400: // "system error" + case -32300: // "transport error + XMLRPC_SetValueString(xCode, "SOAP-ENV:Server", 0); + break; + } + return xDup; +} + +// returns a new XMLRPC_VALUE representing a soap fault, comprised of a struct with four keys. +static XMLRPC_VALUE gen_soap_fault(const char* fault_code, const char* fault_string, + const char* actor, const char* details) { + XMLRPC_VALUE xReturn = XMLRPC_CreateVector(TOKEN_FAULT, xmlrpc_vector_struct); + XMLRPC_AddValuesToVector(xReturn, + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTCODE, fault_code, 0), + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTSTRING, fault_string, 0), + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTACTOR, actor, 0), + XMLRPC_CreateValueString(TOKEN_SOAP_FAULTDETAILS, details, 0), + NULL); + return xReturn; +} + +/* translates xml soap dom to native data structures. recursive. */ +XMLRPC_VALUE xml_element_to_SOAP_REQUEST_worker(XMLRPC_REQUEST request, + XMLRPC_VALUE xParent, + struct array_info* parent_array, + XMLRPC_VALUE xCurrent, + xml_element* el, + int depth) { + XMLRPC_REQUEST_TYPE rtype = xmlrpc_request_none; + + // no current element on first call + if (!xCurrent) { + xCurrent = XMLRPC_CreateValueEmpty(); + } + + // increment recursion depth guage + depth ++; + + // safety first. must have a valid element + if (el && el->name) { + const char* id = NULL; + const char* type = NULL, *arrayType=NULL, *actor = NULL; + xml_element_attr* attr_iter = Q_Head(&el->attrs); + int b_must_understand = 0; + + // in soap, types may be specified in either element name -or- with xsi:type attribute. + if (is_soap_type(el->name)) { + type = el->name; + } + // if our parent node, by definition a vector, is not an array, then + // our element name must be our key identifier. + else if (XMLRPC_GetVectorType(xParent) != xmlrpc_vector_array) { + id = el->name; + if(!strcmp(id, "item")) { + } + } + + // iterate through element attributes, pick out useful stuff. + while (attr_iter) { + // element's type + if (!strcmp(attr_iter->key, TOKEN_TYPE)) { + type = attr_iter->val; + } + // array type + else if (!strcmp(attr_iter->key, TOKEN_ARRAY_TYPE)) { + arrayType = attr_iter->val; + } + // must understand, sometimes present in headers. + else if (!strcmp(attr_iter->key, TOKEN_MUSTUNDERSTAND)) { + b_must_understand = strchr(attr_iter->val, '1') ? 1 : 0; + } + // actor, used in conjuction with must understand. + else if (!strcmp(attr_iter->key, TOKEN_ACTOR)) { + actor = attr_iter->val; + } + attr_iter = Q_Next(&el->attrs); + } + + // check if caller says we must understand something in a header. + if (b_must_understand) { + // is must understand actually indended for us? + // BUG: spec says we should also determine if actor is our URL, but + // we do not have that information. + if (!actor || !strcmp(actor, TOKEN_ACTOR_NEXT)) { + // TODO: implement callbacks or other mechanism for applications + // to "understand" these headers. For now, we just bail if we + // get a mustUnderstand header intended for us. + XMLRPC_RequestSetError(request, + gen_soap_fault("SOAP-ENV:MustUnderstand", + "SOAP Must Understand Error", + "", "")); + return xCurrent; + } + } + + // set id (key) if one was found. + if (id) { + XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); + } + + // according to soap spec, + // depth 1 = Envelope, 2 = Header, Body & Fault, 3 = methodcall or response. + if (depth == 3) { + const char* methodname = el->name; + char* p = NULL; + + // BUG: we determine request or response type using presence of "Response" in element name. + // According to spec, this is only recommended, not required. Apparently, implementations + // are supposed to know the type of action based on state, which strikes me as a bit lame. + // Anyway, we don't have that state info, thus we use Response as a heuristic. + rtype = +#ifdef strcasestr + strcasestr(el->name, "response") ? xmlrpc_request_response : xmlrpc_request_call; +#else + strstr(el->name, "esponse") ? xmlrpc_request_response : xmlrpc_request_call; +#endif + XMLRPC_RequestSetRequestType(request, rtype); + + // Get methodname. strip xml namespace crap. + p = strchr(el->name, ':'); + if (p) { + methodname = p + 1; + } + if (rtype == xmlrpc_request_call) { + XMLRPC_RequestSetMethodName(request, methodname); + } + } + + + // Next, we begin to convert actual values. + // if no children, then must be a scalar value. + if (!Q_Size(&el->children)) { + if (!type && parent_array && parent_array->kids_type[0]) { + type = parent_array->kids_type; + } + if (!type || !strcmp(type, TOKEN_STRING)) { + XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); + } + else if (!strcmp(type, TOKEN_INT)) { + XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); + } + else if (!strcmp(type, TOKEN_BOOLEAN)) { + XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); + } + else if (!strcmp(type, TOKEN_DOUBLE) || + !strcmp(type, TOKEN_FLOAT)) { + XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); + } + else if (!strcmp(type, TOKEN_NULL)) { + // already an empty val. do nothing. + } + else if (!strcmp(type, TOKEN_DATETIME)) { + XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); + } + else if (!strcmp(type, TOKEN_BASE64)) { + struct buffer_st buf; + base64_decode(&buf, el->text.str, el->text.len); + XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset); + buffer_delete(&buf); + } + } + // Element has children, thus a vector, or "compound type" in soap-speak. + else { + struct array_info* ai = NULL; + xml_element* iter = (xml_element*)Q_Head(&el->children); + + if (!type || !strcmp(type, TOKEN_STRUCT)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); + } + else if (!strcmp(type, TOKEN_ARRAY) || arrayType != NULL) { + // determine magic associated with soap array type. + // this is passed down as we recurse, so our children have access to the info. + ai = parse_array_type_info(arrayType); // alloc'ed ai free'd below. + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array); + } + else { + // mixed is probably closest thing we have to compound type. + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + } + // Recurse, adding values as we go. Check for error during recursion + // and if found, bail. this short-circuits us out of the recursion. + while ( iter && !XMLRPC_RequestGetError(request) ) { + XMLRPC_VALUE xNext = NULL; + // top level elements don't actually represent values, so we just pass the + // current value along until we are deep enough. + if ( depth <= 2 || + (rtype == xmlrpc_request_response && depth <= 3) ) { + xml_element_to_SOAP_REQUEST_worker(request, NULL, ai, xCurrent, iter, depth); + } + // ready to do some actual de-serialization. create a new empty value and + // pass that along to be init'd, then add it to our current vector. + else { + xNext = XMLRPC_CreateValueEmpty(); + xml_element_to_SOAP_REQUEST_worker(request, xCurrent, ai, xNext, iter, depth); + XMLRPC_AddValueToVector(xCurrent, xNext); + } + iter = (xml_element*)Q_Next(&el->children); + } + // cleanup + if (ai) { + free(ai); + } + } + } + return xCurrent; +} + +// Convert soap xml dom to XMLRPC_VALUE, sans request info. untested. +XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el) +{ + return xml_element_to_SOAP_REQUEST_worker(NULL, NULL, NULL, NULL, el, 0); +} + +// Convert soap xml dom to XMLRPC_REQUEST +XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el) +{ + if (request) { + return XMLRPC_RequestSetData(request, xml_element_to_SOAP_REQUEST_worker(request, NULL, NULL, NULL, el, 0)); + } + return NULL; +} + + +/* translates data structures to soap/xml. recursive */ +xml_element* SOAP_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { +#define BUF_SIZE 128 + xml_element* elem_val = NULL; + if (node) { + int bFreeNode = 0; /* sometimes we may need to free 'node' variable */ + char buf[BUF_SIZE]; + XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(node); + char* pName = NULL, *pAttrType = NULL; + + // create our return value element + elem_val = xml_elem_new(); + + switch (type) { + case xmlrpc_type_struct: + case xmlrpc_type_mixed: + case xmlrpc_type_array: + if (type == xmlrpc_type_array) { + // array's are _very_ special in soap. + // TODO: Should handle sparse/partial arrays here. + + // determine soap array type. + const char* type = get_array_soap_type(node); + xml_element_attr* attr_array_type = NULL; + + // specify array kids type and array size. + snprintf(buf, sizeof(buf), "%s[%i]", type, XMLRPC_VectorSize(node)); + attr_array_type = new_attr(TOKEN_ARRAY_TYPE, buf); + + Q_PushTail(&elem_val->attrs, attr_array_type); + + pAttrType = TOKEN_ARRAY; + } + // check for fault, which is a rather special case. + // (can't these people design anything consistent/simple/elegant?) + else if (type == xmlrpc_type_struct) { + int fault_type = get_fault_type(node); + if (fault_type) { + if (fault_type == 1) { + // gen fault from xmlrpc style fault codes + // notice that we get a new node, which must be freed herein. + node = gen_fault_xmlrpc(node, elem_val); + bFreeNode = 1; + } + pName = TOKEN_FAULT; + } + } + + { + /* recurse through sub-elements */ + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + while ( xIter ) { + xml_element* next_el = SOAP_to_xml_element_worker(request, xIter); + if (next_el) { + Q_PushTail(&elem_val->children, next_el); + } + xIter = XMLRPC_VectorNext(node); + } + } + + break; + + // handle scalar types + case xmlrpc_type_empty: + pAttrType = TOKEN_NULL; + break; + case xmlrpc_type_string: + pAttrType = TOKEN_STRING; + simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); + break; + case xmlrpc_type_int: + pAttrType = TOKEN_INT; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_type_boolean: + pAttrType = TOKEN_BOOLEAN; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_type_double: + pAttrType = TOKEN_DOUBLE; + snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_type_datetime: + { + time_t tt = XMLRPC_GetValueDateTime(node); + struct tm *tm = localtime (&tt); + pAttrType = TOKEN_DATETIME; + if(strftime (buf, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", tm)) { + simplestring_add(&elem_val->text, buf); + } + } + break; + case xmlrpc_type_base64: + { + struct buffer_st buf; + pAttrType = TOKEN_BASE64; + base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); + simplestring_addn(&elem_val->text, buf.data, buf.offset ); + buffer_delete(&buf); + } + break; + break; + default: + break; + } + + // determining element's name is a bit tricky, due to soap semantics. + if (!pName) { + // if the value's type is known... + if (pAttrType) { + // see if it has an id (key). If so, use that as name, + // and type as an attribute. + pName = (char*)XMLRPC_GetValueID(node); + if (pName) { + Q_PushTail(&elem_val->attrs, new_attr(TOKEN_TYPE, pAttrType)); + } + + // otherwise, use the type as the name. + else { + pName = pAttrType; + } + } + // if the value's type is not known... (a rare case?) + else { + // see if it has an id (key). otherwise, default to generic "item" + pName = (char*)XMLRPC_GetValueID(node); + if (!pName) { + pName = "item"; + } + } + } + elem_val->name = strdup(pName); + + // cleanup + if (bFreeNode) { + XMLRPC_CleanupValue(node); + } + } + return elem_val; +} + +// convert XMLRPC_VALUE to soap xml dom. untested. +xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node) { + return SOAP_to_xml_element_worker(NULL, node); +} + +// convert XMLRPC_REQUEST to soap xml dom. +xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request) { + xml_element* root = xml_elem_new(); + + // safety first. + if (root) { + xml_element* body = xml_elem_new(); + root->name = strdup("SOAP-ENV:Envelope"); + + /* silly namespace stuff */ + Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/")); + Q_PushTail(&root->attrs, new_attr("xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance")); + Q_PushTail(&root->attrs, new_attr("xmlns:xsd", "http://www.w3.org/1999/XMLSchema")); + Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/")); + Q_PushTail(&root->attrs, new_attr("xmlns:si", "http://soapinterop.org/xsd")); + Q_PushTail(&root->attrs, new_attr("xmlns:ns6", "http://testuri.org")); + Q_PushTail(&root->attrs, new_attr("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/")); + + //Q_PushHead(&root->attrs, new_attr("xmlns:ks", "http://kitchen.sink.org/soap/everything/under/sun")); + // JUST KIDDING!! :-) ----> ------------------------------------------------- + + if (body) { + // go ahead and serialize first... + xml_element* el_serialized = + SOAP_to_xml_element_worker(request, + XMLRPC_RequestGetData(request)); + + /* check for fault, in which case, there is no intermediate element */ + if (el_serialized && !strcmp(el_serialized->name, TOKEN_FAULT)) { + Q_PushTail(&body->children, el_serialized); + } + // usual case: not a fault. Add Response element in between. + else { + xml_element* rpc = xml_elem_new(); + + if (rpc) { + const char* methodname = XMLRPC_RequestGetMethodName(request); + XMLRPC_REQUEST_TYPE rtype = XMLRPC_RequestGetRequestType(request); + + // if we are making a request, we want to use the methodname as is. + if (rtype == xmlrpc_request_call) { + if (methodname) { + rpc->name = strdup(methodname); + } + } + // if it's a response, we append "Response". Also, given xmlrpc-epi + // API/architecture, it's likely that we don't have a methodname for + // the response, so we have to check that. + else { + char buf[128]; + snprintf(buf, sizeof(buf), "%s%s", + methodname ? methodname : "", + "Response"); + + rpc->name = strdup(buf); + } + + // add serialized data to method call/response. + // add method call/response to body element + if (rpc->name) { + if(el_serialized) { + if(Q_Size(&el_serialized->children) && rtype == xmlrpc_request_call) { + xml_element* iter = (xml_element*)Q_Head(&el_serialized->children); + while(iter) { + Q_PushTail(&rpc->children, iter); + iter = (xml_element*)Q_Next(&el_serialized->children); + } + xml_elem_free_non_recurse(el_serialized); + } + else { + Q_PushTail(&rpc->children, el_serialized); + } + } + + Q_PushTail(&body->children, rpc); + } + else { + // no method name?! + // TODO: fault here...? + } + } + } + body->name = strdup("SOAP-ENV:Body"); + Q_PushTail(&root->children, body); + } + } + + return root; +} + diff --git a/ext/xmlrpc/libxmlrpc/xml_to_soap.h b/ext/xmlrpc/libxmlrpc/xml_to_soap.h new file mode 100644 index 0000000000..9ae9308b22 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_to_soap.h @@ -0,0 +1,44 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +#ifndef XML_TO_SOAP_H + #define XML_TO_SOAP_H + +#include "xmlrpc.h" + +XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el); +XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el); +xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node); +xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request); + +#endif /* XML_TO_XMLRPC_H */ diff --git a/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c index 1cd3d9ac2b..02f3d430cd 100644 --- a/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c +++ b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c @@ -82,7 +82,8 @@ XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } - } else if (!strcmp(el->name, ELEM_STRUCT)) { + } + else if (!strcmp(el->name, ELEM_STRUCT)) { xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); @@ -92,36 +93,46 @@ XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } - } else if (!strcmp(el->name, ELEM_STRING) || + } + else if (!strcmp(el->name, ELEM_STRING) || (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { XMLRPC_SetValueString(current_val, el->text.str, el->text.len); - } else if (!strcmp(el->name, ELEM_NAME)) { + } + else if (!strcmp(el->name, ELEM_NAME)) { XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); - } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { + } + else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { XMLRPC_SetValueInt(current_val, atoi(el->text.str)); - } else if (!strcmp(el->name, ELEM_BOOLEAN)) { + } + else if (!strcmp(el->name, ELEM_BOOLEAN)) { XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); - } else if (!strcmp(el->name, ELEM_DOUBLE)) { + } + else if (!strcmp(el->name, ELEM_DOUBLE)) { XMLRPC_SetValueDouble(current_val, atof(el->text.str)); - } else if (!strcmp(el->name, ELEM_DATETIME)) { + } + else if (!strcmp(el->name, ELEM_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); - } else if (!strcmp(el->name, ELEM_BASE64)) { + } + else if (!strcmp(el->name, ELEM_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); buffer_delete(&buf); - } else { + } + else { xml_element* iter; if (!strcmp(el->name, ELEM_METHODCALL)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } - } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { + } + else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } - } else if (!strcmp(el->name, ELEM_METHODNAME)) { + } + else if (!strcmp(el->name, ELEM_METHODNAME)) { if (request) { XMLRPC_RequestSetMethodName(request, el->text.str); } @@ -173,8 +184,10 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA Q_PushTail(&elem_val->children, next_el); } elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); - } else { + } + else { switch (type) { + case xmlrpc_empty: // treat null value as empty string in xmlrpc. case xmlrpc_string: elem_val->name = strdup(ELEM_STRING); simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); @@ -270,7 +283,8 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA /* yet another hack for the "fault" crap */ if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { root = value; - } else { + } + else { xml_element* param = xml_elem_new(); param->name = strdup(ELEM_PARAM); @@ -279,7 +293,8 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA root = param; } Q_PushTail(&value->children, elem_val); - } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { + } + else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { xml_element* member = xml_elem_new(); xml_element* name = xml_elem_new(); xml_element* value = xml_elem_new(); @@ -295,7 +310,8 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA Q_PushTail(&value->children, elem_val); root = member; - } else if (vtype == xmlrpc_vector_array) { + } + else if (vtype == xmlrpc_vector_array) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); @@ -303,10 +319,12 @@ xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VA Q_PushTail(&value->children, elem_val); root = value; - } else if (vtype == xmlrpc_vector_none) { + } + else if (vtype == xmlrpc_vector_none) { /* no parent. non-op */ root = elem_val; - } else { + } + else { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); @@ -335,13 +353,15 @@ xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { if (request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; - } else if (request_type == xmlrpc_request_response) { + } + else if (request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } if (pStr) { wrapper->name = strdup(pStr); } + if(request_type == xmlrpc_request_call) { pStr = XMLRPC_RequestGetMethodName(request); if (pStr) { @@ -350,10 +370,12 @@ xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } + } if (xParams) { Q_PushTail(&wrapper->children, XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); - } else { + } + else { /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ xml_element* params = xml_elem_new(); params->name = strdup(ELEM_PARAMS); diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.c b/ext/xmlrpc/libxmlrpc/xmlrpc.c index f95ac949b3..596ca622d3 100644 --- a/ext/xmlrpc/libxmlrpc/xmlrpc.c +++ b/ext/xmlrpc/libxmlrpc/xmlrpc.c @@ -109,6 +109,7 @@ static const char rcsid[] = "#(@) $Id$"; #include "xml_to_xmlrpc.h" #include "xml_to_dandarpc.h" +#include "xml_to_soap.h" #include "xml_element.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" @@ -119,12 +120,25 @@ static const char rcsid[] = "#(@) $Id$"; * Begin Time Functions * ***********************/ -static int date_from_ISO8601(const char *text, time_t *value) -{ +static int date_from_ISO8601 (const char *text, time_t * value) { struct tm tm; int n; int i; time_t t; + char buf[18]; + + if (strchr (text, '-')) { + char *p = (char *) text, *p2 = buf; + while (p && *p) { + if (*p != '-') { + *p2 = *p; + p2++; + } + p++; + } + text = buf; + } + tm.tm_isdst = -1; @@ -182,12 +196,14 @@ static int date_from_ISO8601(const char *text, time_t *value) } -static int date_to_ISO8601(time_t value, char *buf, int length) -{ +static int date_to_ISO8601 (time_t value, char *buf, int length) { struct tm *tm; tm = localtime(&value); - +#if 0 // TODO: soap seems to favor this method. xmlrpc the latter. + return strftime (buf, length, "%Y-%m-%dT%H:%M:%SZ", tm); +#else return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm); +#endif } /*-******************* @@ -219,6 +235,7 @@ XMLRPC_REQUEST XMLRPC_RequestNew() { } return xRequest; } + /*******/ /****f* REQUEST/XMLRPC_RequestFree @@ -249,6 +266,7 @@ void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) { my_free(request); } } + /*******/ /* Set Method Name to call */ @@ -276,6 +294,7 @@ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* meth } return NULL; } + /*******/ /****f* REQUEST/XMLRPC_RequestGetMethodName @@ -296,6 +315,7 @@ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* meth const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { return request ? request->methodName.str : NULL; } + /*******/ /****f* REQUEST/XMLRPC_RequestSetRequestType @@ -317,13 +337,15 @@ const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { * XMLRPC_REQUEST_TYPE * SOURCE */ -XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) { +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 @@ -349,6 +371,7 @@ XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_ XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { return request ? request->request_type : xmlrpc_request_none; } + /*******/ @@ -377,11 +400,15 @@ XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { */ XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { if(request && data) { + if (request->io) { + XMLRPC_CleanupValue (request->io); + } request->io = XMLRPC_CopyValue(data); return request->io; } return NULL; } + /*******/ /****f* REQUEST/XMLRPC_RequestGetData @@ -406,8 +433,66 @@ XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { return request ? request->io : NULL; } + +/*******/ + +/****f* REQUEST/XMLRPC_RequestSetError + * NAME + * XMLRPC_RequestSetError + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestSetError(XMLRPC_REQUEST request, XMLRPC_VALUE error) + * FUNCTION + * Associates a block of xmlrpc data, representing an error + * condition, with the request. + * INPUTS + * request -- previously allocated request struct + * error -- previously allocated error code or struct + * RESULT + * XMLRPC_VALUE -- pointer to value stored, or NULL + * NOTES + * This is a private function for usage by internals only. + * SEE ALSO + * XMLRPC_RequestGetError () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestSetError (XMLRPC_REQUEST request, XMLRPC_VALUE error) { + if (request && error) { + if (request->error) { + XMLRPC_CleanupValue (request->error); + } + request->error = XMLRPC_CopyValue (error); + return request->error; + } + return NULL; +} + +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetError + * NAME + * XMLRPC_RequestGetError + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestGetError(XMLRPC_REQUEST request) + * FUNCTION + * Returns error data associated with request, if any. + * INPUTS + * request -- previously allocated request struct + * RESULT + * XMLRPC_VALUE -- pointer to error value stored, or NULL + * NOTES + * This is a private function for usage by internals only. + * SEE ALSO + * XMLRPC_RequestSetError () + * XMLRPC_RequestFree () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestGetError (XMLRPC_REQUEST request) { + return request ? request->error : NULL; +} + /*******/ + /****f* REQUEST/XMLRPC_RequestSetOutputOptions * NAME * XMLRPC_RequestSetOutputOptions @@ -431,11 +516,13 @@ XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { */ 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)); + memcpy (&request->output, output, + sizeof (STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS)); return &request->output; } return NULL; } + /*******/ @@ -461,6 +548,7 @@ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST requ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) { return request ? &request->output : NULL; } + /*******/ /*-************************* @@ -503,6 +591,7 @@ char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { } return pRet; } + /*******/ /****f* SERIALIZE/XMLRPC_REQUEST_ToXML @@ -527,19 +616,30 @@ char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { * 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 (request) { + xml_element *root_elem = NULL; + if (request->output.version == xmlrpc_version_simple) { + root_elem = DANDARPC_REQUEST_to_xml_element (request); + } + else if (request->output.version == xmlrpc_version_1_0) { + root_elem = XMLRPC_REQUEST_to_xml_element (request); + } + else if (request->output.version == xmlrpc_version_soap_1_1) { + root_elem = SOAP_REQUEST_to_xml_element (request); + } if(root_elem) { - pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len); + pRet = + xml_elem_serialize_to_string (root_elem, + &request->output.xml_elem_opts, + buf_len); xml_elem_free(root_elem); } - return pRet; } + return pRet; } + /*******/ /****f* SERIALIZE/XMLRPC_VALUE_FromXML @@ -562,8 +662,7 @@ char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) { * XMLRPC_VALUE * SOURCE */ -XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) -{ +XMLRPC_VALUE XMLRPC_VALUE_FromXML (const char *in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) { XMLRPC_VALUE xResponse = NULL; XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options); @@ -573,6 +672,7 @@ XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_IN } return xResponse; } + /*******/ /* map parser errors to standard xml-rpc errors */ @@ -583,8 +683,7 @@ static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { char buf[1024]; snprintf(buf, sizeof(buf), "error occurred at line %i, column %i, byte index %i", - error->line, error->column, - error->byte_index); + error->line, error->column, error->byte_index); /* expat specific errors */ switch(error->parser_code) { @@ -622,19 +721,26 @@ static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { * XMLRPC_REQUEST * SOURCE */ -XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) -{ +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); + xml_element *root_elem = + xml_elem_parse_buf (in_buf, len, + (in_options ? &in_options->xml_elem_opts : NULL), + &error); if(root_elem) { if(!strcmp(root_elem->name, "simpleRPC")) { request->output.version = xmlrpc_version_simple; xml_element_to_DANDARPC_REQUEST(request, root_elem); } + else if (!strcmp (root_elem->name, "SOAP-ENV:Envelope")) { + request->output.version = xmlrpc_version_soap_1_1; + xml_element_to_SOAP_REQUEST (request, root_elem); + } else { request->output.version = xmlrpc_version_1_0; xml_element_to_XMLRPC_REQUEST(request, root_elem); @@ -643,13 +749,14 @@ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUES } else { if(error.parser_error) { - request->error = map_expat_errors(&error); + XMLRPC_RequestSetError (request, map_expat_errors (&error)); } } } return request; } + /*******/ /*-************************ @@ -676,6 +783,9 @@ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUES XMLRPC_VALUE XMLRPC_CreateValueEmpty() { XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE)); if(v) { +#ifdef XMLRPC_DEBUG_REFCOUNT + printf ("calloc'd 0x%x\n", v); +#endif v->type = xmlrpc_empty; simplestring_init(&v->id); simplestring_init(&v->str); @@ -689,6 +799,7 @@ static const char* get_string(const char* buf, int bDup) { } return buf; } + /*******/ /****f* VALUE/XMLRPC_SetValueID_Case @@ -724,7 +835,12 @@ const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, 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]); + value->id.str[i] = + (id_case == + xmlrpc_case_lower) ? tolower (value->id. + str[i]) : toupper (value-> + id. + str[i]); } } @@ -738,6 +854,7 @@ const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, return pRetval; } + /*******/ @@ -772,6 +889,7 @@ const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) return pRetval; } + /*******/ /****f* VALUE/XMLRPC_SetValueInt @@ -797,6 +915,7 @@ void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) { value->i = val; } } + /*******/ /****f* VALUE/XMLRPC_SetValueBoolean @@ -822,6 +941,7 @@ void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { value->i = val ? 1 : 0; } } + /*******/ @@ -850,7 +970,16 @@ void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { int bSuccess = 0; - if(value && value->type != xmlrpc_vector) { + if (value) { + // we can change the type so long as nothing is currently stored. + if(value->type == xmlrpc_vector) { + if(value->v) { + if(!Q_Size(value->v->q)) { + value->v->type = type; + } + } + } + else { value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR)); if(value->v) { value->v->q = (queue*)malloc(sizeof(queue)); @@ -862,9 +991,11 @@ int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { } } } + } return bSuccess; } + /*******/ /****f* VECTOR/XMLRPC_CreateVector @@ -912,6 +1043,7 @@ XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) { } return val; } + /*******/ @@ -966,22 +1098,26 @@ int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) { 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))) { + 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"); + 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"); + fprintf (stderr, + "xmlrpc: attempted to add value of unknown type to vector\n"); break; } } } return 0; } + /*******/ @@ -1026,7 +1162,8 @@ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { break; } } - } while(v); + } + while (v); va_end(vl); @@ -1037,6 +1174,7 @@ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { } return iRetval; } + /*******/ @@ -1059,7 +1197,8 @@ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { * XMLRPC_CASE_COMPARISON * SOURCE */ -XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) { +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); @@ -1082,6 +1221,7 @@ XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* i } return NULL; } + /*******/ @@ -1136,6 +1276,7 @@ XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) } return value; } + /*******/ /****f* VALUE/XMLRPC_CreateValueInt @@ -1167,6 +1308,7 @@ XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) { } return val; } + /*******/ /****f* VALUE/XMLRPC_CreateValueBoolean @@ -1198,6 +1340,7 @@ XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) { } return val; } + /*******/ @@ -1235,10 +1378,12 @@ void XMLRPC_CleanupValue(XMLRPC_VALUE value) { #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { - printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount); + 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); + printf ("decremented refcount of 0x%x, now %i\n", value, + value->iRefCount); } #endif @@ -1295,12 +1440,14 @@ void XMLRPC_CleanupValue(XMLRPC_VALUE value) { my_free(value); break; default: - fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n"); + fprintf (stderr, + "xmlrpc: attempted to free value of invalid type\n"); break; } } } } + /*******/ @@ -1339,6 +1486,7 @@ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { } } } + /*******/ /****f* VALUE/XMLRPC_CopyValue @@ -1347,13 +1495,14 @@ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { * SYNOPSIS * XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) * FUNCTION - * Make a copy of an XMLRPC_VALUE + * Make a copy (reference) of an XMLRPC_VALUE * INPUTS * value The target XMLRPC_VALUE * RESULT * XMLRPC_VALUE -- address of the copy * SEE ALSO * XMLRPC_CleanupValue () + * XMLRPC_DupValueNew () * NOTES * This function is implemented via reference counting, so the * returned value is going to be the same as the passed in value. @@ -1366,12 +1515,83 @@ XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) { value->iRefCount ++; #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { - printf("incremented refcount of %s\n", value->id.str); + printf ("incremented refcount of %s, now %i\n", value->id.str, + value->iRefCount); + } + else { + printf ("incremented refcount of 0x%x, now %i\n", value, + value->iRefCount); } #endif } return value; } + +/*******/ + + +/****f* VALUE/XMLRPC_DupValueNew + * NAME + * XMLRPC_DupValueNew + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_DupValueNew(XMLRPC_VALUE value) + * FUNCTION + * Make a duplicate (non reference) of an XMLRPC_VALUE with newly allocated mem. + * INPUTS + * value The source XMLRPC_VALUE to duplicate + * RESULT + * XMLRPC_VALUE -- address of the duplicate value + * SEE ALSO + * XMLRPC_CleanupValue () + * XMLRPC_CopyValue () + * NOTES + * Use this when function when you need to modify the contents of + * the copied value seperately from the original. + * + * this function is recursive, thus the value and all of its children + * (if any) will be duplicated. + * SOURCE + */ +XMLRPC_VALUE XMLRPC_DupValueNew (XMLRPC_VALUE xSource) { + XMLRPC_VALUE xReturn = NULL; + if (xSource) { + xReturn = XMLRPC_CreateValueEmpty (); + if (xSource->id.len) { + XMLRPC_SetValueID (xReturn, xSource->id.str, xSource->id.len); + } + + switch (xSource->type) { + case xmlrpc_int: + case xmlrpc_boolean: + XMLRPC_SetValueInt (xReturn, xSource->i); + break; + case xmlrpc_string: + case xmlrpc_base64: + XMLRPC_SetValueString (xReturn, xSource->str.str, xSource->str.len); + break; + case xmlrpc_datetime: + XMLRPC_SetValueDateTime (xReturn, xSource->i); + break; + case xmlrpc_double: + XMLRPC_SetValueDouble (xReturn, xSource->d); + break; + case xmlrpc_vector: + { + q_iter qi = Q_Iter_Head_F (xSource->v->q); + XMLRPC_SetIsVector (xReturn, xSource->v->type); + + while (qi) { + XMLRPC_VALUE xIter = Q_Iter_Get_F (qi); + XMLRPC_AddValueToVector (xReturn, XMLRPC_DupValueNew (xIter)); + qi = Q_Iter_Next_F (qi); + } + } + break; + } + } + return xReturn; +} + /*******/ @@ -1405,6 +1625,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) { } return val; } + /*******/ @@ -1440,6 +1661,7 @@ void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) { } } } + /*******/ /****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601 @@ -1473,6 +1695,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) { } return val; } + /*******/ @@ -1506,6 +1729,7 @@ void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) { value->type = xmlrpc_base64; } } + /*******/ @@ -1540,6 +1764,7 @@ XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) { } return val; } + /*******/ /****f* VALUE/XMLRPC_SetValueDouble @@ -1566,6 +1791,7 @@ void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) { value->d = val; } } + /*******/ /****f* VALUE/XMLRPC_CreateValueDouble @@ -1596,6 +1822,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { } return val; } + /*******/ /****f* VALUE/XMLRPC_GetValueString @@ -1618,6 +1845,7 @@ XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_string) ? value->str.str : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueStringLen @@ -1640,6 +1868,7 @@ const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { return ((value) ? value->str.len : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueInt @@ -1663,6 +1892,7 @@ int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { int XMLRPC_GetValueInt(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_int) ? value->i : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueBoolean @@ -1686,6 +1916,7 @@ int XMLRPC_GetValueInt(XMLRPC_VALUE value) { int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_boolean) ? value->i : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueDouble @@ -1709,6 +1940,7 @@ int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_double) ? value->d : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueBase64 @@ -1733,6 +1965,7 @@ double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_base64) ? value->str.str : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueDateTime @@ -1757,6 +1990,7 @@ const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0); } + /*******/ /****f* VALUE/XMLRPC_GetValueDateTime_IOS8601 @@ -1779,6 +2013,7 @@ time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { 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 */ @@ -1802,6 +2037,7 @@ const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) { const char* XMLRPC_GetValueID(XMLRPC_VALUE value) { return (const char*)((value && value->id.len) ? value->id.str : 0); } + /*******/ @@ -1830,6 +2066,7 @@ int XMLRPC_VectorSize(XMLRPC_VALUE value) { } return size; } + /*******/ /****f* VECTOR/XMLRPC_VectorRewind @@ -1857,6 +2094,7 @@ XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) { } return xReturn; } + /*******/ /****f* VECTOR/XMLRPC_VectorNext @@ -1882,6 +2120,7 @@ XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { } return xReturn; } + /*******/ /****f* VALUE/XMLRPC_GetValueType @@ -1897,15 +2136,18 @@ XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { * data type of value as enumerated by XMLRPC_VALUE_TYPE * NOTES * all values are of type xmlrpc_empty until set. + * Deprecated for public use. See XMLRPC_GetValueTypeEasy * SEE ALSO * XMLRPC_SetValue* * XMLRPC_CreateValue* * XMLRPC_Append* + * XMLRPC_GetValueTypeEasy () * SOURCE */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { return value ? value->type : xmlrpc_empty; } + /*******/ /* Vector type accessor */ @@ -1919,20 +2161,69 @@ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT - * vector type of value as enumerated by XMLRPC_VECTOR_TYPE + * vector type of value as enumerated by XMLRPC_VECTOR_TYPE. + * xmlrpc_none if not a value. * NOTES * xmlrpc_none is returned if value is not a vector + * Deprecated for public use. See XMLRPC_GetValueTypeEasy * SEE ALSO * XMLRPC_SetIsVector () * XMLRPC_GetValueType () + * XMLRPC_GetValueTypeEasy () * SOURCE */ XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) { return(value && value->v) ? value->v->type : xmlrpc_none; } + +/*******/ + +/****f* VALUE/XMLRPC_GetValueTypeEasy + * NAME + * XMLRPC_GetValueTypeEasy + * SYNOPSIS + * XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE value) + * FUNCTION + * determine data type of the XMLRPC_VALUE. includes vector types. + * INPUTS + * XMLRPC_VALUE target of query + * RESULT + * data type of value as enumerated by XMLRPC_VALUE_TYPE_EASY + * xmlrpc_type_none if not a value. + * NOTES + * all values are of type xmlrpc_type_empty until set. + * SEE ALSO + * XMLRPC_SetValue* + * XMLRPC_CreateValue* + * XMLRPC_Append* + * SOURCE + */ +XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy (XMLRPC_VALUE value) { + if (value) { + switch (value->type) { + case xmlrpc_vector: + switch (value->v->type) { + case xmlrpc_vector_none: + return xmlrpc_type_none; + case xmlrpc_vector_struct: + return xmlrpc_type_struct; + case xmlrpc_vector_mixed: + return xmlrpc_type_mixed; + case xmlrpc_vector_array: + return xmlrpc_type_array; + } + default: + /* evil cast, but we know they are the same */ + return(XMLRPC_VALUE_TYPE_EASY) value->type; + } + } + return xmlrpc_none; +} + /*******/ + /*-******************* * Begin Server Funcs * *********************/ @@ -1966,6 +2257,7 @@ XMLRPC_SERVER XMLRPC_ServerCreate() { } return server; } + /*******/ /* Return global server. Not locking! Not Thread Safe! */ @@ -1996,6 +2288,7 @@ XMLRPC_SERVER XMLRPC_GetGlobalServer() { } return xsServer; } + /*******/ /****f* VALUE/XMLRPC_ServerDestroy @@ -2042,6 +2335,7 @@ void XMLRPC_ServerDestroy(XMLRPC_SERVER server) { my_free(server); } } + /*******/ @@ -2082,6 +2376,7 @@ int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_C } return 0; } + /*******/ inline server_method* find_method(XMLRPC_SERVER server, const char* name) { @@ -2164,6 +2459,7 @@ XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callNa } return NULL; } + /*******/ @@ -2199,17 +2495,21 @@ XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST reques 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); + else if (server && request) { + XMLRPC_Callback cb = + XMLRPC_ServerFindMethod (server, request->methodName.str); if(cb) { xReturn = cb(server, request, userData); } else { - xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str); + xReturn = + XMLRPC_UtilityCreateFault (xmlrpc_error_unknown_method, + request->methodName.str); } } return xReturn; } + /*******/ /*-***************** @@ -2227,7 +2527,8 @@ XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST reques typedef struct _xmlrpc_options { XMLRPC_CASE id_case; XMLRPC_CASE_COMPARISON id_case_compare; -} STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; +} +STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() { static STRUCT_XMLRPC_OPTIONS options = { @@ -2259,6 +2560,7 @@ XMLRPC_CASE XMLRPC_GetDefaultIdCase() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case; } + /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCase @@ -2284,6 +2586,7 @@ XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) { options->id_case = id_case; return options->id_case; } + /*******/ /****f* VALUE/XMLRPC_GetDefaultIdCaseComparison @@ -2308,6 +2611,7 @@ XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case_compare; } + /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCaseComparison @@ -2333,6 +2637,7 @@ XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON options->id_case_compare = id_case_compare; return options->id_case_compare; } + /*******/ /*-********************************* @@ -2385,16 +2690,36 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) 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; + 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); @@ -2408,7 +2733,8 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) if(description.len) { xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); - XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len); + XMLRPC_VectorAppendString (xOutput, "faultString", description.str, + description.len); XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code); } @@ -2416,6 +2742,7 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) return xOutput; } + /*******/ @@ -2438,6 +2765,7 @@ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) void XMLRPC_Free(void* mem) { my_free(mem); } + /*******/ @@ -2458,14 +2786,10 @@ void XMLRPC_Free(void* mem) { const char* XMLRPC_GetVersionString() { return XMLRPC_VERSION_STR; } + /*******/ /*-********************** * End Utility API funcs * ************************/ - - - - - diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.h b/ext/xmlrpc/libxmlrpc/xmlrpc.h index c7223adf2e..bcfa46fadc 100644 --- a/ext/xmlrpc/libxmlrpc/xmlrpc.h +++ b/ext/xmlrpc/libxmlrpc/xmlrpc.h @@ -43,7 +43,7 @@ extern "C" { /* allow version to be specified via compile line define */ #ifndef XMLRPC_LIB_VERSION - #define XMLRPC_LIB_VERSION "0.41" + #define XMLRPC_LIB_VERSION "0.50" #endif /* this number, representing the date, must be increased each time the API changes */ @@ -61,6 +61,7 @@ extern "C" { * XMLRPC_VALUE_TYPE * NOTES * Defines data types for XMLRPC_VALUE + * Deprecated for public use. See XMLRPC_VALUE_TYPE_EASY * SEE ALSO * XMLRPC_VECTOR_TYPE * XMLRPC_REQUEST_TYPE @@ -83,7 +84,8 @@ typedef enum _XMLRPC_VALUE_TYPE { * NAME * XMLRPC_VECTOR_TYPE * NOTES - * Defines data types for XMLRPC_VECTOR + * Defines data types for XMLRPC_VECTOR. + * Deprecated for public use. See XMLRPC_VALUE_TYPE_EASY * SEE ALSO * XMLRPC_VALUE_TYPE * XMLRPC_REQUEST_TYPE @@ -97,6 +99,33 @@ typedef enum _XMLRPC_VECTOR_TYPE { } XMLRPC_VECTOR_TYPE; /*******/ +/****d* VALUE/XMLRPC_VALUE_TYPE_EASY + * NAME + * XMLRPC_VALUE_TYPE_EASY + * NOTES + * Defines data types for XMLRPC_VALUE, including vector types. + * SEE ALSO + * XMLRPC_VECTOR_TYPE + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +typedef enum _XMLRPC_VALUE_TYPE_EASY { + xmlrpc_type_none, /* not a value */ + xmlrpc_type_empty, /* empty value, eg NULL */ + xmlrpc_type_base64, /* base64 value, eg binary data */ + xmlrpc_type_boolean, /* boolean [0 | 1] */ + xmlrpc_type_datetime, /* datetime [ISO8601 | time_t] */ + xmlrpc_type_double, /* double / floating point */ + xmlrpc_type_int, /* integer */ + xmlrpc_type_string, /* string */ +/* -- IMPORTANT: identical to XMLRPC_VALUE_TYPE to this point. -- */ + xmlrpc_type_array, /* vector array */ + xmlrpc_type_mixed, /* vector mixed */ + xmlrpc_type_struct /* vector struct */ +} XMLRPC_VALUE_TYPE_EASY; +/*******/ + + /****d* VALUE/XMLRPC_REQUEST_TYPE * NAME * XMLRPC_REQUEST_TYPE @@ -165,7 +194,8 @@ typedef enum _xmlrpc_version { xmlrpc_version_none, /* not a recognized vocabulary */ xmlrpc_version_1_0, /* xmlrpc 1.0 standard vocab */ xmlrpc_version_simple = 2, /* alt more readable vocab */ - xmlrpc_version_danda = 2 /* same as simple. legacy */ + xmlrpc_version_danda = 2, /* same as simple. legacy */ + xmlrpc_version_soap_1_1 = 3 /* SOAP. version 1.1 */ } XMLRPC_VERSION; /******/ @@ -303,7 +333,10 @@ XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type); /* Cleanup values */ void XMLRPC_CleanupValue(XMLRPC_VALUE value); + +/* Copy values */ XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_DupValueNew(XMLRPC_VALUE xSource); /* Set Values */ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time); @@ -329,6 +362,7 @@ const char* XMLRPC_GetValueID(XMLRPC_VALUE value); /* Type introspection */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v); +XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE v); XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v); /* Parsing and Creating XML */ diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_private.h b/ext/xmlrpc/libxmlrpc/xmlrpc_private.h index 0b4a078ee9..65c6b136a6 100644 --- a/ext/xmlrpc/libxmlrpc/xmlrpc_private.h +++ b/ext/xmlrpc/libxmlrpc/xmlrpc_private.h @@ -122,7 +122,6 @@ typedef struct _xmlrpc_request { /* Vector type. Used by XMLRPC_VALUE. Never visible to users of the API. */ typedef struct _xmlrpc_vector { XMLRPC_VECTOR_TYPE type; /* vector type */ - const char* id; /* ??? unused? */ queue *q; /* list of child values */ } STRUCT_XMLRPC_VECTOR; /******/ diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c index 8cd552c1f4..c73fe67413 100644 --- a/ext/xmlrpc/xmlrpc-epi-php.c +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -52,13 +52,22 @@ +----------------------------------------------------------------------+ */ +/********************************************************************** +* BUGS: * +* - when calling a php user function, there appears to be no way to * +* distinguish between a return value of null, and no return value * +* at all. The xml serialization layer(s) will then return a value * +* of null, when the right thing may be no value at all. (SOAP) * +**********************************************************************/ + #include "php.h" #include "ext/standard/info.h" #include "php_ini.h" #include "php_xmlrpc.h" +#include "php_config.h" #include "xmlrpc.h" -#define PHP_EXT_VERSION "0.41" +#define PHP_EXT_VERSION "0.50" /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. @@ -125,6 +134,7 @@ typedef struct _xmlrpc_server_data { // how to format output typedef struct _php_output_options { int b_php_out; + int b_auto_version; STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; } php_output_options; @@ -161,6 +171,8 @@ typedef struct _xmlrpc_callback_data { #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1) #define VERSION_VALUE_SIMPLE "simple" #define VERSION_VALUE_XMLRPC "xmlrpc" +#define VERSION_VALUE_SOAP11 "soap 1.1" +#define VERSION_VALUE_AUTO "auto" #define ENCODING_KEY "encoding" #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1) @@ -327,6 +339,7 @@ static void set_output_options(php_output_options* options, pval* output_opts) { /* defaults */ options->b_php_out = 0; + options->b_auto_version = 1; options->xmlrpc_out.version = xmlrpc_version_1_0; options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT; options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; @@ -335,7 +348,7 @@ static void set_output_options(php_output_options* options, pval* output_opts) { if(output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) { pval** val; - /* verbosity of generated xml */ + /* type of output (xml/php) */ if(zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**)&val) == SUCCESS) { @@ -371,12 +384,19 @@ static void set_output_options(php_output_options* options, pval* output_opts) { VERSION_KEY, VERSION_KEY_LEN + 1, (void**)&val) == SUCCESS) { if(Z_TYPE_PP(val) == IS_STRING) { + options->b_auto_version = 0; if(!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) { options->xmlrpc_out.version = xmlrpc_version_1_0; } else if(!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) { options->xmlrpc_out.version = xmlrpc_version_simple; } + else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) { + options->xmlrpc_out.version = xmlrpc_version_soap_1_1; + } + else { // if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { + options->b_auto_version = 1; + } } } @@ -493,7 +513,8 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int dept switch(type) { case xmlrpc_base64: if(Z_TYPE_P(val) == IS_NULL) { - xReturn = XMLRPC_CreateValueBase64(key, "", 1); + xReturn = XMLRPC_CreateValueEmpty(); + XMLRPC_SetValueID(xReturn, key, 0); } else { xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val)); @@ -645,6 +666,7 @@ PHP_FUNCTION(xmlrpc_encode_request) { set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0); + if(return_value_used) { xRequest = XMLRPC_RequestNew(); @@ -874,6 +896,8 @@ static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRe call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC); pData->php_executed = 1; + + return NULL; } /* called by the C server when it first receives an introspection request. We pass this on to @@ -1052,19 +1076,21 @@ PHP_FUNCTION(xmlrpc_server_call_method) { xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(rawxml), Z_STRLEN_P(rawxml), &input_opts); if(xRequest) { - - /* check if we have a method name -- indicating success and all manner of good things */ - if(XMLRPC_RequestGetMethodName(xRequest)) { - pval** php_function; + const char* methodname = XMLRPC_RequestGetMethodName(xRequest); + pval** php_function; XMLRPC_VALUE xAnswer = NULL; MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */ MAKE_STD_ZVAL(data.return_data); Z_TYPE_P(data.return_data) = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ Z_TYPE_P(data.xmlrpc_method) = IS_NULL; - /* setup some data to pass to the callback function */ - Z_STRVAL_P(data.xmlrpc_method) = estrdup(XMLRPC_RequestGetMethodName(xRequest)); - Z_STRLEN_P(data.xmlrpc_method) = strlen(Z_STRVAL_P(data.xmlrpc_method)); + if (!methodname) { + methodname = ""; + } + + /* setup some data to pass to the callback function */ + Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname); + Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname); Z_TYPE_P(data.xmlrpc_method) = IS_STRING; data.caller_params = caller_params; data.php_executed = 0; @@ -1104,10 +1130,26 @@ PHP_FUNCTION(xmlrpc_server_call_method) { char* outBuf = 0; int buf_len = 0; + /* automagically determine output serialization type from request type */ + if (out.b_auto_version) { + XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest); + if (opts) { + out.xmlrpc_out.version = opts->version; + } + } + + /* automagically determine output serialization type from request type */ + if (out.b_auto_version) { + XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest); + if (opts) { + out.xmlrpc_out.version = opts->version; + } + } /* set some required request hoojum */ XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); XMLRPC_RequestSetData(xResponse, xAnswer); + XMLRPC_RequestSetMethodName(xResponse, methodname); /* generate xml */ outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len); @@ -1134,7 +1176,6 @@ PHP_FUNCTION(xmlrpc_server_call_method) { if(xAnswer) { XMLRPC_CleanupValue(xAnswer); } - } XMLRPC_RequestFree(xRequest, 1); } -- 2.40.0