This commit was generated by cvs2svn to compensate for changes in r467,
[plcapi.git] / php / xmlrpc / libxmlrpc / xml_to_xmlrpc.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   Epinions.com may be contacted at feedback@epinions-inc.com
6 */
7
8 /*  
9   Copyright 2000 Epinions, Inc. 
10
11   Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
12   of charge, to (a) use, copy, distribute, modify, perform and display this 
13   software and associated documentation files (the "Software"), and (b) 
14   permit others to whom the Software is furnished to do so as well.  
15
16   1) The above copyright notice and this permission notice shall be included 
17   without modification in all copies or substantial portions of the 
18   Software.  
19
20   2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
21   ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
22   IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
23   PURPOSE OR NONINFRINGEMENT.  
24
25   3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
26   SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
27   OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
28   NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
29   DAMAGES.    
30
31 */
32
33
34 static const char rcsid[] = "#(@) $Id: xml_to_xmlrpc.c,v 1.5 2004/04/27 17:33:59 iliaa Exp $";
35
36 #ifdef _WIN32
37 #include "xmlrpc_win32.h"
38 #endif
39 #include <string.h>
40 #include <stdlib.h>
41 #include "xml_to_xmlrpc.h"
42 #include "base64.h"
43
44 /* list of tokens used in vocab */
45 #define ELEM_ARRAY          "array"
46 #define ELEM_BASE64         "base64"
47 #define ELEM_BOOLEAN        "boolean"
48 #define ELEM_DATA           "data"
49 #define ELEM_DATETIME       "dateTime.iso8601"
50 #define ELEM_DOUBLE         "double"
51 #define ELEM_FAULT          "fault"
52 #define ELEM_FAULTCODE      "faultCode"
53 #define ELEM_FAULTSTRING    "faultString"
54 #define ELEM_I4             "i4"
55 #define ELEM_INT            "int"
56 #define ELEM_MEMBER         "member"
57 #define ELEM_METHODCALL     "methodCall"
58 #define ELEM_METHODNAME     "methodName"
59 #define ELEM_METHODRESPONSE "methodResponse"
60 #define ELEM_NAME           "name"
61 #define ELEM_PARAM          "param"
62 #define ELEM_PARAMS         "params"
63 #define ELEM_STRING         "string"
64 #define ELEM_STRUCT         "struct"
65 #define ELEM_VALUE          "value"
66
67
68 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
69    if (!current_val) {
70       /* This should only be the case for the first element */
71       current_val = XMLRPC_CreateValueEmpty();
72    }
73
74         if (el->name) {
75
76       /* first, deal with the crazy/stupid fault format */
77       if (!strcmp(el->name, ELEM_FAULT)) {
78                         xml_element* fault_value = (xml_element*)Q_Head(&el->children);
79                         XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
80
81          if(fault_value) {
82             xml_element* fault_struct = (xml_element*)Q_Head(&fault_value->children);
83             if(fault_struct) {
84                xml_element* iter = (xml_element*)Q_Head(&fault_struct->children);
85
86                while (iter) {
87                   XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
88                   xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
89                   XMLRPC_AddValueToVector(current_val, xNextVal);
90                   iter = (xml_element*)Q_Next(&fault_struct->children);
91                }
92             }
93          }
94       }
95                 else if (!strcmp(el->name, ELEM_DATA)   /* should be ELEM_ARRAY, but there is an extra level. weird */
96                          || (!strcmp(el->name, ELEM_PARAMS) && 
97                                   (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) ) {           /* this "PARAMS" concept is silly.  dave?! */
98          xml_element* iter = (xml_element*)Q_Head(&el->children);
99          XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
100
101          while (iter) {
102             XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
103             xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
104             XMLRPC_AddValueToVector(current_val, xNextVal);
105             iter = (xml_element*)Q_Next(&el->children);
106          }
107                 }
108                 else if (!strcmp(el->name, ELEM_STRUCT)) {
109          xml_element* iter = (xml_element*)Q_Head(&el->children);
110          XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
111
112          while ( iter ) {
113             XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
114             xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
115             XMLRPC_AddValueToVector(current_val, xNextVal);
116             iter = (xml_element*)Q_Next(&el->children);
117          }
118                 }
119                 else if (!strcmp(el->name, ELEM_STRING) || 
120                  (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
121          XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
122                 }
123                 else if (!strcmp(el->name, ELEM_NAME)) {
124          XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
125                 }
126                 else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
127          XMLRPC_SetValueInt(current_val, atoi(el->text.str));
128                 }
129                 else if (!strcmp(el->name, ELEM_BOOLEAN)) {
130          XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
131                 }
132                 else if (!strcmp(el->name, ELEM_DOUBLE)) {
133          XMLRPC_SetValueDouble(current_val, atof(el->text.str));
134                 }
135                 else if (!strcmp(el->name, ELEM_DATETIME)) {
136          XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
137                 }
138                 else if (!strcmp(el->name, ELEM_BASE64)) {
139          struct buffer_st buf;
140          base64_decode(&buf, el->text.str, el->text.len);
141          XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
142          buffer_delete(&buf);
143                 }
144                 else {
145          xml_element* iter;
146
147          if (!strcmp(el->name, ELEM_METHODCALL)) {
148             if (request) {
149                XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
150             }
151                         }
152                         else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
153             if (request) {
154                XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
155             }
156                         }
157                         else if (!strcmp(el->name, ELEM_METHODNAME)) {
158             if (request) {
159                XMLRPC_RequestSetMethodName(request, el->text.str);
160             }
161          }
162
163          iter = (xml_element*)Q_Head(&el->children);
164          while ( iter ) {
165             xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, 
166                                                  current_val, iter);
167             iter = (xml_element*)Q_Next(&el->children);
168          }
169       }
170    }
171    return current_val;
172 }
173
174 XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
175 {
176    return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
177 }
178
179 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
180 {
181    if (request) {
182       return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
183    }
184    return NULL;
185 }
186
187 xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, 
188                                           XMLRPC_REQUEST_TYPE request_type, int depth) {
189 #define BUF_SIZE 512
190    xml_element* root = NULL;
191    if (node) {
192       char buf[BUF_SIZE];
193       XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
194       XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
195       xml_element* elem_val = xml_elem_new();
196
197       /* special case for when root element is not an array */
198       if (depth == 0 && 
199           !(type == xmlrpc_vector && 
200             vtype == xmlrpc_vector_array && 
201             request_type == xmlrpc_request_call) ) {
202          int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
203
204          xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
205          if (next_el) {
206             Q_PushTail(&elem_val->children, next_el);
207          }
208          elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
209                 }
210                 else {
211          switch (type) {
212                         case xmlrpc_empty: /*  treat null value as empty string in xmlrpc. */
213          case xmlrpc_string:
214             elem_val->name = strdup(ELEM_STRING);
215             simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
216             break;
217          case xmlrpc_int:
218             elem_val->name = strdup(ELEM_INT);
219             snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
220             simplestring_add(&elem_val->text, buf);
221             break;
222          case xmlrpc_boolean:
223             elem_val->name = strdup(ELEM_BOOLEAN);
224             snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
225             simplestring_add(&elem_val->text, buf);
226             break;
227          case xmlrpc_double:
228             elem_val->name = strdup(ELEM_DOUBLE);
229             snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
230             simplestring_add(&elem_val->text, buf);
231             break;
232          case xmlrpc_datetime:
233             elem_val->name = strdup(ELEM_DATETIME);
234             simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
235             break;
236          case xmlrpc_base64:
237             {
238                struct buffer_st buf;
239                elem_val->name = strdup(ELEM_BASE64);
240                base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
241                simplestring_addn(&elem_val->text, buf.data, buf.offset );
242                buffer_delete(&buf);
243             }
244             break;
245          case xmlrpc_vector:
246             {
247                XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
248                XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
249                xml_element* root_vector_elem = elem_val;
250
251                switch (my_type) {
252                case xmlrpc_vector_array:
253                   {
254                       if(depth == 0) {
255                          elem_val->name = strdup(ELEM_PARAMS);
256                       }
257                       else {
258                          /* Hi my name is Dave and I like to make things as confusing
259                           * as possible, thus I will throw in this 'data' element
260                           * where it absolutely does not belong just so that people
261                           * cannot code arrays and structs in a similar and straight
262                           * forward manner. Have a good day.
263                           *
264                           * GRRRRRRRRR!
265                           */
266                          xml_element* data = xml_elem_new();
267                          data->name = strdup(ELEM_DATA);
268     
269                          elem_val->name = strdup(ELEM_ARRAY);
270                          Q_PushTail(&elem_val->children, data);
271                          root_vector_elem = data;
272                       }
273                   }
274                   break;
275                case xmlrpc_vector_mixed:       /* not officially supported */
276                case xmlrpc_vector_struct:
277                   elem_val->name = strdup(ELEM_STRUCT);
278                   break;
279                default:
280                   break;
281                }
282
283                /* recurse through sub-elements */
284                while ( xIter ) {
285                   xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
286                   if (next_el) {
287                      Q_PushTail(&root_vector_elem->children, next_el);
288                   }
289                   xIter = XMLRPC_VectorNext(node);
290                }
291             }
292             break;
293          default:
294             break;
295          }
296       }
297
298       {
299          XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
300
301          if (depth == 1) {
302             xml_element* value = xml_elem_new();
303             value->name = strdup(ELEM_VALUE);
304
305             /* yet another hack for the "fault" crap */
306             if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
307                root = value;
308                                 }
309                                 else {
310                xml_element* param = xml_elem_new();
311                param->name = strdup(ELEM_PARAM);
312
313                Q_PushTail(&param->children, value);
314
315                root = param;
316             }
317             Q_PushTail(&value->children, elem_val);
318                         }
319                         else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
320             xml_element* member = xml_elem_new();
321             xml_element* name = xml_elem_new();
322             xml_element* value = xml_elem_new();
323
324             member->name = strdup(ELEM_MEMBER);
325             name->name = strdup(ELEM_NAME);
326             value->name = strdup(ELEM_VALUE);
327
328             simplestring_add(&name->text, XMLRPC_GetValueID(node));
329
330             Q_PushTail(&member->children, name);
331             Q_PushTail(&member->children, value);
332             Q_PushTail(&value->children, elem_val);
333
334             root = member;
335                         }
336                         else if (vtype == xmlrpc_vector_array) {
337             xml_element* value = xml_elem_new();
338
339             value->name = strdup(ELEM_VALUE);
340
341             Q_PushTail(&value->children, elem_val);
342
343             root = value;
344                         }
345                         else if (vtype == xmlrpc_vector_none) {
346             /* no parent.  non-op */
347             root = elem_val;
348                         }
349                         else {
350             xml_element* value = xml_elem_new();
351
352             value->name = strdup(ELEM_VALUE);
353
354             Q_PushTail(&value->children, elem_val);
355
356             root = value;
357          }
358       }
359    }
360    return root;
361 }
362
363 xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
364    return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
365 }
366
367 xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
368    xml_element* wrapper = NULL;
369    if (request) {
370       const char* pStr = NULL;
371       XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
372       XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
373
374       wrapper = xml_elem_new();
375
376       if (request_type == xmlrpc_request_call) {
377          pStr = ELEM_METHODCALL;
378                 }
379                 else if (request_type == xmlrpc_request_response) {
380          pStr = ELEM_METHODRESPONSE;
381       }
382       if (pStr) {
383          wrapper->name = strdup(pStr);
384       }
385
386                 if(request_type == xmlrpc_request_call) {
387       pStr = XMLRPC_RequestGetMethodName(request);
388
389       if (pStr) {
390          xml_element* method = xml_elem_new();
391          method->name = strdup(ELEM_METHODNAME);
392          simplestring_add(&method->text, pStr);
393          Q_PushTail(&wrapper->children, method);
394       }
395                 }
396       if (xParams) {
397          Q_PushTail(&wrapper->children, 
398                     XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
399                 }
400                 else {
401          /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
402          xml_element* params = xml_elem_new();
403          params->name = strdup(ELEM_PARAMS);
404          Q_PushTail(&wrapper->children, params);
405       }
406    }
407    return wrapper;
408 }
409