xref: /PHP-7.4/ext/soap/php_packet_soap.c (revision 0cf7de1c)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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