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