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