xref: /PHP-7.2/ext/xmlrpc/libxmlrpc/xml_to_soap.c (revision 60a69dae)
1 /*
2   This file is part of libXMLRPC - a C library for xml-encoded function calls.
3 
4   Author: Dan Libby (dan@libby.com)
5 */
6 
7 
8 /*-**********************************************************************
9 * TODO:                                                                 *
10 *  - [SOAP-ENC:position] read sparse arrays (and write?)                *
11 *  - [SOAP-ENC:offset] read partially transmitted arrays  (and write?)  *
12 *  - read "flattened" multi-dimensional arrays. (don't bother writing)  *
13 *                                                                       *
14 * BUGS:                                                                 *
15 *  - does not read schema. thus only knows soap pre-defined types.      *
16 *  - references (probably) do not work. untested.                       *
17 *  - does not expose SOAP-ENV:Header to application at all.             *
18 *  - does not use namespaces correctly, thus:                           *
19 *    - namespaces are hard-coded in comparison tokens                   *
20 *    - if a sender uses another namespace identifer, it will break      *
21 ************************************************************************/
22 
23 
24 static const char rcsid[] = "#(@) $Id:";
25 
26 #include <string.h>
27 #include <stdlib.h>
28 #include "xml_to_soap.h"
29 #include "base64.h"
30 
31 /* list of tokens used in vocab */
32 #define TOKEN_ANY				 "xsd:ur-type"
33 #define TOKEN_ARRAY          "SOAP-ENC:Array"
34 #define TOKEN_ARRAY_TYPE     "SOAP-ENC:arrayType"
35 #define TOKEN_BASE64         "SOAP-ENC:base64"
36 #define TOKEN_BOOLEAN        "xsd:boolean"
37 #define TOKEN_DATETIME       "xsd:timeInstant"
38 #define TOKEN_DOUBLE         "xsd:double"
39 #define TOKEN_FLOAT          "xsd:float"
40 #define TOKEN_ID             "id"
41 #define TOKEN_INT            "xsd:int"
42 #define TOKEN_NULL           "xsi:null"
43 #define TOKEN_STRING         "xsd:string"
44 #define TOKEN_STRUCT			 "xsd:struct"
45 #define TOKEN_TYPE           "xsi:type"
46 #define TOKEN_FAULT			 "SOAP-ENV:Fault"
47 #define TOKEN_MUSTUNDERSTAND "SOAP-ENV:mustUnderstand"
48 #define TOKEN_ACTOR			 "SOAP-ENV:actor"
49 #define TOKEN_ACTOR_NEXT		 "http://schemas.xmlsoap.org/soap/actor/next"
50 
51 #define TOKEN_XMLRPC_FAULTCODE   "faultCode"
52 #define TOKEN_XMLRPC_FAULTSTRING "faultString"
53 #define TOKEN_SOAP_FAULTCODE     "faultcode"
54 #define TOKEN_SOAP_FAULTSTRING   "faultstring"
55 #define TOKEN_SOAP_FAULTDETAILS  "details"
56 #define TOKEN_SOAP_FAULTACTOR    "actor"
57 
58 
59 /* determine if a string represents a soap type, as used in element names */
is_soap_type(const char * soap_type)60 static inline int is_soap_type(const char* soap_type) {
61 	return(strstr(soap_type, "SOAP-ENC:") || strstr(soap_type, "xsd:")) ? 1 : 0;
62 }
63 
64 /* utility func to generate a new attribute. possibly should be in xml_element.c?? */
new_attr(const char * key,const char * val)65 static xml_element_attr* new_attr(const char* key, const char* val) {
66 	xml_element_attr* attr = emalloc(sizeof(xml_element_attr));
67 	if (attr) {
68 		attr->key = key ? estrdup(key) : NULL;
69 		attr->val = val ? estrdup(val) : NULL;
70 	}
71 	return attr;
72 }
73 
74 struct array_info {
75 	char          kids_type[128];
76 	unsigned long size;
77 	/* ... ? */
78 };
79 
80 
81 /* parses soap arrayType attribute to generate an array_info structure.
82  * TODO: should deal with sparse, flattened, & multi-dimensional arrays
83  */
parse_array_type_info(const char * array_type)84 static struct array_info* parse_array_type_info(const char* array_type) {
85 	struct array_info* ai = NULL;
86 	if (array_type) {
87 		ai = (struct array_info*)ecalloc(1, sizeof(struct array_info));
88 		if (ai) {
89 			char buf[128], *p;
90 			snprintf(buf, sizeof(buf), "%s", array_type);
91 			p = strchr(buf, '[');
92 			if (p) {
93 				*p = 0;
94 			}
95 			strcpy(ai->kids_type, buf);
96 		}
97 	}
98 	return ai;
99 }
100 
101 /* performs heuristics on an xmlrpc_vector_array to determine
102  * appropriate soap arrayType string.
103  */
get_array_soap_type(XMLRPC_VALUE node)104 static const char* get_array_soap_type(XMLRPC_VALUE node) {
105 	XMLRPC_VALUE_TYPE_EASY type = xmlrpc_type_none;
106 
107 	XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
108 	int loopCount = 0;
109 	const char* soapType = TOKEN_ANY;
110 
111 	type = XMLRPC_GetValueTypeEasy(xIter);
112 	xIter = XMLRPC_VectorNext(node);
113 
114 	while (xIter) {
115 		/* 50 seems like a decent # of loops.  That will likely
116 		 * cover most cases.  Any more and we start to sacrifice
117 		 * performance.
118 		 */
119 		if ( (XMLRPC_GetValueTypeEasy(xIter) != type) || loopCount >= 50) {
120 			type = xmlrpc_type_none;
121 			break;
122 		}
123 		loopCount ++;
124 
125 		xIter = XMLRPC_VectorNext(node);
126 	}
127 	switch (type) {
128 	case xmlrpc_type_none:
129 		soapType = TOKEN_ANY;
130 		break;
131 	case xmlrpc_type_empty:
132 		soapType = TOKEN_NULL;
133 		break;
134 	case xmlrpc_type_int:
135 		soapType = TOKEN_INT;
136 		break;
137 	case xmlrpc_type_double:
138 		soapType = TOKEN_DOUBLE;
139 		break;
140 	case xmlrpc_type_boolean:
141 		soapType = TOKEN_BOOLEAN;
142 		break;
143 	case xmlrpc_type_string:
144 		soapType = TOKEN_STRING;
145 		break;
146 	case xmlrpc_type_base64:
147 		soapType = TOKEN_BASE64;
148 		break;
149 	case xmlrpc_type_datetime:
150 		soapType = TOKEN_DATETIME;
151 		break;
152 	case xmlrpc_type_struct:
153 		soapType = TOKEN_STRUCT;
154 		break;
155 	case xmlrpc_type_array:
156 		soapType = TOKEN_ARRAY;
157 		break;
158 	case xmlrpc_type_mixed:
159 		soapType = TOKEN_STRUCT;
160 		break;
161 	}
162 	return soapType;
163 }
164 
165 /* determines whether a node is a fault or not, and of which type:
166  * 0 = not a fault,
167  * 1 = xmlrpc style fault
168  * 2 = soap style fault.
169  */
get_fault_type(XMLRPC_VALUE node)170 static inline int get_fault_type(XMLRPC_VALUE node) {
171 	if (XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTCODE) &&
172 		 XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTSTRING)) {
173 		return 1;
174 	}
175 	else if (XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTCODE) &&
176 				XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTSTRING)) {
177 		return 2;
178 	}
179 	return 0;
180 }
181 
182 /* input: an XMLRPC_VALUE representing a fault struct in xml-rpc style.
183  * output: an XMLRPC_VALUE representing a fault struct in soap style,
184  *  with xmlrpc codes mapped to soap codes, and all other values preserved.
185  *  note that the returned value is a completely new value, and must be freed.
186  *  the input value is untouched.
187  */
gen_fault_xmlrpc(XMLRPC_VALUE node,xml_element * el_target)188 static XMLRPC_VALUE gen_fault_xmlrpc(XMLRPC_VALUE node, xml_element* el_target) {
189 	XMLRPC_VALUE xDup = XMLRPC_DupValueNew(node);
190 	XMLRPC_VALUE xCode = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTCODE);
191 	XMLRPC_VALUE xStr = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTSTRING);
192 
193 	XMLRPC_SetValueID(xCode, TOKEN_SOAP_FAULTCODE, 0);
194 	XMLRPC_SetValueID(xStr, TOKEN_SOAP_FAULTSTRING, 0);
195 
196 	/* rough mapping of xmlrpc fault codes to soap codes */
197 	switch (XMLRPC_GetValueInt(xCode)) {
198 	case -32700:		  /* "parse error. not well formed", */
199 	case -32701:		  /* "parse error. unsupported encoding" */
200 	case -32702:		  /* "parse error. invalid character for encoding" */
201 	case -32600:		  /* "server error. invalid xml-rpc.  not conforming to spec." */
202 	case -32601:		  /* "server error. requested method not found" */
203 	case -32602:		  /* "server error. invalid method parameters" */
204 		XMLRPC_SetValueString(xCode, "SOAP-ENV:Client", 0);
205 		break;
206 	case -32603:		  /* "server error. internal xml-rpc error" */
207 	case -32500:		  /* "application error" */
208 	case -32400:		  /* "system error" */
209 	case -32300:		  /* "transport error */
210 		XMLRPC_SetValueString(xCode, "SOAP-ENV:Server", 0);
211 		break;
212 	}
213 	return xDup;
214 }
215 
216 /* returns a new XMLRPC_VALUE representing a soap fault, comprised of a struct with four keys. */
gen_soap_fault(const char * fault_code,const char * fault_string,const char * actor,const char * details)217 static XMLRPC_VALUE gen_soap_fault(const char* fault_code, const char* fault_string,
218 											  const char* actor, const char* details) {
219 	XMLRPC_VALUE xReturn = XMLRPC_CreateVector(TOKEN_FAULT, xmlrpc_vector_struct);
220 	XMLRPC_AddValuesToVector(xReturn,
221 									 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTCODE, fault_code, 0),
222 									 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTSTRING, fault_string, 0),
223 									 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTACTOR, actor, 0),
224 									 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTDETAILS, details, 0),
225 									 NULL);
226 	return xReturn;
227 }
228 
229 /* translates xml soap dom to native data structures. recursive. */
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)230 XMLRPC_VALUE xml_element_to_SOAP_REQUEST_worker(XMLRPC_REQUEST request,
231 																XMLRPC_VALUE xParent,
232 																struct array_info* parent_array,
233 																XMLRPC_VALUE xCurrent,
234 																xml_element* el,
235 																int depth) {
236 	XMLRPC_REQUEST_TYPE rtype = xmlrpc_request_none;
237 
238 	/* no current element on first call */
239 	if (!xCurrent) {
240 		xCurrent = XMLRPC_CreateValueEmpty();
241 	}
242 
243 	/* increment recursion depth gauge */
244 	depth ++;
245 
246 	/* safety first. must have a valid element */
247 	if (el && el->name) {
248 		const char* id = NULL;
249 		const char* type = NULL, *arrayType=NULL, *actor = NULL;
250 		xml_element_attr* attr_iter = Q_Head(&el->attrs);
251 		int b_must_understand = 0;
252 
253 		/* in soap, types may be specified in either element name -or- with xsi:type attribute. */
254 		if (is_soap_type(el->name)) {
255 			type = el->name;
256 		}
257 		/* if our parent node, by definition a vector, is not an array, then
258 		   our element name must be our key identifier. */
259 		else if (XMLRPC_GetVectorType(xParent) != xmlrpc_vector_array) {
260 			id = el->name;
261 			if(!strcmp(id, "item")) {
262 			}
263 		}
264 
265 		/* iterate through element attributes, pick out useful stuff. */
266 		while (attr_iter) {
267 			/* element's type */
268 			if (!strcmp(attr_iter->key, TOKEN_TYPE)) {
269 				type = attr_iter->val;
270 			}
271 			/* array type */
272 			else if (!strcmp(attr_iter->key, TOKEN_ARRAY_TYPE)) {
273 				arrayType = attr_iter->val;
274 			}
275 			/* must understand, sometimes present in headers. */
276 			else if (!strcmp(attr_iter->key, TOKEN_MUSTUNDERSTAND)) {
277 				b_must_understand = strchr(attr_iter->val, '1') ? 1 : 0;
278 			}
279 			/* actor, used in conjunction with must understand. */
280 			else if (!strcmp(attr_iter->key, TOKEN_ACTOR)) {
281 				actor = attr_iter->val;
282 			}
283 			attr_iter = Q_Next(&el->attrs);
284 		}
285 
286 		/* check if caller says we must understand something in a header. */
287 		if (b_must_understand) {
288 			/* is must understand actually indended for us?
289 			   BUG: spec says we should also determine if actor is our URL, but
290 			        we do not have that information. */
291 			if (!actor || !strcmp(actor, TOKEN_ACTOR_NEXT)) {
292 				/* TODO: implement callbacks or other mechanism for applications
293 				   to "understand" these headers. For now, we just bail if we
294 				   get a mustUnderstand header intended for us. */
295 				XMLRPC_RequestSetError(request,
296 											  gen_soap_fault("SOAP-ENV:MustUnderstand",
297 																  "SOAP Must Understand Error",
298 																  "", ""));
299 				return xCurrent;
300 			}
301 		}
302 
303 		/* set id (key) if one was found. */
304 		if (id) {
305 			XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact);
306 		}
307 
308 		/* according to soap spec,
309 		   depth 1 = Envelope, 2 = Header, Body & Fault, 3 = methodcall or response. */
310 		if (depth == 3) {
311 			const char* methodname = el->name;
312 			char* p = NULL;
313 
314 			/* BUG: we determine request or response type using presence of "Response" in element name.
315 			   According to spec, this is only recommended, not required. Apparently, implementations
316 			   are supposed to know the type of action based on state, which strikes me as a bit lame.
317 			   Anyway, we don't have that state info, thus we use Response as a heuristic. */
318 			rtype =
319 #ifdef strcasestr
320 			strcasestr(el->name, "response") ? xmlrpc_request_response : xmlrpc_request_call;
321 #else
322 			strstr(el->name, "esponse") ? xmlrpc_request_response : xmlrpc_request_call;
323 #endif
324 			XMLRPC_RequestSetRequestType(request, rtype);
325 
326 			/* Get methodname.  strip xml namespace crap. */
327 			p = strchr(el->name, ':');
328 			if (p) {
329 				methodname = p + 1;
330 			}
331 			if (rtype == xmlrpc_request_call) {
332 				XMLRPC_RequestSetMethodName(request, methodname);
333 			}
334 		}
335 
336 
337 		/* Next, we begin to convert actual values. if no children, then must be a scalar value. */
338 		if (!Q_Size(&el->children)) {
339 			if (!type && parent_array && parent_array->kids_type[0]) {
340 				type = parent_array->kids_type;
341 			}
342 			if (!type || !strcmp(type, TOKEN_STRING)) {
343 				XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len);
344 			}
345 			else if (!strcmp(type, TOKEN_INT)) {
346 				XMLRPC_SetValueInt(xCurrent, atoi(el->text.str));
347 			}
348 			else if (!strcmp(type, TOKEN_BOOLEAN)) {
349 				XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str));
350 			}
351 			else if (!strcmp(type, TOKEN_DOUBLE) ||
352 						!strcmp(type, TOKEN_FLOAT)) {
353 				XMLRPC_SetValueDouble(xCurrent, atof(el->text.str));
354 			}
355 			else if (!strcmp(type, TOKEN_NULL)) {
356 				/* already an empty val. do nothing. */
357 			}
358 			else if (!strcmp(type, TOKEN_DATETIME)) {
359 				XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str);
360 			}
361 			else if (!strcmp(type, TOKEN_BASE64)) {
362 				struct buffer_st buf;
363 				base64_decode_xmlrpc(&buf, el->text.str, el->text.len);
364 				XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset);
365 				buffer_delete(&buf);
366 			}
367 		}
368 		/* Element has children, thus a vector, or "compound type" in soap-speak. */
369 		else {
370 			struct array_info* ai = NULL;
371 			xml_element* iter = (xml_element*)Q_Head(&el->children);
372 
373 			if (!type || !strcmp(type, TOKEN_STRUCT)) {
374 				XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct);
375 			}
376 			else if (!strcmp(type, TOKEN_ARRAY) || arrayType != NULL) {
377 				/* determine magic associated with soap array type.
378 				   this is passed down as we recurse, so our children have access to the info. */
379 				ai = parse_array_type_info(arrayType);	/* alloc'ed ai free'd below. */
380 				XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array);
381 			}
382 			else {
383 				/* mixed is probably closest thing we have to compound type. */
384 				XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
385 			}
386 			/* Recurse, adding values as we go.  Check for error during recursion
387 			   and if found, bail.  this short-circuits us out of the recursion. */
388 			while ( iter && !XMLRPC_RequestGetError(request) ) {
389 				XMLRPC_VALUE xNext = NULL;
390 				/* top level elements don't actually represent values, so we just pass the
391 				   current value along until we are deep enough. */
392 				if ( depth <= 2 ||
393 					  (rtype == xmlrpc_request_response && depth <= 3) ) {
394 					xml_element_to_SOAP_REQUEST_worker(request, NULL, ai, xCurrent, iter, depth);
395 				}
396 				/* ready to do some actual de-serialization. create a new empty value and
397 				   pass that along to be init'd, then add it to our current vector. */
398 				else {
399 					xNext = XMLRPC_CreateValueEmpty();
400 					xml_element_to_SOAP_REQUEST_worker(request, xCurrent, ai, xNext, iter, depth);
401 					XMLRPC_AddValueToVector(xCurrent, xNext);
402 				}
403 				iter = (xml_element*)Q_Next(&el->children);
404 			}
405 			/* cleanup */
406 			if (ai) {
407 				efree(ai);
408 			}
409 		}
410 	}
411 	return xCurrent;
412 }
413 
414 /* Convert soap xml dom to XMLRPC_VALUE, sans request info.  untested. */
xml_element_to_SOAP_VALUE(xml_element * el)415 XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el)
416 {
417 	return xml_element_to_SOAP_REQUEST_worker(NULL, NULL, NULL, NULL, el, 0);
418 }
419 
420 /* Convert soap xml dom to XMLRPC_REQUEST */
xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request,xml_element * el)421 XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el)
422 {
423 	if (request) {
424 		return XMLRPC_RequestSetData(request, xml_element_to_SOAP_REQUEST_worker(request, NULL, NULL, NULL, el, 0));
425 	}
426 	return NULL;
427 }
428 
429 
430 /* translates data structures to soap/xml. recursive */
SOAP_to_xml_element_worker(XMLRPC_REQUEST request,XMLRPC_VALUE node)431 xml_element* SOAP_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) {
432 #define BUF_SIZE 128
433 	xml_element* elem_val = NULL;
434 	if (node) {
435 		int bFreeNode = 0;  /* sometimes we may need to free 'node' variable */
436 		char buf[BUF_SIZE];
437 		XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(node);
438 		char* pName = NULL, *pAttrType = NULL;
439 
440 		/* create our return value element */
441 		elem_val = xml_elem_new();
442 
443 		switch (type) {
444 		case xmlrpc_type_struct:
445 		case xmlrpc_type_mixed:
446 		case xmlrpc_type_array:
447 			if (type == xmlrpc_type_array) {
448 				/* array's are _very_ special in soap.
449 				   TODO: Should handle sparse/partial arrays here. */
450 
451 				/* determine soap array type. */
452 				const char* type = get_array_soap_type(node);
453 				xml_element_attr* attr_array_type = NULL;
454 
455 				/* specify array kids type and array size. */
456 				snprintf(buf, sizeof(buf), "%s[%i]", type, XMLRPC_VectorSize(node));
457 				attr_array_type = new_attr(TOKEN_ARRAY_TYPE, buf);
458 
459 				Q_PushTail(&elem_val->attrs, attr_array_type);
460 
461 				pAttrType = TOKEN_ARRAY;
462 			}
463 			/* check for fault, which is a rather special case.
464 			   (can't these people design anything consistent/simple/elegant?) */
465 			else if (type == xmlrpc_type_struct) {
466 				int fault_type = get_fault_type(node);
467 				if (fault_type) {
468 					if (fault_type == 1) {
469 						/* gen fault from xmlrpc style fault codes
470 						    notice that we get a new node, which must be freed herein. */
471 						node = gen_fault_xmlrpc(node, elem_val);
472 						bFreeNode = 1;
473 					}
474 					pName = TOKEN_FAULT;
475 				}
476 			}
477 
478 			{
479 				/* recurse through sub-elements */
480 				XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
481 				while ( xIter ) {
482 					xml_element* next_el = SOAP_to_xml_element_worker(request, xIter);
483 					if (next_el) {
484 						Q_PushTail(&elem_val->children, next_el);
485 					}
486 					xIter = XMLRPC_VectorNext(node);
487 				}
488 			}
489 
490 			break;
491 
492 			/* handle scalar types */
493 		case xmlrpc_type_empty:
494 			pAttrType = TOKEN_NULL;
495 			break;
496 		case xmlrpc_type_string:
497 			pAttrType = TOKEN_STRING;
498 			simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
499 			break;
500 		case xmlrpc_type_int:
501 			pAttrType = TOKEN_INT;
502 			snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
503 			simplestring_add(&elem_val->text, buf);
504 			break;
505 		case xmlrpc_type_boolean:
506 			pAttrType = TOKEN_BOOLEAN;
507 			snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
508 			simplestring_add(&elem_val->text, buf);
509 			break;
510 		case xmlrpc_type_double:
511 			pAttrType = TOKEN_DOUBLE;
512 			snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
513 			simplestring_add(&elem_val->text, buf);
514 			break;
515 		case xmlrpc_type_datetime:
516 			{
517 				time_t tt = XMLRPC_GetValueDateTime(node);
518 				struct tm *tm = localtime (&tt);
519 				pAttrType = TOKEN_DATETIME;
520 				if(strftime (buf, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", tm)) {
521 					simplestring_add(&elem_val->text, buf);
522 				}
523 			}
524 			break;
525 		case xmlrpc_type_base64:
526 			{
527 				struct buffer_st buf;
528 				pAttrType = TOKEN_BASE64;
529 				base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
530 				simplestring_addn(&elem_val->text, buf.data, buf.offset );
531 				buffer_delete(&buf);
532 			}
533 			break;
534 			break;
535 		default:
536 			break;
537 		}
538 
539 		/* determining element's name is a bit tricky, due to soap semantics. */
540 		if (!pName) {
541 			/* if the value's type is known... */
542 			if (pAttrType) {
543 				/* see if it has an id (key). If so, use that as name, and type as an attribute. */
544 				pName = (char*)XMLRPC_GetValueID(node);
545 				if (pName) {
546 					Q_PushTail(&elem_val->attrs, new_attr(TOKEN_TYPE, pAttrType));
547 				}
548 
549 				/* otherwise, use the type as the name. */
550 				else {
551 					pName = pAttrType;
552 				}
553 			}
554 			/* if the value's type is not known... (a rare case?) */
555 			else {
556 				/* see if it has an id (key). otherwise, default to generic "item" */
557 				pName = (char*)XMLRPC_GetValueID(node);
558 				if (!pName) {
559 					pName = "item";
560 				}
561 			}
562 		}
563 		elem_val->name = estrdup(pName);
564 
565 		/* cleanup */
566 		if (bFreeNode) {
567 			XMLRPC_CleanupValue(node);
568 		}
569 	}
570 	return elem_val;
571 }
572 
573 /* convert XMLRPC_VALUE to soap xml dom.  untested. */
SOAP_VALUE_to_xml_element(XMLRPC_VALUE node)574 xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node) {
575 	return SOAP_to_xml_element_worker(NULL, node);
576 }
577 
578 /* convert XMLRPC_REQUEST to soap xml dom. */
SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request)579 xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
580 	xml_element* root = xml_elem_new();
581 
582 	/* safety first. */
583 	if (root) {
584 		xml_element* body = xml_elem_new();
585 		root->name = estrdup("SOAP-ENV:Envelope");
586 
587 		/* silly namespace stuff */
588 		Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"));
589 		Q_PushTail(&root->attrs, new_attr("xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance"));
590 		Q_PushTail(&root->attrs, new_attr("xmlns:xsd", "http://www.w3.org/1999/XMLSchema"));
591 		Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"));
592 		Q_PushTail(&root->attrs, new_attr("xmlns:si", "http://soapinterop.org/xsd"));
593 		Q_PushTail(&root->attrs, new_attr("xmlns:ns6", "http://testuri.org"));
594 		Q_PushTail(&root->attrs, new_attr("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/"));
595 
596 		/* Q_PushHead(&root->attrs, new_attr("xmlns:ks", "http://kitchen.sink.org/soap/everything/under/sun"));
597 		       JUST KIDDING!! :-)  ---->                ------------------------------------------------- */
598 
599 		if (body) {
600 			/* go ahead and serialize first... */
601 			xml_element* el_serialized =
602 			SOAP_to_xml_element_worker(request,
603 												XMLRPC_RequestGetData(request));
604 
605 			/* check for fault, in which case, there is no intermediate element */
606 			if (el_serialized && !strcmp(el_serialized->name, TOKEN_FAULT)) {
607 				Q_PushTail(&body->children, el_serialized);
608 			}
609 			/* usual case: not a fault. Add Response element in between. */
610 			else {
611 				xml_element* rpc = xml_elem_new();
612 
613 				if (rpc) {
614 					const char* methodname = XMLRPC_RequestGetMethodName(request);
615 					XMLRPC_REQUEST_TYPE rtype = XMLRPC_RequestGetRequestType(request);
616 
617 					/* if we are making a request, we want to use the methodname as is. */
618 					if (rtype == xmlrpc_request_call) {
619 						if (methodname) {
620 							rpc->name = estrdup(methodname);
621 						}
622 					}
623 					/* if it's a response, we append "Response". Also, given xmlrpc-epi
624 					   API/architecture, it's likely that we don't have a methodname for
625 					   the response, so we have to check that. */
626 					else {
627 						char buf[128];
628 						snprintf(buf, sizeof(buf), "%s%s",
629 									methodname ? methodname : "",
630 									"Response");
631 
632 						rpc->name = estrdup(buf);
633 					}
634 
635 					/* add serialized data to method call/response.
636 					   add method call/response to body element */
637 					if (rpc->name) {
638 						if(el_serialized) {
639 							if(Q_Size(&el_serialized->children) && rtype == xmlrpc_request_call) {
640 								xml_element* iter = (xml_element*)Q_Head(&el_serialized->children);
641 								while(iter) {
642 									Q_PushTail(&rpc->children, iter);
643 									iter = (xml_element*)Q_Next(&el_serialized->children);
644 								}
645 								xml_elem_free_non_recurse(el_serialized);
646 							}
647 							else {
648 								Q_PushTail(&rpc->children, el_serialized);
649 							}
650 						}
651 
652 						Q_PushTail(&body->children, rpc);
653 					}
654 					else {
655 						/* no method name?!
656 						   TODO: fault here...? */
657 					}
658 				}
659 			}
660 			body->name = estrdup("SOAP-ENV:Body");
661 			Q_PushTail(&root->children, body);
662 		}
663 	}
664 
665 	return root;
666 }
667