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