]> granicus.if.org Git - php/commitdiff
patches to sync with sourceforge project, version 0.50. primary change: adds support...
authorDan Libby <danda@php.net>
Tue, 30 Oct 2001 02:33:13 +0000 (02:33 +0000)
committerDan Libby <danda@php.net>
Tue, 30 Oct 2001 02:33:13 +0000 (02:33 +0000)
20 files changed:
ext/rpc/xmlrpc/libxmlrpc/Makefile.in
ext/rpc/xmlrpc/libxmlrpc/queue.c
ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c
ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.c [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_to_soap.h [new file with mode: 0644]
ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c
ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c
ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h
ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h
ext/rpc/xmlrpc/xmlrpc-epi-php.c
ext/xmlrpc/libxmlrpc/Makefile.in
ext/xmlrpc/libxmlrpc/queue.c
ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c
ext/xmlrpc/libxmlrpc/xml_to_soap.c [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_to_soap.h [new file with mode: 0644]
ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c
ext/xmlrpc/libxmlrpc/xmlrpc.c
ext/xmlrpc/libxmlrpc/xmlrpc.h
ext/xmlrpc/libxmlrpc/xmlrpc_private.h
ext/xmlrpc/xmlrpc-epi-php.c

index 44257c4882b31cebbc40c87fefc0e02d4e468f24..5c9d4f4aa9105a9617a30a3dfc132edbe16b6a3e 100644 (file)
@@ -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
index 25d62b96a70f5ddc509269bd632410d5eae81969..7ae4c460787806676fe1a50d99615cb811bcb41d 100644 (file)
@@ -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;
index 0d827a5fb0492f39e2d959ebf4cf26362b65ecf3..773eca5ecc0093b1f828ecb706b5e343ead53565 100644 (file)
@@ -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 (file)
index 0000000..6aae027
--- /dev/null
@@ -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 <string.h>
+#include <stdlib.h>
+#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 (file)
index 0000000..9ae9308
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+#ifndef XML_TO_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 */
index 1cd3d9ac2bd1ef8a5ed3685d5d9c8d6b39b96ff4..02f3d430cd43f849a6edfda9f087f11ae0c70287 100644 (file)
@@ -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);
index f95ac949b388001ec01c5ebccaa4765f5b567eeb..596ca622d3522cc93a0a6553032a6378b55fe4ce 100644 (file)
@@ -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 *
 ************************/
-
-
-
-
-
index c7223adf2e2e3af82c4c74c98f639b39cf7abdb2..bcfa46fadc9d73a879527d0b87c693756a29abe4 100644 (file)
@@ -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 */
index 0b4a078ee90770222e41d41ddd61091fee90d605..65c6b136a69480359e3c07c6f7b8fb0acd21038e 100644 (file)
@@ -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;
 /******/
index 8cd552c1f45a8e4df4c712bdcb0236964b0c88bc..c73fe67413086486a738e514977b1b6d5fddd741 100644 (file)
    +----------------------------------------------------------------------+
  */
 
+/**********************************************************************
+* 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);
       }
index 44257c4882b31cebbc40c87fefc0e02d4e468f24..5c9d4f4aa9105a9617a30a3dfc132edbe16b6a3e 100644 (file)
@@ -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
index 25d62b96a70f5ddc509269bd632410d5eae81969..7ae4c460787806676fe1a50d99615cb811bcb41d 100644 (file)
@@ -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;
index 0d827a5fb0492f39e2d959ebf4cf26362b65ecf3..773eca5ecc0093b1f828ecb706b5e343ead53565 100644 (file)
@@ -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 (file)
index 0000000..6aae027
--- /dev/null
@@ -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 <string.h>
+#include <stdlib.h>
+#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 (file)
index 0000000..9ae9308
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+  This file is part of libXMLRPC - a C library for xml-encoded function calls.
+
+  Author: Dan Libby (dan@libby.com)
+  Epinions.com may be contacted at feedback@epinions-inc.com
+*/
+
+/*  
+  Copyright 2000 Epinions, Inc. 
+
+  Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
+  of charge, to (a) use, copy, distribute, modify, perform and display this 
+  software and associated documentation files (the "Software"), and (b) 
+  permit others to whom the Software is furnished to do so as well.  
+
+  1) The above copyright notice and this permission notice shall be included 
+  without modification in all copies or substantial portions of the 
+  Software.  
+
+  2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
+  ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
+  IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+  PURPOSE OR NONINFRINGEMENT.  
+
+  3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
+  SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
+  OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
+  NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
+  DAMAGES.    
+
+*/
+
+
+#ifndef XML_TO_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 */
index 1cd3d9ac2bd1ef8a5ed3685d5d9c8d6b39b96ff4..02f3d430cd43f849a6edfda9f087f11ae0c70287 100644 (file)
@@ -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);
index f95ac949b388001ec01c5ebccaa4765f5b567eeb..596ca622d3522cc93a0a6553032a6378b55fe4ce 100644 (file)
@@ -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 *
 ************************/
-
-
-
-
-
index c7223adf2e2e3af82c4c74c98f639b39cf7abdb2..bcfa46fadc9d73a879527d0b87c693756a29abe4 100644 (file)
@@ -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 */
index 0b4a078ee90770222e41d41ddd61091fee90d605..65c6b136a69480359e3c07c6f7b8fb0acd21038e 100644 (file)
@@ -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;
 /******/
index 8cd552c1f45a8e4df4c712bdcb0236964b0c88bc..c73fe67413086486a738e514977b1b6d5fddd741 100644 (file)
    +----------------------------------------------------------------------+
  */
 
+/**********************************************************************
+* 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);
       }