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