/*
  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;
}



syntax highlighted by Code2HTML, v. 0.9.1