Setting tag plcapi-5.3-11
[plcapi.git] / php / xmlrpc / libxmlrpc / xml_to_soap.c
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 */
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?? */
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[30];
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  */
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  */
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 wether 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  */
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  */
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. */
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. */
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 conjuction 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(&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. */
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 */
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 */
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(&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. */
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. */
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