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