1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Brad Lafountain <rodif_bl@yahoo.com> |
16 | Shane Caraveo <shane@caraveo.com> |
17 | Dmitry Stogov <dmitry@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include "php_soap.h"
22
23 /* SOAP client calls this function to parse response from SOAP server */
parse_packet_soap(zval * this_ptr,char * buffer,int buffer_size,sdlFunctionPtr fn,char * fn_name,zval * return_value,zval * soap_headers)24 int parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctionPtr fn, char *fn_name, zval *return_value, zval *soap_headers)
25 {
26 char* envelope_ns = NULL;
27 xmlDocPtr response;
28 xmlNodePtr trav, env, head, body, resp, cur, fault;
29 xmlAttrPtr attr;
30 int param_count = 0;
31 int soap_version = SOAP_1_1;
32 HashTable *hdrs = NULL;
33
34 ZVAL_NULL(return_value);
35
36 /* Response for one-way opearation */
37 if (buffer_size == 0) {
38 return TRUE;
39 }
40
41 /* Parse XML packet */
42 response = soap_xmlParseMemory(buffer, buffer_size);
43
44 if (!response) {
45 add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL);
46 return FALSE;
47 }
48 if (xmlGetIntSubset(response) != NULL) {
49 add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL);
50 xmlFreeDoc(response);
51 return FALSE;
52 }
53
54 /* Get <Envelope> element */
55 env = NULL;
56 trav = response->children;
57 while (trav != NULL) {
58 if (trav->type == XML_ELEMENT_NODE) {
59 if (env == NULL && node_is_equal_ex(trav,"Envelope",SOAP_1_1_ENV_NAMESPACE)) {
60 env = trav;
61 envelope_ns = SOAP_1_1_ENV_NAMESPACE;
62 soap_version = SOAP_1_1;
63 } else if (env == NULL && node_is_equal_ex(trav,"Envelope",SOAP_1_2_ENV_NAMESPACE)) {
64 env = trav;
65 envelope_ns = SOAP_1_2_ENV_NAMESPACE;
66 soap_version = SOAP_1_2;
67 } else {
68 add_soap_fault(this_ptr, "VersionMismatch", "Wrong Version", NULL, NULL);
69 xmlFreeDoc(response);
70 return FALSE;
71 }
72 }
73 trav = trav->next;
74 }
75 if (env == NULL) {
76 add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL);
77 xmlFreeDoc(response);
78 return FALSE;
79 }
80
81 attr = env->properties;
82 while (attr != NULL) {
83 if (attr->ns == NULL) {
84 add_soap_fault(this_ptr, "Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL);
85 xmlFreeDoc(response);
86 return FALSE;
87 } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) {
88 if (soap_version == SOAP_1_2) {
89 add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL);
90 xmlFreeDoc(response);
91 return FALSE;
92 } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) {
93 add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL);
94 xmlFreeDoc(response);
95 return FALSE;
96 }
97 }
98 attr = attr->next;
99 }
100
101 /* Get <Header> element */
102 head = NULL;
103 trav = env->children;
104 while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
105 trav = trav->next;
106 }
107 if (trav != NULL && node_is_equal_ex(trav,"Header",envelope_ns)) {
108 head = trav;
109 trav = trav->next;
110 }
111
112 /* Get <Body> element */
113 body = NULL;
114 while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
115 trav = trav->next;
116 }
117 if (trav != NULL && node_is_equal_ex(trav,"Body",envelope_ns)) {
118 body = trav;
119 trav = trav->next;
120 }
121 while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
122 trav = trav->next;
123 }
124 if (body == NULL) {
125 add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL);
126 xmlFreeDoc(response);
127 return FALSE;
128 }
129 attr = body->properties;
130 while (attr != NULL) {
131 if (attr->ns == NULL) {
132 if (soap_version == SOAP_1_2) {
133 add_soap_fault(this_ptr, "Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL);
134 xmlFreeDoc(response);
135 return FALSE;
136 }
137 } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) {
138 if (soap_version == SOAP_1_2) {
139 add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Body", NULL, NULL);
140 xmlFreeDoc(response);
141 return FALSE;
142 } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) {
143 add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL);
144 xmlFreeDoc(response);
145 return FALSE;
146 }
147 }
148 attr = attr->next;
149 }
150 if (trav != NULL && soap_version == SOAP_1_2) {
151 add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL);
152 xmlFreeDoc(response);
153 return FALSE;
154 }
155
156 if (head != NULL) {
157 attr = head->properties;
158 while (attr != NULL) {
159 if (attr->ns == NULL) {
160 add_soap_fault(this_ptr, "Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL);
161 xmlFreeDoc(response);
162 return FALSE;
163 } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) {
164 if (soap_version == SOAP_1_2) {
165 add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Header", NULL, NULL);
166 xmlFreeDoc(response);
167 return FALSE;
168 } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) {
169 add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL);
170 xmlFreeDoc(response);
171 return FALSE;
172 }
173 }
174 attr = attr->next;
175 }
176 }
177
178 /* Check if <Body> contains <Fault> element */
179 fault = get_node_ex(body->children,"Fault",envelope_ns);
180 if (fault != NULL) {
181 char *faultcode = NULL;
182 zend_string *faultstring = NULL, *faultactor = NULL;
183 zval details;
184 xmlNodePtr tmp;
185
186 ZVAL_UNDEF(&details);
187 if (soap_version == SOAP_1_1) {
188 tmp = get_node(fault->children, "faultcode");
189 if (tmp != NULL && tmp->children != NULL) {
190 faultcode = (char*)tmp->children->content;
191 }
192
193 tmp = get_node(fault->children, "faultstring");
194 if (tmp != NULL && tmp->children != NULL) {
195 zval zv;
196 master_to_zval(&zv, get_conversion(IS_STRING), tmp);
197 faultstring = Z_STR(zv);
198 }
199
200 tmp = get_node(fault->children, "faultactor");
201 if (tmp != NULL && tmp->children != NULL) {
202 zval zv;
203 master_to_zval(&zv, get_conversion(IS_STRING), tmp);
204 faultactor = Z_STR(zv);
205 }
206
207 tmp = get_node(fault->children, "detail");
208 if (tmp != NULL) {
209 master_to_zval(&details, NULL, tmp);
210 }
211 } else {
212 tmp = get_node(fault->children, "Code");
213 if (tmp != NULL && tmp->children != NULL) {
214 tmp = get_node(tmp->children, "Value");
215 if (tmp != NULL && tmp->children != NULL) {
216 faultcode = (char*)tmp->children->content;
217 }
218 }
219
220 tmp = get_node(fault->children,"Reason");
221 if (tmp != NULL && tmp->children != NULL) {
222 /* TODO: lang attribute */
223 tmp = get_node(tmp->children,"Text");
224 if (tmp != NULL && tmp->children != NULL) {
225 zval zv;
226 master_to_zval(&zv, get_conversion(IS_STRING), tmp);
227 faultstring = Z_STR(zv);
228 }
229 }
230
231 tmp = get_node(fault->children,"Detail");
232 if (tmp != NULL) {
233 master_to_zval(&details, NULL, tmp);
234 }
235 }
236 add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor ? ZSTR_VAL(faultactor) : NULL, &details);
237 if (faultstring) {
238 zend_string_release_ex(faultstring, 0);
239 }
240 if (faultactor) {
241 zend_string_release_ex(faultactor, 0);
242 }
243 if (Z_REFCOUNTED(details)) {
244 Z_DELREF(details);
245 }
246 xmlFreeDoc(response);
247 return FALSE;
248 }
249
250 /* Parse content of <Body> element */
251 array_init(return_value);
252 resp = body->children;
253 while (resp != NULL && resp->type != XML_ELEMENT_NODE) {
254 resp = resp->next;
255 }
256 if (resp != NULL) {
257 if (fn != NULL && fn->binding && fn->binding->bindingType == BINDING_SOAP) {
258 /* Function has WSDL description */
259 sdlParamPtr param = NULL;
260 xmlNodePtr val = NULL;
261 char *name, *ns = NULL;
262 zval tmp;
263 sdlSoapBindingFunctionPtr fnb = (sdlSoapBindingFunctionPtr)fn->bindingAttributes;
264 int res_count;
265
266 hdrs = fnb->output.headers;
267
268 if (fn->responseParameters) {
269 res_count = zend_hash_num_elements(fn->responseParameters);
270 ZEND_HASH_FOREACH_PTR(fn->responseParameters, param) {
271 if (fnb->style == SOAP_DOCUMENT) {
272 if (param->element) {
273 name = param->element->name;
274 ns = param->element->namens;
275 /*
276 name = param->encode->details.type_str;
277 ns = param->encode->details.ns;
278 */
279 } else {
280 name = param->paramName;
281 }
282 } else {
283 name = fn->responseName;
284 /* ns = ? */
285 }
286
287 /* Get value of parameter */
288 cur = get_node_ex(resp, name, ns);
289 if (!cur) {
290 cur = get_node(resp, name);
291 /* TODO: produce warning invalid ns */
292 }
293 if (!cur && fnb->style == SOAP_RPC) {
294 cur = resp;
295 }
296 if (cur) {
297 if (fnb->style == SOAP_DOCUMENT) {
298 val = cur;
299 } else {
300 val = get_node(cur->children, param->paramName);
301 if (res_count == 1) {
302 if (val == NULL) {
303 val = get_node(cur->children, "return");
304 }
305 if (val == NULL) {
306 val = get_node(cur->children, "result");
307 }
308 if (val == NULL && cur->children && cur->children->next == NULL) {
309 val = cur->children;
310 }
311 }
312 }
313 }
314
315 if (!val) {
316 /* TODO: may be "nil" is not OK? */
317 ZVAL_NULL(&tmp);
318 /*
319 add_soap_fault(this_ptr, "Client", "Can't find response data", NULL, NULL);
320 xmlFreeDoc(response);
321 return FALSE;
322 */
323 } else {
324 /* Decoding value of parameter */
325 if (param != NULL) {
326 master_to_zval(&tmp, param->encode, val);
327 } else {
328 master_to_zval(&tmp, NULL, val);
329 }
330 }
331 add_assoc_zval(return_value, param->paramName, &tmp);
332
333 param_count++;
334 } ZEND_HASH_FOREACH_END();
335 }
336 } else {
337 /* Function has no WSDL description */
338 xmlNodePtr val;
339 val = resp->children;
340 while (val != NULL) {
341 while (val && val->type != XML_ELEMENT_NODE) {
342 val = val->next;
343 }
344 if (val != NULL) {
345 if (!node_is_equal_ex(val,"result",RPC_SOAP12_NAMESPACE)) {
346 zval tmp;
347 zval *arr;
348
349 master_to_zval(&tmp, NULL, val);
350 if (val->name) {
351 if ((arr = zend_hash_str_find(Z_ARRVAL_P(return_value), (char*)val->name, strlen((char*)val->name))) != NULL) {
352 add_next_index_zval(arr, &tmp);
353 } else if (val->next && get_node(val->next, (char*)val->name)) {
354 zval arr;
355
356 array_init(&arr);
357 add_next_index_zval(&arr, &tmp);
358 add_assoc_zval(return_value, (char*)val->name, &arr);
359 } else {
360 add_assoc_zval(return_value, (char*)val->name, &tmp);
361 }
362 } else {
363 add_next_index_zval(return_value, &tmp);
364 }
365 ++param_count;
366 }
367 val = val->next;
368 }
369 }
370 }
371 }
372
373 if (Z_TYPE_P(return_value) == IS_ARRAY) {
374 if (param_count == 0) {
375 zend_array_destroy(Z_ARR_P(return_value));
376 ZVAL_NULL(return_value);
377 } else if (param_count == 1) {
378 zval *tmp;
379
380 zend_hash_internal_pointer_reset(Z_ARRVAL_P(return_value));
381 tmp = zend_hash_get_current_data(Z_ARRVAL_P(return_value));
382 if (!Z_REFCOUNTED_P(return_value)) {
383 ZVAL_COPY(return_value, tmp);
384 } else {
385 zend_refcounted *garbage = Z_COUNTED_P(return_value);
386 ZVAL_COPY(return_value, tmp);
387 rc_dtor_func(garbage);
388 }
389 }
390 }
391
392 if (soap_headers && head) {
393 trav = head->children;
394 while (trav != NULL) {
395 if (trav->type == XML_ELEMENT_NODE) {
396 encodePtr enc = NULL;
397 zval val;
398
399 if (hdrs) {
400 smart_str key = {0};
401 sdlSoapBindingFunctionHeaderPtr hdr;
402
403 if (trav->ns) {
404 smart_str_appends(&key, (char*)trav->ns->href);
405 smart_str_appendc(&key,':');
406 }
407 smart_str_appends(&key, (char*)trav->name);
408 smart_str_0(&key);
409 if ((hdr = zend_hash_find_ptr(hdrs, key.s)) != NULL) {
410 enc = hdr->encode;
411 }
412 smart_str_free(&key);
413 }
414 master_to_zval(&val, enc, trav);
415 add_assoc_zval(soap_headers, (char*)trav->name, &val);
416 }
417 trav = trav->next;
418 }
419 }
420
421 xmlFreeDoc(response);
422 return TRUE;
423 }
424