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$";
35 
36 #include "php.h"
37 #include "main/snprintf.h"
38 #include <string.h>
39 #include <stdlib.h>
40 #include "xml_to_xmlrpc.h"
41 #include "base64.h"
42 
43 /* list of tokens used in vocab */
44 #define ELEM_ARRAY          "array"
45 #define ELEM_BASE64         "base64"
46 #define ELEM_BOOLEAN        "boolean"
47 #define ELEM_DATA           "data"
48 #define ELEM_DATETIME       "dateTime.iso8601"
49 #define ELEM_DOUBLE         "double"
50 #define ELEM_FAULT          "fault"
51 #define ELEM_FAULTCODE      "faultCode"
52 #define ELEM_FAULTSTRING    "faultString"
53 #define ELEM_I4             "i4"
54 #define ELEM_INT            "int"
55 #define ELEM_MEMBER         "member"
56 #define ELEM_METHODCALL     "methodCall"
57 #define ELEM_METHODNAME     "methodName"
58 #define ELEM_METHODRESPONSE "methodResponse"
59 #define ELEM_NAME           "name"
60 #define ELEM_PARAM          "param"
61 #define ELEM_PARAMS         "params"
62 #define ELEM_STRING         "string"
63 #define ELEM_STRUCT         "struct"
64 #define ELEM_VALUE          "value"
65 
66 
xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request,XMLRPC_VALUE parent_vector,XMLRPC_VALUE current_val,xml_element * el)67 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
68    if (!current_val) {
69       /* This should only be the case for the first element */
70       current_val = XMLRPC_CreateValueEmpty();
71    }
72 
73 	if (el->name) {
74 
75       /* first, deal with the crazy/stupid fault format */
76       if (!strcmp(el->name, ELEM_FAULT)) {
77 			xml_element* fault_value = (xml_element*)Q_Head(&el->children);
78 			XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
79 
80          if(fault_value) {
81             xml_element* fault_struct = (xml_element*)Q_Head(&fault_value->children);
82             if(fault_struct) {
83                xml_element* iter = (xml_element*)Q_Head(&fault_struct->children);
84 
85                while (iter) {
86                   XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
87                   xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
88                   XMLRPC_AddValueToVector(current_val, xNextVal);
89                   iter = (xml_element*)Q_Next(&fault_struct->children);
90                }
91             }
92          }
93       }
94 		else if (!strcmp(el->name, ELEM_DATA)	/* should be ELEM_ARRAY, but there is an extra level. weird */
95 			 || (!strcmp(el->name, ELEM_PARAMS) &&
96 				  (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) ) {		/* this "PARAMS" concept is silly.  dave?! */
97          xml_element* iter = (xml_element*)Q_Head(&el->children);
98          XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
99 
100          while (iter) {
101             XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
102             xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
103             XMLRPC_AddValueToVector(current_val, xNextVal);
104             iter = (xml_element*)Q_Next(&el->children);
105          }
106 		}
107 		else if (!strcmp(el->name, ELEM_STRUCT)) {
108          xml_element* iter = (xml_element*)Q_Head(&el->children);
109          XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
110 
111          while ( iter ) {
112             XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
113             xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
114             XMLRPC_AddValueToVector(current_val, xNextVal);
115             iter = (xml_element*)Q_Next(&el->children);
116          }
117 		}
118 		else if (!strcmp(el->name, ELEM_STRING) ||
119                  (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
120          XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
121 		}
122 		else if (!strcmp(el->name, ELEM_NAME)) {
123          XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
124 		}
125 		else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
126          XMLRPC_SetValueInt(current_val, atoi(el->text.str));
127 		}
128 		else if (!strcmp(el->name, ELEM_BOOLEAN)) {
129          XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
130 		}
131 		else if (!strcmp(el->name, ELEM_DOUBLE)) {
132          XMLRPC_SetValueDouble(current_val, atof(el->text.str));
133 		}
134 		else if (!strcmp(el->name, ELEM_DATETIME)) {
135          XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
136 		}
137 		else if (!strcmp(el->name, ELEM_BASE64)) {
138          struct buffer_st buf;
139          base64_decode_xmlrpc(&buf, el->text.str, el->text.len);
140          XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
141          buffer_delete(&buf);
142 		}
143 		else {
144          xml_element* iter;
145 
146          if (!strcmp(el->name, ELEM_METHODCALL)) {
147             if (request) {
148                XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
149             }
150 			}
151 			else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
152             if (request) {
153                XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
154             }
155 			}
156 			else if (!strcmp(el->name, ELEM_METHODNAME)) {
157             if (request) {
158                XMLRPC_RequestSetMethodName(request, el->text.str);
159             }
160          }
161 
162          iter = (xml_element*)Q_Head(&el->children);
163          while ( iter ) {
164             xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector,
165                                                  current_val, iter);
166             iter = (xml_element*)Q_Next(&el->children);
167          }
168       }
169    }
170    return current_val;
171 }
172 
xml_element_to_XMLRPC_VALUE(xml_element * el)173 XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
174 {
175    return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
176 }
177 
xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request,xml_element * el)178 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
179 {
180    if (request) {
181       return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
182    }
183    return NULL;
184 }
185 
XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector,XMLRPC_VALUE node,XMLRPC_REQUEST_TYPE request_type,int depth)186 xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node,
187                                           XMLRPC_REQUEST_TYPE request_type, int depth) {
188 #define BUF_SIZE 512
189    xml_element* root = NULL;
190    if (node) {
191       char buf[BUF_SIZE];
192       XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
193       XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
194       xml_element* elem_val = xml_elem_new();
195 
196       /* special case for when root element is not an array */
197       if (depth == 0 &&
198           !(type == xmlrpc_vector &&
199             vtype == xmlrpc_vector_array &&
200             request_type == xmlrpc_request_call) ) {
201          int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
202 
203          xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
204          if (next_el) {
205             Q_PushTail(&elem_val->children, next_el);
206          }
207          elem_val->name = estrdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
208 		}
209 		else {
210          switch (type) {
211 			case xmlrpc_empty: /*  treat null value as empty string in xmlrpc. */
212          case xmlrpc_string:
213             elem_val->name = estrdup(ELEM_STRING);
214             simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
215             break;
216          case xmlrpc_int:
217             elem_val->name = estrdup(ELEM_INT);
218             snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
219             simplestring_add(&elem_val->text, buf);
220             break;
221          case xmlrpc_boolean:
222             elem_val->name = estrdup(ELEM_BOOLEAN);
223             snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
224             simplestring_add(&elem_val->text, buf);
225             break;
226          case xmlrpc_double:
227             {
228                                 elem_val->name = estrdup(ELEM_DOUBLE);
229                 ap_php_snprintf(buf, BUF_SIZE, "%.*G", (int) EG(precision), XMLRPC_GetValueDouble(node));
230                 simplestring_add(&elem_val->text, buf);
231             }
232             break;
233          case xmlrpc_datetime:
234             elem_val->name = estrdup(ELEM_DATETIME);
235             simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
236             break;
237          case xmlrpc_base64:
238             {
239                struct buffer_st buf;
240                elem_val->name = estrdup(ELEM_BASE64);
241                base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
242                simplestring_addn(&elem_val->text, buf.data, buf.offset );
243                buffer_delete(&buf);
244             }
245             break;
246          case xmlrpc_vector:
247             {
248                XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
249                XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
250                xml_element* root_vector_elem = elem_val;
251 
252                switch (my_type) {
253                case xmlrpc_vector_array:
254                   {
255                       if(depth == 0) {
256                          elem_val->name = estrdup(ELEM_PARAMS);
257                       }
258                       else {
259                          /* Hi my name is Dave and I like to make things as confusing
260                           * as possible, thus I will throw in this 'data' element
261                           * where it absolutely does not belong just so that people
262                           * cannot code arrays and structs in a similar and straight
263                           * forward manner. Have a good day.
264                           *
265                           * GRRRRRRRRR!
266                           */
267                          xml_element* data = xml_elem_new();
268                          data->name = estrdup(ELEM_DATA);
269 
270                          elem_val->name = estrdup(ELEM_ARRAY);
271                          Q_PushTail(&elem_val->children, data);
272                          root_vector_elem = data;
273                       }
274                   }
275                   break;
276                case xmlrpc_vector_mixed:       /* not officially supported */
277                case xmlrpc_vector_struct:
278                   elem_val->name = estrdup(ELEM_STRUCT);
279                   break;
280                default:
281                   break;
282                }
283 
284                /* recurse through sub-elements */
285                while ( xIter ) {
286                   xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
287                   if (next_el) {
288                      Q_PushTail(&root_vector_elem->children, next_el);
289                   }
290                   xIter = XMLRPC_VectorNext(node);
291                }
292             }
293             break;
294          default:
295             break;
296          }
297       }
298 
299       {
300          XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
301 
302          if (depth == 1) {
303             xml_element* value = xml_elem_new();
304             value->name = estrdup(ELEM_VALUE);
305 
306             /* yet another hack for the "fault" crap */
307             if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
308                root = value;
309 				}
310 				else {
311                xml_element* param = xml_elem_new();
312                param->name = estrdup(ELEM_PARAM);
313 
314                Q_PushTail(&param->children, value);
315 
316                root = param;
317             }
318             Q_PushTail(&value->children, elem_val);
319 			}
320 			else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
321             xml_element* member = xml_elem_new();
322             xml_element* name = xml_elem_new();
323             xml_element* value = xml_elem_new();
324 
325             member->name = estrdup(ELEM_MEMBER);
326             name->name = estrdup(ELEM_NAME);
327             value->name = estrdup(ELEM_VALUE);
328 
329             simplestring_add(&name->text, XMLRPC_GetValueID(node));
330 
331             Q_PushTail(&member->children, name);
332             Q_PushTail(&member->children, value);
333             Q_PushTail(&value->children, elem_val);
334 
335             root = member;
336 			}
337 			else if (vtype == xmlrpc_vector_array) {
338             xml_element* value = xml_elem_new();
339 
340             value->name = estrdup(ELEM_VALUE);
341 
342             Q_PushTail(&value->children, elem_val);
343 
344             root = value;
345 			}
346 			else if (vtype == xmlrpc_vector_none) {
347             /* no parent.  non-op */
348             root = elem_val;
349 			}
350 			else {
351             xml_element* value = xml_elem_new();
352 
353             value->name = estrdup(ELEM_VALUE);
354 
355             Q_PushTail(&value->children, elem_val);
356 
357             root = value;
358          }
359       }
360    }
361    return root;
362 }
363 
XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node)364 xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
365    return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
366 }
367 
XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request)368 xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
369    xml_element* wrapper = NULL;
370    if (request) {
371       const char* pStr = NULL;
372       XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
373       XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
374 
375       wrapper = xml_elem_new();
376 
377       if (request_type == xmlrpc_request_call) {
378          pStr = ELEM_METHODCALL;
379 		}
380 		else if (request_type == xmlrpc_request_response) {
381          pStr = ELEM_METHODRESPONSE;
382       }
383       if (pStr) {
384          wrapper->name = estrdup(pStr);
385       }
386 
387 		if(request_type == xmlrpc_request_call) {
388       pStr = XMLRPC_RequestGetMethodName(request);
389 
390       if (pStr) {
391          xml_element* method = xml_elem_new();
392          method->name = estrdup(ELEM_METHODNAME);
393          simplestring_add(&method->text, pStr);
394          Q_PushTail(&wrapper->children, method);
395       }
396 		}
397       if (xParams) {
398          Q_PushTail(&wrapper->children,
399                     XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
400 		}
401 		else {
402          /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
403          xml_element* params = xml_elem_new();
404          params->name = estrdup(ELEM_PARAMS);
405          Q_PushTail(&wrapper->children, params);
406       }
407    }
408    return wrapper;
409 }
410