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