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