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