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