1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 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