2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/ext_soap.h"
23 #include "folly/ScopeGuard.h"
25 #include "hphp/runtime/base/http-client.h"
26 #include "hphp/runtime/base/php-globals.h"
27 #include "hphp/runtime/server/http-protocol.h"
28 #include "hphp/runtime/base/class-info.h"
29 #include "hphp/runtime/ext/soap/soap.h"
30 #include "hphp/runtime/ext/soap/packet.h"
31 #include "hphp/runtime/base/string-util.h"
32 #include "hphp/runtime/ext/zlib/ext_zlib.h"
33 #include "hphp/runtime/ext/std/ext_std_network.h"
34 #include "hphp/runtime/ext/array/ext_array.h"
35 #include "hphp/runtime/ext/ext_function.h"
36 #include "hphp/runtime/ext/std/ext_std_classobj.h"
37 #include "hphp/runtime/ext/std/ext_std_output.h"
38 #include "hphp/runtime/ext/stream/ext_stream.h"
39 #include "hphp/runtime/ext/string/ext_string.h"
41 #include "hphp/system/systemlib.h"
45 const StaticString
s___dorequest("__dorequest");
47 IMPLEMENT_DEFAULT_EXTENSION_VERSION(soap
, NO_EXTENSION_VERSION_YET
);
49 ///////////////////////////////////////////////////////////////////////////////
50 // helper classes for setting/resetting globals within a method call
58 m_old_handler
= SOAP_GLOBAL(use_soap_error_handler
);
59 m_old_error_code
= SOAP_GLOBAL(error_code
);
60 m_old_error_object
= SOAP_GLOBAL(error_object
);
61 m_old_soap_version
= SOAP_GLOBAL(soap_version
);
63 SOAP_GLOBAL(use_soap_error_handler
) = true;
68 SOAP_GLOBAL(use_soap_error_handler
) = m_old_handler
;
69 SOAP_GLOBAL(error_code
) = m_old_error_code
;
70 SOAP_GLOBAL(error_object
) = m_old_error_object
;
71 SOAP_GLOBAL(soap_version
) = m_old_soap_version
;
76 const char *m_old_error_code
;
77 Object m_old_error_object
;
78 int m_old_soap_version
;
81 class SoapServerScope
: public SoapScope
{
83 explicit SoapServerScope(c_SoapServer
*server
) {
85 SOAP_GLOBAL(error_code
) = "Server";
86 SOAP_GLOBAL(error_object
) = Object(server
);
90 class SoapClientScope
: public SoapScope
{
92 explicit SoapClientScope(c_SoapClient
*client
) {
94 SOAP_GLOBAL(error_code
) = "Client";
95 SOAP_GLOBAL(error_object
) = Object(client
);
99 class SoapServiceScope
{
101 explicit SoapServiceScope(c_SoapServer
*server
) {
104 SOAP_GLOBAL(soap_version
) = server
->m_version
;
105 SOAP_GLOBAL(sdl
) = server
->m_sdl
;
106 SOAP_GLOBAL(encoding
) = server
->m_encoding
;
107 SOAP_GLOBAL(classmap
) = server
->m_classmap
;
108 SOAP_GLOBAL(typemap
) = server
->m_typemap
;
109 SOAP_GLOBAL(features
) = server
->m_features
;
112 explicit SoapServiceScope(c_SoapClient
*client
) {
115 SOAP_GLOBAL(soap_version
) = client
->m_soap_version
;
116 SOAP_GLOBAL(sdl
) = client
->m_sdl
;
117 SOAP_GLOBAL(encoding
) = client
->m_encoding
;
118 SOAP_GLOBAL(classmap
) = client
->m_classmap
;
119 SOAP_GLOBAL(typemap
) = client
->m_typemap
;
120 SOAP_GLOBAL(features
) = client
->m_features
;
123 ~SoapServiceScope() {
125 SOAP_GLOBAL(soap_version
) = m_old_soap_version
;
126 SOAP_GLOBAL(encoding
) = m_old_encoding
;
127 SOAP_GLOBAL(sdl
) = m_old_sdl
;
128 SOAP_GLOBAL(classmap
) = m_old_classmap
;
129 SOAP_GLOBAL(typemap
) = m_old_typemap
;
130 SOAP_GLOBAL(features
) = m_old_features
;
135 xmlCharEncodingHandlerPtr m_old_encoding
;
136 Array m_old_classmap
;
137 encodeMap
*m_old_typemap
;
138 int64_t m_old_features
;
139 int m_old_soap_version
;
143 m_old_soap_version
= SOAP_GLOBAL(soap_version
);
144 m_old_sdl
= SOAP_GLOBAL(sdl
);
145 m_old_encoding
= SOAP_GLOBAL(encoding
);
146 m_old_classmap
= SOAP_GLOBAL(classmap
);
147 m_old_typemap
= SOAP_GLOBAL(typemap
);
148 m_old_features
= SOAP_GLOBAL(features
);
152 ///////////////////////////////////////////////////////////////////////////////
153 // forward declarations
155 static void throw_soap_server_fault(litstr code
, litstr fault
);
156 static void model_to_string(sdlContentModelPtr model
, StringBuffer
&buf
,
158 static void header_if_not_sent(const String
& str
);
160 ///////////////////////////////////////////////////////////////////////////////
163 static Object
create_soap_fault(const String
& code
, const String
& fault
) {
164 return Object(SystemLib::AllocSoapFaultObject(code
, fault
));
167 static Object
create_soap_fault(Exception
&e
) {
169 return create_soap_fault(SOAP_GLOBAL(error_code
), String(e
.getMessage()));
172 static sdlParamPtr
get_param(sdlFunction
*function
, const char *param_name
,
173 int index
, bool response
) {
177 return sdlParamPtr();
181 ht
= &function
->requestParameters
;
183 ht
= &function
->responseParameters
;
187 return sdlParamPtr();
191 for (unsigned int i
= 0; i
< ht
->size(); i
++) {
192 sdlParamPtr p
= (*ht
)[i
];
193 if (p
->paramName
== param_name
) return p
;
196 if (index
>= 0 && index
< (int)ht
->size()) {
200 return sdlParamPtr();
203 static xmlNodePtr
serialize_zval(const Variant
& val
, sdlParamPtr param
,
204 const char *paramName
, int style
,
213 if (param
->element
) {
214 if (!param
->element
->fixed
.empty()) {
215 v
= String(param
->element
->fixed
);
216 } else if (!param
->element
->def
.empty() && !param
->element
->nillable
) {
217 v
= String(param
->element
->def
);
224 xmlParam
= master_to_xml(enc
, val
, style
, parent
);
225 if (!strcmp((char*)xmlParam
->name
, "BOGUS")) {
226 xmlNodeSetName(xmlParam
, BAD_CAST(paramName
));
231 static xmlNodePtr
serialize_parameter(sdlParamPtr param
, Variant value
,
232 int index
, const char *name
, int style
,
234 if (!value
.isNull() && value
.isObject()) {
235 c_SoapParam
*p
= value
.toObject().getTyped
<c_SoapParam
>(true, true);
238 name
= p
->m_name
.c_str();
242 if (param
&& !param
->paramName
.empty()) {
243 name
= param
->paramName
.c_str();
246 char paramNameBuf
[10];
247 snprintf(paramNameBuf
, sizeof(paramNameBuf
), "param%d", index
);
252 return serialize_zval(value
, param
, name
, style
, parent
);
255 static xmlDocPtr serialize_function_call
256 (c_SoapClient
*client
, std::shared_ptr
<sdlFunction
> function
,
257 const char *function_name
,
258 const char *uri
, const Array
& arguments
, const Array
& soap_headers
) {
259 xmlNodePtr envelope
= NULL
, body
, method
= NULL
, head
= NULL
;
262 sdlSoapBindingFunctionHeaderMap
*hdrs
= NULL
;
266 xmlDoc
*doc
= xmlNewDoc(BAD_CAST("1.0"));
267 doc
->encoding
= xmlCharStrdup("UTF-8");
268 doc
->charset
= XML_CHAR_ENCODING_UTF8
;
269 if (client
->m_soap_version
== SOAP_1_1
) {
270 envelope
= xmlNewDocNode(doc
, NULL
, BAD_CAST("Envelope"), NULL
);
271 ns
= xmlNewNs(envelope
, BAD_CAST(SOAP_1_1_ENV_NAMESPACE
),
272 BAD_CAST(SOAP_1_1_ENV_NS_PREFIX
));
273 xmlSetNs(envelope
, ns
);
274 } else if (client
->m_soap_version
== SOAP_1_2
) {
275 envelope
= xmlNewDocNode(doc
, NULL
, BAD_CAST("Envelope"), NULL
);
276 ns
= xmlNewNs(envelope
, BAD_CAST(SOAP_1_2_ENV_NAMESPACE
),
277 BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
));
278 xmlSetNs(envelope
, ns
);
280 throw SoapException("Unknown SOAP version");
282 xmlDocSetRootElement(doc
, envelope
);
284 if (!soap_headers
.empty()) {
285 head
= xmlNewChild(envelope
, ns
, BAD_CAST("Header"), NULL
);
288 body
= xmlNewChild(envelope
, ns
, BAD_CAST("Body"), NULL
);
290 if (function
&& function
->binding
->bindingType
== BINDING_SOAP
) {
291 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
293 hdrs
= &fnb
->input
.headers
;
295 /*FIXME: how to pass method name if style is SOAP_DOCUMENT */
296 /*style = SOAP_RPC;*/
297 use
= fnb
->input
.use
;
298 if (style
== SOAP_RPC
) {
299 ns
= encode_add_ns(body
, fnb
->input
.ns
.c_str());
300 if (!function
->requestName
.empty()) {
301 method
= xmlNewChild(body
, ns
,
302 BAD_CAST(function
->requestName
.c_str()), NULL
);
304 method
= xmlNewChild(body
, ns
,
305 BAD_CAST(function
->functionName
.c_str()), NULL
);
310 style
= client
->m_style
;
311 /*FIXME: how to pass method name if style is SOAP_DOCUMENT */
312 /*style = SOAP_RPC;*/
313 if (style
== SOAP_RPC
) {
314 ns
= encode_add_ns(body
, uri
);
316 method
= xmlNewChild(body
, ns
, BAD_CAST(function_name
), NULL
);
317 } else if (function
&& !function
->requestName
.empty()) {
318 method
= xmlNewChild(body
, ns
,
319 BAD_CAST(function
->requestName
.c_str()), NULL
);
320 } else if (function
&& !function
->functionName
.empty()) {
321 method
= xmlNewChild(body
, ns
,
322 BAD_CAST(function
->functionName
.c_str()), NULL
);
332 for (ArrayIter
iter(arguments
); iter
; ++iter
, ++i
) {
334 sdlParamPtr parameter
;
336 parameter
= get_param(function
.get(), NULL
, i
, false);
339 if (style
== SOAP_RPC
) {
341 param
= serialize_parameter(parameter
, iter
.second(), i
, NULL
,
344 } else if (style
== SOAP_DOCUMENT
) {
345 param
= serialize_parameter(parameter
, iter
.second(), i
, NULL
,
347 if (function
&& function
->binding
->bindingType
== BINDING_SOAP
) {
348 if (parameter
&& parameter
->element
) {
349 ns
= encode_add_ns(param
, parameter
->element
->namens
.c_str());
350 xmlNodeSetName(param
, BAD_CAST(parameter
->element
->name
.c_str()));
357 if (function
&& !function
->requestParameters
.empty()) {
358 int n
= function
->requestParameters
.size();
359 if (n
> arguments
.size()) {
360 for (i
= arguments
.size(); i
< n
; i
++) {
362 sdlParamPtr parameter
= get_param(function
.get(), NULL
, i
, false);
364 if (style
== SOAP_RPC
) {
365 param
= serialize_parameter(parameter
, uninit_null(), i
, NULL
, use
, method
);
366 } else if (style
== SOAP_DOCUMENT
) {
367 param
= serialize_parameter(parameter
, uninit_null(), i
, NULL
, use
, body
);
368 if (function
&& function
->binding
->bindingType
== BINDING_SOAP
) {
369 if (parameter
&& parameter
->element
) {
370 ns
= encode_add_ns(param
, parameter
->element
->namens
.c_str());
371 xmlNodeSetName(param
,
372 BAD_CAST(parameter
->element
->name
.c_str()));
382 for (ArrayIter
iter(soap_headers
); iter
; ++iter
) {
383 c_SoapHeader
*header
= iter
.second().toObject().getTyped
<c_SoapHeader
>();
387 int hdr_use
= SOAP_LITERAL
;
391 std::string key
= header
->m_namespace
.data();
393 key
+= header
->m_name
.data();
394 sdlSoapBindingFunctionHeaderMap::iterator iter
= hdrs
->find(key
);
395 if (iter
!= hdrs
->end()) {
396 auto hdr
= iter
->second
;
399 if (hdr_use
== SOAP_ENCODED
) {
405 if (!header
->m_data
.isNull()) {
406 h
= master_to_xml(enc
, header
->m_data
, hdr_use
, head
);
407 xmlNodeSetName(h
, BAD_CAST(header
->m_name
.data()));
409 h
= xmlNewNode(NULL
, BAD_CAST(header
->m_name
.data()));
410 xmlAddChild(head
, h
);
412 nsptr
= encode_add_ns(h
, header
->m_namespace
.data());
415 if (header
->m_mustUnderstand
) {
416 if (client
->m_soap_version
== SOAP_1_1
) {
417 xmlSetProp(h
, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX
":mustUnderstand"),
420 xmlSetProp(h
, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
":mustUnderstand"),
424 if (!header
->m_actor
.isNull()) {
425 if (header
->m_actor
.isString()) {
426 if (client
->m_soap_version
== SOAP_1_1
) {
427 xmlSetProp(h
, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX
":actor"),
428 BAD_CAST(header
->m_actor
.toString().data()));
430 xmlSetProp(h
, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
":role"),
431 BAD_CAST(header
->m_actor
.toString().data()));
433 } else if (header
->m_actor
.isInteger()) {
434 int64_t actor
= header
->m_actor
.toInt64();
435 if (client
->m_soap_version
== SOAP_1_1
) {
436 if (actor
== SOAP_ACTOR_NEXT
) {
437 xmlSetProp(h
, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX
":actor"),
438 BAD_CAST(SOAP_1_1_ACTOR_NEXT
));
441 if (actor
== SOAP_ACTOR_NEXT
) {
442 xmlSetProp(h
, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
":role"),
443 BAD_CAST(SOAP_1_2_ACTOR_NEXT
));
444 } else if (actor
== SOAP_ACTOR_NONE
) {
445 xmlSetProp(h
, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
":role"),
446 BAD_CAST(SOAP_1_2_ACTOR_NONE
));
447 } else if (actor
== SOAP_ACTOR_UNLIMATERECEIVER
) {
448 xmlSetProp(h
, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
":role"),
449 BAD_CAST(SOAP_1_2_ACTOR_UNLIMATERECEIVER
));
457 if (use
== SOAP_ENCODED
) {
458 xmlNewNs(envelope
, BAD_CAST(XSD_NAMESPACE
), BAD_CAST(XSD_NS_PREFIX
));
459 if (client
->m_soap_version
== SOAP_1_1
) {
460 xmlNewNs(envelope
, BAD_CAST(SOAP_1_1_ENC_NAMESPACE
),
461 BAD_CAST(SOAP_1_1_ENC_NS_PREFIX
));
462 xmlSetNsProp(envelope
, envelope
->ns
, BAD_CAST("encodingStyle"),
463 BAD_CAST(SOAP_1_1_ENC_NAMESPACE
));
464 } else if (client
->m_soap_version
== SOAP_1_2
) {
465 xmlNewNs(envelope
, BAD_CAST(SOAP_1_2_ENC_NAMESPACE
),
466 BAD_CAST(SOAP_1_2_ENC_NS_PREFIX
));
468 xmlSetNsProp(method
, envelope
->ns
, BAD_CAST("encodingStyle"),
469 BAD_CAST(SOAP_1_2_ENC_NAMESPACE
));
478 static bool do_request(c_SoapClient
*client
, xmlDoc
*request
,
479 const char *location
, const char *action
, int version
,
480 bool one_way
, Variant
&response
) {
481 char *buf
; int buf_size
;
482 xmlDocDumpMemory(request
, (xmlChar
**)&buf
, &buf_size
);
484 client
->m_soap_fault
=
485 create_soap_fault("HTTP", "Error build soap request");
489 if (client
->m_trace
) {
490 client
->m_last_request
= String((char*)buf
, buf_size
, CopyString
);
492 response
= client
->o_invoke_few_args(s___dorequest
, 5,
493 String(buf
, buf_size
, CopyString
),
494 String(location
, CopyString
),
495 String(action
, CopyString
),
497 if (!response
.isString()) {
498 if (client
->m_soap_fault
.isNull()) {
499 client
->m_soap_fault
=
500 create_soap_fault("Client", "SoapClient::__doRequest() "
501 "returned non string value");
503 } else if (client
->m_trace
) {
504 client
->m_last_response
= response
;
508 return client
->m_soap_fault
.isNull();
511 static void verify_soap_headers_array(Array
&headers
) {
512 for (ArrayIter
iter(headers
); iter
; ++iter
) {
513 Variant tmp
= iter
.second();
514 if (!tmp
.isObject() || !tmp
.toObject().is
<c_SoapHeader
>()) {
515 throw SoapException("Invalid SOAP header");
520 ///////////////////////////////////////////////////////////////////////////////
524 s_type_name("type_name"),
525 s_type_ns("type_ns"),
527 s_from_xml("from_xml");
529 static encodeMapPtr
soap_create_typemap_impl(sdl
*sdl
, Array
&ht
) {
530 encodeMapPtr
typemap(new encodeMap());
531 for (ArrayIter
iter(ht
); iter
; ++iter
) {
532 Variant tmp
= iter
.second();
533 if (!tmp
.isArray()) {
534 raise_warning("Wrong 'typemap' option");
537 Array ht2
= tmp
.toArray();
539 String type_name
, type_ns
;
540 Variant to_xml
, to_zval
;
541 for (ArrayIter
it(ht2
); it
; ++it
) {
543 if (it
.first().isString()) {
544 String name
= it
.first().toString();
545 if (name
== s_type_name
) {
546 if (tmp
.isString()) type_name
= tmp
.toString();
547 } else if (name
== s_type_ns
) {
548 if (tmp
.isString()) type_ns
= tmp
.toString();
549 } else if (name
== s_to_xml
) {
551 } else if (name
== s_from_xml
) {
557 encodePtr enc
, new_enc
;
558 if (!type_name
.empty()) {
559 if (!type_ns
.empty()) {
560 enc
= get_encoder(sdl
, type_ns
.data(), type_name
.data());
562 enc
= get_encoder_ex(sdl
, type_name
.data());
565 new_enc
= encodePtr(new encode());
567 new_enc
->details
.type
= enc
->details
.type
;
568 new_enc
->details
.ns
= enc
->details
.ns
;
569 new_enc
->details
.type_str
= enc
->details
.type_str
;
570 new_enc
->details
.sdl_type
= enc
->details
.sdl_type
;
572 enc
= get_conversion(UNKNOWN_TYPE
);
573 new_enc
->details
.type
= enc
->details
.type
;
574 if (!type_ns
.empty()) {
575 new_enc
->details
.ns
= type_ns
.data();
577 new_enc
->details
.type_str
= type_name
.data();
579 new_enc
->to_xml
= enc
->to_xml
;
580 new_enc
->to_zval
= enc
->to_zval
;
581 new_enc
->details
.map
= std::make_shared
<soapMapping
>();
582 if (to_xml
.toBoolean()) {
583 new_enc
->details
.map
->to_xml
= to_xml
;
584 new_enc
->to_xml
= to_xml_user
;
585 } else if (enc
->details
.map
&& !enc
->details
.map
->to_xml
.isNull()) {
586 new_enc
->details
.map
->to_xml
= enc
->details
.map
->to_xml
;
588 if (to_zval
.toBoolean()) {
589 new_enc
->details
.map
->to_zval
= to_zval
;
590 new_enc
->to_zval
= to_zval_user
;
591 } else if (enc
->details
.map
&& !enc
->details
.map
->to_zval
.isNull()) {
592 new_enc
->details
.map
->to_zval
= enc
->details
.map
->to_zval
;
596 if (!type_ns
.empty()) {
597 nscat
+= type_ns
.data();
600 nscat
+= type_name
.data();
601 (*typemap
)[nscat
] = new_enc
;
607 static encodeMap
*soap_create_typemap(sdl
*sdl
, Array
&ht
) {
608 return s_soap_data
->register_typemap(soap_create_typemap_impl(sdl
, ht
));
611 static void output_xml_header(int soap_version
) {
612 if (soap_version
== SOAP_1_2
) {
613 header_if_not_sent("Content-Type: application/soap+xml; charset=utf-8");
615 header_if_not_sent("Content-Type: text/xml; charset=utf-8");
619 ///////////////////////////////////////////////////////////////////////////////
623 * The PHP5 extension uses SAPI to set known headers; unlike header(), this
624 * silently fails if headers_sent(). We need to do the same.
626 static void header_if_not_sent(const String
& str
) {
627 if (!HHVM_FN(headers_sent
)()) {
628 HHVM_FN(header
)(str
);
632 static void deserialize_parameters(xmlNodePtr params
, sdlFunction
*function
,
634 int num_of_params
= 0;
637 bool use_names
= false;
638 num_of_params
= function
->requestParameters
.size();
639 if (num_of_params
== 0) return;
640 for (int i
= 0; i
< num_of_params
; i
++) {
641 sdlParamPtr param
= function
->requestParameters
[i
];
642 if (get_node(params
, (char*)param
->paramName
.c_str())) {
648 for (int i
= 0; i
< num_of_params
; i
++, cur_param
++) {
649 sdlParamPtr param
= function
->requestParameters
[i
];
650 xmlNodePtr val
= get_node(params
, (char*)param
->paramName
.c_str());
652 parameters
.append(master_to_zval(param
->encode
, val
));
659 int num_of_params
= 0;
660 xmlNodePtr trav
= params
;
661 while (trav
!= NULL
) {
662 if (trav
->type
== XML_ELEMENT_NODE
) {
668 if (num_of_params
== 1 && function
&& function
->binding
&&
669 function
->binding
->bindingType
== BINDING_SOAP
&&
670 function
->bindingAttributes
->style
== SOAP_DOCUMENT
&&
671 function
->requestParameters
.empty() &&
672 strcmp((char*)params
->name
, function
->functionName
.c_str()) == 0) {
674 } else if (num_of_params
> 0) {
676 while (trav
!= 0 && cur_param
< num_of_params
) {
677 if (trav
->type
== XML_ELEMENT_NODE
) {
681 if (cur_param
>= (int)function
->requestParameters
.size()) {
682 throw_soap_server_fault("Client","Error cannot find parameter");
684 param
= function
->requestParameters
[cur_param
];
691 parameters
.set(cur_param
, master_to_zval(enc
, trav
));
698 if (num_of_params
> cur_param
) {
699 throw_soap_server_fault("Client","Missing parameter");
703 static std::shared_ptr
<sdlFunction
>
704 get_doc_function(sdl
*sdl
, xmlNodePtr params
) {
706 for (sdlFunctionMap::iterator iter
= sdl
->functions
.begin();
707 iter
!= sdl
->functions
.end(); ++iter
) {
708 auto tmp
= iter
->second
;
709 if (tmp
->binding
&& tmp
->binding
->bindingType
== BINDING_SOAP
) {
710 sdlSoapBindingFunctionPtr fnb
= tmp
->bindingAttributes
;
711 if (fnb
->style
== SOAP_DOCUMENT
) {
712 if (params
== NULL
) {
713 if (tmp
->requestParameters
.empty()) {
716 } else if (!tmp
->requestParameters
.empty()) {
718 xmlNodePtr node
= params
;
719 for (unsigned int i
= 0; i
< tmp
->requestParameters
.size(); i
++) {
720 sdlParamPtr param
= tmp
->requestParameters
[i
];
721 if (param
->element
) {
722 if (param
->element
->name
!= (char*)node
->name
) {
726 if (!param
->element
->namens
.empty() && node
->ns
) {
727 if (param
->element
->namens
!= (char*)node
->ns
->href
) {
731 } else if ((param
->element
->namens
.empty() && node
->ns
) ||
732 (!param
->element
->namens
.empty() &&
737 } else if (param
->paramName
!= (char*)node
->name
) {
743 if (ok
/*&& node == NULL*/) {
751 return std::shared_ptr
<sdlFunction
>();
754 static std::shared_ptr
<sdlFunction
>
755 get_function(sdl
*sdl
, const char *function_name
) {
757 String lowered
= HHVM_FN(strtolower
)(function_name
);
758 sdlFunctionMap::iterator iter
= sdl
->functions
.find(lowered
.data());
759 if (iter
== sdl
->functions
.end()) {
760 iter
= sdl
->requests
.find(lowered
.data());
761 if (iter
== sdl
->requests
.end()) {
762 return std::shared_ptr
<sdlFunction
>();
767 return std::shared_ptr
<sdlFunction
>();
770 static std::shared_ptr
<sdlFunction
>
771 find_function(sdl
*sdl
, xmlNodePtr func
, String
&function_name
) {
772 auto function
= get_function(sdl
, (char*)func
->name
);
773 if (function
&& function
->binding
&&
774 function
->binding
->bindingType
== BINDING_SOAP
) {
775 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
776 if (fnb
->style
== SOAP_DOCUMENT
) {
777 if (func
->children
!= NULL
|| !function
->requestParameters
.empty()) {
782 if (sdl
!= NULL
&& function
== NULL
) {
783 function
= get_doc_function(sdl
, func
);
786 if (function
!= NULL
) {
787 function_name
= String(function
->functionName
);
789 function_name
= String((char *)func
->name
, CopyString
);
795 static std::shared_ptr
<sdlFunction
> deserialize_function_call
796 (sdl
*sdl
, xmlDocPtr request
, const char* actor
, String
&function_name
,
797 Array
¶meters
, int &version
, Array
&headers
) {
799 char* envelope_ns
= NULL
;
800 xmlNodePtr trav
,env
,head
,body
,func
;
802 std::shared_ptr
<sdlFunction
> function
;
806 /* Get <Envelope> element */
808 trav
= request
->children
;
809 while (trav
!= NULL
) {
810 if (trav
->type
== XML_ELEMENT_NODE
) {
812 node_is_equal_ex(trav
,"Envelope",SOAP_1_1_ENV_NAMESPACE
)) {
815 envelope_ns
= SOAP_1_1_ENV_NAMESPACE
;
816 SOAP_GLOBAL(soap_version
) = SOAP_1_1
;
817 } else if (env
== NULL
&&
818 node_is_equal_ex(trav
,"Envelope",SOAP_1_2_ENV_NAMESPACE
)) {
821 envelope_ns
= SOAP_1_2_ENV_NAMESPACE
;
822 SOAP_GLOBAL(soap_version
) = SOAP_1_2
;
824 throw_soap_server_fault("VersionMismatch", "Wrong Version");
830 throw_soap_server_fault
831 ("Client", "looks like we got XML without \"Envelope\" element");
834 attr
= env
->properties
;
835 while (attr
!= NULL
) {
836 if (attr
->ns
== NULL
) {
837 throw_soap_server_fault("Client", "A SOAP Envelope element cannot have "
838 "non Namespace qualified attributes");
839 } else if (attr_is_equal_ex(attr
,"encodingStyle",SOAP_1_2_ENV_NAMESPACE
)) {
840 if (version
== SOAP_1_2
) {
841 throw_soap_server_fault("Client", "encodingStyle cannot be specified "
843 } else if (strcmp((char*)attr
->children
->content
,
844 SOAP_1_1_ENC_NAMESPACE
) != 0) {
845 throw_soap_server_fault("Client", "Unknown data encoding style");
851 /* Get <Header> element */
853 trav
= env
->children
;
854 while (trav
!= NULL
&& trav
->type
!= XML_ELEMENT_NODE
) {
857 if (trav
!= NULL
&& node_is_equal_ex(trav
,"Header",envelope_ns
)) {
862 /* Get <Body> element */
864 while (trav
!= NULL
&& trav
->type
!= XML_ELEMENT_NODE
) {
867 if (trav
!= NULL
&& node_is_equal_ex(trav
,"Body",envelope_ns
)) {
871 while (trav
!= NULL
&& trav
->type
!= XML_ELEMENT_NODE
) {
875 throw_soap_server_fault("Client", "Body must be present in a "
878 attr
= body
->properties
;
879 while (attr
!= NULL
) {
880 if (attr
->ns
== NULL
) {
881 if (version
== SOAP_1_2
) {
882 throw_soap_server_fault("Client", "A SOAP Body element cannot have non"
883 " Namespace qualified attributes");
885 } else if (attr_is_equal_ex(attr
,"encodingStyle",SOAP_1_2_ENV_NAMESPACE
)) {
886 if (version
== SOAP_1_2
) {
887 throw_soap_server_fault("Client", "encodingStyle cannot be specified "
889 } else if (strcmp((char*)attr
->children
->content
,
890 SOAP_1_1_ENC_NAMESPACE
) != 0) {
891 throw_soap_server_fault("Client", "Unknown data encoding style");
897 if (trav
!= NULL
&& version
== SOAP_1_2
) {
898 throw_soap_server_fault("Client", "A SOAP 1.2 envelope can contain only "
903 trav
= body
->children
;
904 while (trav
!= NULL
) {
905 if (trav
->type
== XML_ELEMENT_NODE
) {
908 throw_soap_server_fault("Client", "looks like we got \"Body\" with "
909 "several functions call", NULL, NULL, NULL);
913 break; /* FIXME: the rest of body is ignored */
918 function
= get_doc_function(sdl
, NULL
);
919 if (function
!= NULL
) {
920 function_name
= String(function
->functionName
);
922 throw_soap_server_fault
923 ("Client", "looks like we got \"Body\" without function call");
926 if (version
== SOAP_1_1
) {
927 attr
= get_attribute_ex(func
->properties
,"encodingStyle",
928 SOAP_1_1_ENV_NAMESPACE
);
929 if (attr
&& strcmp((char*)attr
->children
->content
,
930 SOAP_1_1_ENC_NAMESPACE
) != 0) {
931 throw_soap_server_fault("Client","Unknown Data Encoding Style");
934 attr
= get_attribute_ex(func
->properties
,"encodingStyle",
935 SOAP_1_2_ENV_NAMESPACE
);
936 if (attr
&& strcmp((char*)attr
->children
->content
,
937 SOAP_1_2_ENC_NAMESPACE
) != 0) {
938 throw_soap_server_fault
939 ("DataEncodingUnknown","Unknown Data Encoding Style");
942 function
= find_function(sdl
, func
, function_name
);
943 if (sdl
!= NULL
&& function
== NULL
) {
944 if (version
== SOAP_1_2
) {
945 throw_soap_server_fault
946 ("rpc:ProcedureNotPresent","Procedure not present");
948 throw SoapException("Procedure '%s' not present", func
->name
);
953 headers
= Array::Create();
955 soapHeader
*h
= NULL
;
956 attr
= head
->properties
;
957 while (attr
!= NULL
) {
958 if (attr
->ns
== NULL
) {
959 throw_soap_server_fault("Client", "A SOAP Header element cannot have "
960 "non Namespace qualified attributes");
961 } else if (attr_is_equal_ex(attr
,"encodingStyle",
962 SOAP_1_2_ENV_NAMESPACE
)) {
963 if (version
== SOAP_1_2
) {
964 throw_soap_server_fault("Client", "encodingStyle cannot be specified"
966 } else if (strcmp((char*)attr
->children
->content
,
967 SOAP_1_1_ENC_NAMESPACE
) != 0) {
968 throw_soap_server_fault("Client", "Unknown data encoding style");
973 trav
= head
->children
;
974 while (trav
!= NULL
) {
975 if (trav
->type
== XML_ELEMENT_NODE
) {
976 xmlNodePtr hdr_func
= trav
;
978 int mustUnderstand
= 0;
980 if (version
== SOAP_1_1
) {
981 attr
= get_attribute_ex(hdr_func
->properties
,"encodingStyle",
982 SOAP_1_1_ENV_NAMESPACE
);
983 if (attr
&& strcmp((char*)attr
->children
->content
,
984 SOAP_1_1_ENC_NAMESPACE
) != 0) {
985 throw_soap_server_fault("Client","Unknown Data Encoding Style");
987 attr
= get_attribute_ex(hdr_func
->properties
,"actor",envelope_ns
);
989 if (strcmp((char*)attr
->children
->content
,
990 SOAP_1_1_ACTOR_NEXT
) != 0 &&
992 strcmp((char*)attr
->children
->content
,actor
) != 0)) {
996 } else if (version
== SOAP_1_2
) {
997 attr
= get_attribute_ex(hdr_func
->properties
,"encodingStyle",
998 SOAP_1_2_ENV_NAMESPACE
);
999 if (attr
&& strcmp((char*)attr
->children
->content
,
1000 SOAP_1_2_ENC_NAMESPACE
) != 0) {
1001 throw_soap_server_fault
1002 ("DataEncodingUnknown","Unknown Data Encoding Style");
1004 attr
= get_attribute_ex(hdr_func
->properties
,"role",envelope_ns
);
1006 if (strcmp((char*)attr
->children
->content
,
1007 SOAP_1_2_ACTOR_UNLIMATERECEIVER
) != 0 &&
1008 strcmp((char*)attr
->children
->content
,
1009 SOAP_1_2_ACTOR_NEXT
) != 0 &&
1011 strcmp((char*)attr
->children
->content
,actor
) != 0)) {
1016 attr
= get_attribute_ex(hdr_func
->properties
,"mustUnderstand",
1019 if (strcmp((char*)attr
->children
->content
,"1") == 0 ||
1020 strcmp((char*)attr
->children
->content
,"true") == 0) {
1022 } else if (strcmp((char*)attr
->children
->content
,"0") == 0 ||
1023 strcmp((char*)attr
->children
->content
,"false") == 0) {
1026 throw_soap_server_fault("Client",
1027 "mustUnderstand value is not boolean");
1030 h
= NEWOBJ(soapHeader
)();
1032 h
->function
= find_function(sdl
, hdr_func
, h
->function_name
).get();
1033 h
->mustUnderstand
= mustUnderstand
;
1035 if (!h
->function
&& sdl
&& function
&& function
->binding
&&
1036 function
->binding
->bindingType
== BINDING_SOAP
) {
1037 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
1038 if (!fnb
->input
.headers
.empty()) {
1041 key
+= (char*)hdr_func
->ns
->href
;
1044 key
+= (std::string
)h
->function_name
;
1045 sdlSoapBindingFunctionHeaderMap::iterator iter
=
1046 fnb
->input
.headers
.find(key
);
1047 if (iter
!= fnb
->input
.headers
.end()) {
1048 h
->hdr
= iter
->second
.get();
1053 h
->parameters
.append(master_to_zval(h
->hdr
->encode
, hdr_func
));
1055 if (h
->function
&& h
->function
->binding
&&
1056 h
->function
->binding
->bindingType
== BINDING_SOAP
) {
1057 sdlSoapBindingFunctionPtr fnb
= h
->function
->bindingAttributes
;
1058 if (fnb
->style
== SOAP_RPC
) {
1059 hdr_func
= hdr_func
->children
;
1062 deserialize_parameters(hdr_func
, h
->function
, h
->parameters
);
1064 headers
.append(hobj
);
1071 if (function
&& function
->binding
&&
1072 function
->binding
->bindingType
== BINDING_SOAP
) {
1073 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
1074 if (fnb
->style
== SOAP_RPC
) {
1075 func
= func
->children
;
1078 func
= func
->children
;
1080 deserialize_parameters(func
, function
.get(), parameters
);
1087 static int serialize_response_call2(xmlNodePtr body
, sdlFunction
*function
,
1088 const char *function_name
, const char *uri
,
1089 Variant
&ret
, int version
, int main
) {
1090 xmlNodePtr method
= NULL
, param
;
1091 sdlParamPtr parameter
;
1096 if (function
&& function
->binding
->bindingType
== BINDING_SOAP
) {
1097 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
1099 use
= fnb
->output
.use
;
1100 ns
= encode_add_ns(body
, fnb
->output
.ns
.c_str());
1101 if (style
== SOAP_RPC
) {
1102 if (!function
->responseName
.empty()) {
1103 method
= xmlNewChild(body
, ns
,
1104 BAD_CAST(function
->responseName
.c_str()), NULL
);
1105 } else if (!function
->responseParameters
.empty()) {
1106 method
= xmlNewChild(body
, ns
,
1107 BAD_CAST(function
->functionName
.c_str()), NULL
);
1111 style
= main
?SOAP_RPC
:SOAP_DOCUMENT
;
1112 use
= main
?SOAP_ENCODED
:SOAP_LITERAL
;
1113 if (style
== SOAP_RPC
) {
1114 ns
= encode_add_ns(body
, uri
);
1115 method
= xmlNewChild(body
, ns
, BAD_CAST(function_name
), NULL
);
1120 param_count
= function
->responseParameters
.size();
1125 if (param_count
== 1) {
1126 parameter
= get_param(function
, NULL
, 0, true);
1128 if (style
== SOAP_RPC
) {
1129 xmlNode
*rpc_result
;
1130 if (main
&& version
== SOAP_1_2
) {
1131 xmlNs
*rpc_ns
= xmlNewNs(body
, BAD_CAST(RPC_SOAP12_NAMESPACE
),
1132 BAD_CAST(RPC_SOAP12_NS_PREFIX
));
1133 rpc_result
= xmlNewChild(method
, rpc_ns
, BAD_CAST("result"), NULL
);
1134 param
= serialize_parameter(parameter
, ret
, 0, "return", use
, method
);
1135 xmlNodeSetContent(rpc_result
,param
->name
);
1137 param
= serialize_parameter(parameter
, ret
, 0, "return", use
, method
);
1140 param
= serialize_parameter(parameter
, ret
, 0, "return", use
, body
);
1141 if (function
&& function
->binding
->bindingType
== BINDING_SOAP
) {
1142 if (parameter
&& parameter
->element
) {
1143 ns
= encode_add_ns(param
, parameter
->element
->namens
.c_str());
1144 xmlNodeSetName(param
, BAD_CAST(parameter
->element
->name
.c_str()));
1145 xmlSetNs(param
, ns
);
1147 } else if (strcmp((char*)param
->name
,"return") == 0) {
1148 ns
= encode_add_ns(param
, uri
);
1149 xmlNodeSetName(param
, BAD_CAST(function_name
));
1150 xmlSetNs(param
, ns
);
1153 } else if (param_count
> 1 && ret
.isArray()) {
1154 Array arr
= ret
.toArray();
1156 for (ArrayIter
iter(arr
); iter
; ++iter
, ++i
) {
1157 Variant data
= iter
.second();
1158 Variant key
= iter
.first();
1160 int64_t param_index
= i
;
1161 if (key
.isString()) {
1162 param_name
= key
.toString();
1164 param_index
= key
.toInt64();
1167 parameter
= get_param(function
, param_name
.c_str(), param_index
, true);
1168 if (style
== SOAP_RPC
) {
1169 param
= serialize_parameter(parameter
, data
, i
, param_name
.data(),
1172 param
= serialize_parameter(parameter
, data
, i
, param_name
.data(),
1174 if (function
&& function
->binding
->bindingType
== BINDING_SOAP
) {
1175 if (parameter
&& parameter
->element
) {
1176 ns
= encode_add_ns(param
, parameter
->element
->namens
.c_str());
1177 xmlNodeSetName(param
, BAD_CAST(parameter
->element
->name
.c_str()));
1178 xmlSetNs(param
, ns
);
1184 if (use
== SOAP_ENCODED
&& version
== SOAP_1_2
&& method
) {
1185 xmlSetNsProp(method
, body
->ns
, BAD_CAST("encodingStyle"),
1186 BAD_CAST(SOAP_1_2_ENC_NAMESPACE
));
1191 static xmlDocPtr
serialize_response_call(
1192 std::shared_ptr
<sdlFunction
> function
,
1193 const char *function_name
,
1194 const char *uri
, Variant
&ret
,
1195 const Array
& headers
, int version
) {
1196 xmlNodePtr envelope
= NULL
, body
, param
;
1198 int use
= SOAP_LITERAL
;
1199 xmlNodePtr head
= NULL
;
1203 xmlDocPtr doc
= xmlNewDoc(BAD_CAST("1.0"));
1204 doc
->charset
= XML_CHAR_ENCODING_UTF8
;
1205 doc
->encoding
= xmlCharStrdup("UTF-8");
1207 if (version
== SOAP_1_1
) {
1208 envelope
= xmlNewDocNode(doc
, NULL
, BAD_CAST("Envelope"), NULL
);
1209 ns
= xmlNewNs(envelope
, BAD_CAST(SOAP_1_1_ENV_NAMESPACE
),
1210 BAD_CAST(SOAP_1_1_ENV_NS_PREFIX
));
1211 xmlSetNs(envelope
,ns
);
1212 } else if (version
== SOAP_1_2
) {
1213 envelope
= xmlNewDocNode(doc
, NULL
, BAD_CAST("Envelope"), NULL
);
1214 ns
= xmlNewNs(envelope
, BAD_CAST(SOAP_1_2_ENV_NAMESPACE
),
1215 BAD_CAST(SOAP_1_2_ENV_NS_PREFIX
));
1216 xmlSetNs(envelope
,ns
);
1218 throw_soap_server_fault("Server", "Unknown SOAP version");
1220 xmlDocSetRootElement(doc
, envelope
);
1222 if (ret
.isObject() &&
1223 ret
.toObject()->instanceof(SystemLib::s_SoapFaultClass
)) {
1224 ObjectData
* obj
= ret
.getObjectData();
1227 std::shared_ptr
<sdlFault
> fault
;
1230 if (!headers
.empty()) {
1233 int hdr_use
= SOAP_LITERAL
;
1234 Variant hdr_ret
= obj
->o_get("headerfault");
1235 soapHeader
*h
= headers
[0].toResource().getTyped
<soapHeader
>();
1236 const char *hdr_ns
= h
->hdr
? h
->hdr
->ns
.c_str() : NULL
;
1237 const char *hdr_name
= h
->function_name
.data();
1239 head
= xmlNewChild(envelope
, ns
, BAD_CAST("Header"), NULL
);
1240 if (hdr_ret
.isObject() && hdr_ret
.toObject().is
<c_SoapHeader
>()) {
1241 c_SoapHeader
*ht
= hdr_ret
.toObject().getTyped
<c_SoapHeader
>();
1244 if (!ht
->m_namespace
.empty()) {
1245 key
+= ht
->m_namespace
.data();
1247 hdr_ns
= ht
->m_namespace
.data();
1249 if (!ht
->m_name
.empty()) {
1250 key
+= ht
->m_name
.data();
1251 hdr_name
= ht
->m_name
.data();
1254 sdlSoapBindingFunctionHeaderMap::iterator iter
=
1255 h
->hdr
->headerfaults
.find(key
);
1256 if (iter
!= h
->hdr
->headerfaults
.end()) {
1257 auto hdr
= iter
->second
;
1258 hdr_enc
= hdr
->encode
;
1262 hdr_ret
= ht
->m_data
;
1263 obj
->o_set("headerfault", hdr_ret
);
1267 if (serialize_response_call2(head
, h
->function
,
1268 h
->function_name
.data(), uri
,
1269 hdr_ret
, version
, 0) == SOAP_ENCODED
) {
1270 obj
->o_set("headerfault", hdr_ret
);
1274 xmlNodePtr xmlHdr
= master_to_xml(hdr_enc
, hdr_ret
, hdr_use
, head
);
1276 xmlNodeSetName(xmlHdr
, BAD_CAST(hdr_name
));
1279 xmlNsPtr nsptr
= encode_add_ns(xmlHdr
, hdr_ns
);
1280 xmlSetNs(xmlHdr
, nsptr
);
1285 body
= xmlNewChild(envelope
, ns
, BAD_CAST("Body"), NULL
);
1286 param
= xmlNewChild(body
, ns
, BAD_CAST("Fault"), NULL
);
1288 fault_ns
= obj
->o_get("faultcodens").toString().data();
1290 if (!obj
->o_get("_name").toString().empty()) {
1292 sdlFaultMap::iterator iter
=
1293 function
->faults
.find(obj
->o_get("_name").toString().data());
1294 if (iter
!= function
->faults
.end()) {
1295 fault
= iter
->second
;
1296 if (function
->binding
&&
1297 function
->binding
->bindingType
== BINDING_SOAP
&&
1298 fault
->bindingAttributes
) {
1299 sdlSoapBindingFunctionFaultPtr fb
= fault
->bindingAttributes
;
1301 if (fault_ns
.empty()) {
1307 } else if (function
&& function
->faults
.size() == 1) {
1308 fault
= function
->faults
[0];
1309 if (function
->binding
&&
1310 function
->binding
->bindingType
== BINDING_SOAP
&&
1311 fault
->bindingAttributes
) {
1312 sdlSoapBindingFunctionFaultPtr fb
= fault
->bindingAttributes
;
1314 if (fault_ns
.empty()) {
1320 if (fault_ns
.empty() && fault
&& fault
->details
.size() == 1) {
1321 sdlParamPtr sparam
= fault
->details
[0];
1322 if (sparam
->element
) {
1323 fault_ns
= sparam
->element
->namens
;
1327 if (version
== SOAP_1_1
) {
1328 if (!obj
->o_get("faultcode").toString().empty()) {
1329 xmlNodePtr node
= xmlNewNode(NULL
, BAD_CAST("faultcode"));
1330 String str
= StringUtil::HtmlEncode(obj
->o_get("faultcode"),
1331 StringUtil::QuoteStyle::Double
,
1332 "UTF-8", true, true);
1333 xmlAddChild(param
, node
);
1334 if (!fault_ns
.empty()) {
1335 xmlNsPtr nsptr
= encode_add_ns(node
, fault_ns
.c_str());
1336 xmlChar
*code
= xmlBuildQName(BAD_CAST(str
.data()), nsptr
->prefix
,
1338 xmlNodeSetContent(node
, code
);
1341 xmlNodeSetContentLen(node
, BAD_CAST(str
.data()), str
.size());
1344 if (!obj
->o_get("faultstring").toString().empty()) {
1345 xmlNodePtr node
= master_to_xml(get_conversion(KindOfString
),
1346 obj
->o_get("faultstring"), SOAP_LITERAL
,
1348 xmlNodeSetName(node
, BAD_CAST("faultstring"));
1350 if (!obj
->o_get("faultactor").toString().empty()) {
1351 xmlNodePtr node
= master_to_xml(get_conversion(KindOfString
),
1352 obj
->o_get("faultactor"), SOAP_LITERAL
,
1354 xmlNodeSetName(node
, BAD_CAST("faultactor"));
1356 detail_name
= "detail";
1358 if (!obj
->o_get("faultcode").toString().empty()) {
1359 xmlNodePtr node
= xmlNewChild(param
, ns
, BAD_CAST("Code"), NULL
);
1360 String str
= StringUtil::HtmlEncode(obj
->o_get("faultcode"),
1361 StringUtil::QuoteStyle::Double
,
1362 "UTF-8", true, true);
1363 node
= xmlNewChild(node
, ns
, BAD_CAST("Value"), NULL
);
1364 if (!fault_ns
.empty()) {
1365 xmlNsPtr nsptr
= encode_add_ns(node
, fault_ns
.c_str());
1366 xmlChar
*code
= xmlBuildQName(BAD_CAST(str
.data()), nsptr
->prefix
,
1368 xmlNodeSetContent(node
, code
);
1371 xmlNodeSetContentLen(node
, BAD_CAST(str
.data()), str
.size());
1374 if (!obj
->o_get("faultstring").toString().empty()) {
1375 xmlNodePtr node
= xmlNewChild(param
, ns
, BAD_CAST("Reason"), NULL
);
1376 node
= master_to_xml(get_conversion(KindOfString
), obj
->o_get("faultstring"),
1377 SOAP_LITERAL
, node
);
1378 xmlNodeSetName(node
, BAD_CAST("Text"));
1381 detail_name
= SOAP_1_2_ENV_NS_PREFIX
":Detail";
1383 if (fault
&& fault
->details
.size() == 1) {
1389 if (!obj
->o_get("detail").isNull()) {
1390 detail
= obj
->o_get("detail");
1392 node
= xmlNewNode(NULL
, BAD_CAST(detail_name
));
1393 xmlAddChild(param
, node
);
1395 sparam
= fault
->details
[0];
1397 if (detail
.isObject() && sparam
->element
) {
1398 Variant prop
= detail
.toObject()->o_get(sparam
->element
->name
.c_str());
1399 if (!prop
.isNull()) {
1404 x
= serialize_parameter(sparam
, detail
, 1, NULL
, use
, node
);
1407 function
->binding
&&
1408 function
->binding
->bindingType
== BINDING_SOAP
&&
1409 function
->bindingAttributes
) {
1410 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
1411 if (fnb
->style
== SOAP_RPC
&& !sparam
->element
) {
1412 if (fault
->bindingAttributes
) {
1413 sdlSoapBindingFunctionFaultPtr fb
= fault
->bindingAttributes
;
1414 if (!fb
->ns
.empty()) {
1415 xmlNsPtr ns
= encode_add_ns(x
, fb
->ns
.c_str());
1420 if (sparam
->element
) {
1421 xmlNsPtr ns
= encode_add_ns(x
, sparam
->element
->namens
.c_str());
1422 xmlNodeSetName(x
, BAD_CAST(sparam
->element
->name
.c_str()));
1427 if (use
== SOAP_ENCODED
&& version
== SOAP_1_2
) {
1428 xmlSetNsProp(x
, envelope
->ns
, BAD_CAST("encodingStyle"),
1429 BAD_CAST(SOAP_1_2_ENC_NAMESPACE
));
1431 } else if (!obj
->o_get("detail").isNull()) {
1432 serialize_zval(obj
->o_get("detail"), sdlParamPtr(), detail_name
, use
, param
);
1436 if (!headers
.empty()) {
1437 head
= xmlNewChild(envelope
, ns
, BAD_CAST("Header"), NULL
);
1438 for (ArrayIter
iter(headers
); iter
; ++iter
) {
1439 soapHeader
*h
= iter
.second().toResource().getTyped
<soapHeader
>();
1440 if (!h
->retval
.isNull()) {
1442 int hdr_use
= SOAP_LITERAL
;
1443 Variant
&hdr_ret
= h
->retval
;
1444 const char *hdr_ns
= h
->hdr
? h
->hdr
->ns
.c_str() : NULL
;
1445 const char *hdr_name
= h
->function_name
.data();
1447 if (h
->retval
.isObject() &&
1448 h
->retval
.toObject().is
<c_SoapHeader
>()) {
1449 c_SoapHeader
*ht
= h
->retval
.toObject().getTyped
<c_SoapHeader
>();
1451 if (!ht
->m_namespace
.empty()) {
1452 key
+= ht
->m_namespace
.data();
1454 hdr_ns
= ht
->m_namespace
.data();
1456 if (!ht
->m_name
.empty()) {
1457 key
+= ht
->m_name
.data();
1458 hdr_name
= ht
->m_name
.data();
1460 if (function
&& function
->binding
&&
1461 function
->binding
->bindingType
== BINDING_SOAP
) {
1462 sdlSoapBindingFunctionPtr fnb
= function
->bindingAttributes
;
1463 sdlSoapBindingFunctionHeaderMap::iterator iter
=
1464 fnb
->output
.headers
.find(key
);
1465 if (iter
!= fnb
->output
.headers
.end()) {
1466 hdr_enc
= iter
->second
->encode
;
1467 hdr_use
= iter
->second
->use
;
1470 hdr_ret
= ht
->m_data
;
1474 if (serialize_response_call2(head
, h
->function
,
1475 h
->function_name
.data(), uri
, hdr_ret
,
1476 version
, 0) == SOAP_ENCODED
) {
1480 xmlNodePtr xmlHdr
= master_to_xml(hdr_enc
, hdr_ret
, hdr_use
, head
);
1482 xmlNodeSetName(xmlHdr
, BAD_CAST(hdr_name
));
1485 xmlNsPtr nsptr
= encode_add_ns(xmlHdr
,hdr_ns
);
1486 xmlSetNs(xmlHdr
, nsptr
);
1492 if (head
->children
== NULL
) {
1493 xmlUnlinkNode(head
);
1498 body
= xmlNewChild(envelope
, ns
, BAD_CAST("Body"), NULL
);
1500 if (serialize_response_call2(body
, function
.get(), function_name
, uri
, ret
,
1501 version
, 1) == SOAP_ENCODED
) {
1506 if (use
== SOAP_ENCODED
) {
1507 xmlNewNs(envelope
, BAD_CAST(XSD_NAMESPACE
), BAD_CAST(XSD_NS_PREFIX
));
1508 if (version
== SOAP_1_1
) {
1509 xmlNewNs(envelope
, BAD_CAST(SOAP_1_1_ENC_NAMESPACE
),
1510 BAD_CAST(SOAP_1_1_ENC_NS_PREFIX
));
1511 xmlSetNsProp(envelope
, envelope
->ns
, BAD_CAST("encodingStyle"),
1512 BAD_CAST(SOAP_1_1_ENC_NAMESPACE
));
1513 } else if (version
== SOAP_1_2
) {
1514 xmlNewNs(envelope
, BAD_CAST(SOAP_1_2_ENC_NAMESPACE
),
1515 BAD_CAST(SOAP_1_2_ENC_NS_PREFIX
));
1521 if (function
&& function
->responseName
.empty() &&
1522 body
->children
== NULL
&& head
== NULL
) {
1529 static void function_to_string(std::shared_ptr
<sdlFunction
> function
,
1532 sdlParamVec
&res
= function
->responseParameters
;
1534 if (res
.size() == 1) {
1536 if (param
->encode
&& !param
->encode
->details
.type_str
.empty()) {
1537 sb
.append(param
->encode
->details
.type_str
);
1540 sb
.append("UNKNOWN ");
1544 bool started
= false;
1545 for (unsigned int i
= 0; i
< res
.size(); i
++) {
1552 if (param
->encode
&& !param
->encode
->details
.type_str
.empty()) {
1553 sb
.append(param
->encode
->details
.type_str
);
1555 sb
.append("UNKNOWN");
1558 sb
.append(param
->paramName
);
1566 sb
.append(function
->functionName
);
1569 sdlParamVec
&req
= function
->requestParameters
;
1570 bool started
= false;
1571 for (unsigned int i
= 0; i
< req
.size(); i
++) {
1579 if (param
->encode
&& !param
->encode
->details
.type_str
.empty()) {
1580 sb
.append(param
->encode
->details
.type_str
);
1582 sb
.append("UNKNOWN");
1585 sb
.append(param
->paramName
);
1590 static void type_to_string(sdlType
*type
, StringBuffer
&buf
, int level
) {
1591 StringBuffer sbspaces
;
1592 for (int i
= 0; i
< level
;i
++) {
1593 sbspaces
.append(' ');
1595 String spaces
= sbspaces
.detach();
1598 switch (type
->kind
) {
1599 case XSD_TYPEKIND_SIMPLE
:
1601 buf
.append(type
->encode
->details
.type_str
);
1604 buf
.append("anyType ");
1606 buf
.append(type
->name
);
1608 case XSD_TYPEKIND_LIST
:
1609 buf
.append("list ");
1610 buf
.append(type
->name
);
1611 if (!type
->elements
.empty()) {
1613 buf
.append(type
->elements
[0]->name
);
1617 case XSD_TYPEKIND_UNION
:
1618 buf
.append("union ");
1619 buf
.append(type
->name
);
1620 if (!type
->elements
.empty()) {
1623 for (unsigned int i
= 0; i
< type
->elements
.size(); i
++) {
1628 buf
.append(type
->elements
[i
]->name
);
1633 case XSD_TYPEKIND_COMPLEX
:
1634 case XSD_TYPEKIND_RESTRICTION
:
1635 case XSD_TYPEKIND_EXTENSION
:
1637 (type
->encode
->details
.type
== KindOfArray
||
1638 type
->encode
->details
.type
== SOAP_ENC_ARRAY
)) {
1639 sdlAttributeMap::iterator iter
;
1640 sdlExtraAttributeMap::iterator iterExtra
;
1641 if (!type
->attributes
.empty() &&
1642 ((iter
= type
->attributes
.find(SOAP_1_1_ENC_NAMESPACE
":arrayType"))
1643 != type
->attributes
.end())) {
1644 if ((iterExtra
= iter
->second
->extraAttributes
.find
1645 (WSDL_NAMESPACE
":arrayType"))
1646 != iter
->second
->extraAttributes
.end()) {
1647 auto ext
= iterExtra
->second
;
1648 const char *end
= strchr(ext
->val
.c_str(), '[');
1651 len
= ext
->val
.size();
1653 len
= end
- ext
->val
.c_str();
1656 buf
.append("anyType");
1658 buf
.append(ext
->val
.c_str(), len
);
1661 buf
.append(type
->name
);
1667 sdlTypePtr elementType
;
1668 sdlAttributeMap::iterator iter
;
1669 sdlExtraAttributeMap::iterator iterExtra
;
1670 if (!type
->attributes
.empty() &&
1671 ((iter
= type
->attributes
.find(SOAP_1_2_ENC_NAMESPACE
":itemType"))
1672 != type
->attributes
.end())) {
1673 if ((iterExtra
= iter
->second
->extraAttributes
.find
1674 (WSDL_NAMESPACE
":itemType"))
1675 != iter
->second
->extraAttributes
.end()) {
1676 auto ext
= iterExtra
->second
;
1677 buf
.append(ext
->val
);
1680 } else if (type
->elements
.size() == 1 &&
1681 (elementType
= type
->elements
[0]) != NULL
&&
1682 elementType
->encode
&&
1683 !elementType
->encode
->details
.type_str
.empty()) {
1684 buf
.append(elementType
->encode
->details
.type_str
);
1687 buf
.append("anyType ");
1689 buf
.append(type
->name
);
1690 if (!type
->attributes
.empty() &&
1691 ((iter
= type
->attributes
.find(SOAP_1_2_ENC_NAMESPACE
":arraySize"))
1692 != type
->attributes
.end())) {
1693 if ((iterExtra
= iter
->second
->extraAttributes
.find
1694 (WSDL_NAMESPACE
":itemType"))
1695 != iter
->second
->extraAttributes
.end()) {
1696 auto ext
= iterExtra
->second
;
1698 buf
.append(ext
->val
);
1706 buf
.append("struct ");
1707 buf
.append(type
->name
);
1710 if ((type
->kind
== XSD_TYPEKIND_RESTRICTION
||
1711 type
->kind
== XSD_TYPEKIND_EXTENSION
) && type
->encode
) {
1712 encodePtr enc
= type
->encode
;
1713 while (enc
&& enc
->details
.sdl_type
&&
1714 enc
!= enc
->details
.sdl_type
->encode
&&
1715 enc
->details
.sdl_type
->kind
!= XSD_TYPEKIND_SIMPLE
&&
1716 enc
->details
.sdl_type
->kind
!= XSD_TYPEKIND_LIST
&&
1717 enc
->details
.sdl_type
->kind
!= XSD_TYPEKIND_UNION
) {
1718 enc
= enc
->details
.sdl_type
->encode
;
1723 buf
.append(type
->encode
->details
.type_str
);
1724 buf
.append(" _;\n");
1728 model_to_string(type
->model
, buf
, level
+1);
1730 if (!type
->attributes
.empty()) {
1731 for (sdlAttributeMap::iterator iter
= type
->attributes
.begin();
1732 iter
!= type
->attributes
.end(); ++iter
) {
1733 sdlAttributePtr attr
= iter
->second
;
1736 if (attr
->encode
&& !attr
->encode
->details
.type_str
.empty()) {
1737 buf
.append(attr
->encode
->details
.type_str
);
1740 buf
.append("UNKNOWN ");
1742 buf
.append(attr
->name
);
1755 static void model_to_string(sdlContentModelPtr model
, StringBuffer
&buf
,
1758 switch (model
->kind
) {
1759 case XSD_CONTENT_ELEMENT
:
1760 type_to_string(model
->u_element
, buf
, level
);
1763 case XSD_CONTENT_ANY
:
1764 for (i
= 0;i
< level
;i
++) {
1767 buf
.append("<anyXML> any;\n");
1769 case XSD_CONTENT_SEQUENCE
:
1770 case XSD_CONTENT_ALL
:
1771 case XSD_CONTENT_CHOICE
: {
1772 for (unsigned int i
= 0; i
< model
->u_content
.size(); i
++) {
1773 sdlContentModelPtr tmp
= model
->u_content
[i
];
1774 model_to_string(tmp
, buf
, level
);
1778 case XSD_CONTENT_GROUP
:
1779 model_to_string(model
->u_group
->model
, buf
, level
);
1785 ///////////////////////////////////////////////////////////////////////////////
1786 // soap fault functions
1789 s_HTTP_USER_AGENT("HTTP_USER_AGENT"),
1790 s__SERVER("_SERVER"),
1791 s_Shockwave_Flash("Shockwave Flash");
1793 static void send_soap_server_fault(
1794 std::shared_ptr
<sdlFunction
> function
,
1798 bool use_http_error_status
= true;
1799 if (php_global(s__SERVER
).toArray()[s_HTTP_USER_AGENT
].toString() ==
1800 s_Shockwave_Flash
) {
1801 use_http_error_status
= false;
1803 if (use_http_error_status
) {
1804 header_if_not_sent("HTTP/1.1 500 Internal Service Error");
1806 output_xml_header(SOAP_GLOBAL(soap_version
));
1809 if (hdr
) headers
.append(Resource(hdr
));
1810 xmlDocPtr doc_return
= serialize_response_call
1811 (function
, NULL
, NULL
, fault
, headers
, SOAP_GLOBAL(soap_version
));
1813 HHVM_FN(ob_end_clean
)(); // dump all buffered output
1815 xmlChar
*buf
; int size
;
1816 xmlDocDumpMemory(doc_return
, &buf
, &size
);
1818 g_context
->write(String((const char *)buf
, size
, CopyString
));
1821 xmlFreeDoc(doc_return
);
1824 static void send_soap_server_fault(
1825 std::shared_ptr
<sdlFunction
> function
,
1829 if (SOAP_GLOBAL(use_soap_error_handler
)) {
1830 send_soap_server_fault(
1831 std::shared_ptr
<sdlFunction
>(), create_soap_fault(e
), NULL
);
1833 throw create_soap_fault(e
); // assuming we are in "catch"
1837 static void throw_soap_server_fault(litstr code
, litstr fault
) {
1838 send_soap_server_fault(
1839 std::shared_ptr
<sdlFunction
>(), create_soap_fault(code
, fault
),
1841 throw ExitException(1);
1844 bool f_use_soap_error_handler(bool handler
/* = true */) {
1846 bool old
= SOAP_GLOBAL(use_soap_error_handler
);
1847 SOAP_GLOBAL(use_soap_error_handler
) = handler
;
1851 bool f_is_soap_fault(const Variant
& fault
) {
1852 return fault
.isObject() &&
1853 fault
.getObjectData()->instanceof(SystemLib::s_SoapFaultClass
);
1856 int64_t f__soap_active_version() {
1858 return SOAP_GLOBAL(soap_version
);
1861 ///////////////////////////////////////////////////////////////////////////////
1864 c_SoapServer::c_SoapServer(Class
* cb
) :
1866 m_type(SOAP_FUNCTIONS
),
1867 m_version(SOAP_1_1
),
1875 c_SoapServer::~c_SoapServer() {
1879 s_soap_version("soap_version"),
1882 s_encoding("encoding"),
1883 s_classmap("classmap"),
1884 s_typemap("typemap"),
1885 s_features("features"),
1886 s_cache_wsdl("cache_wsdl"),
1887 s_send_errors("send_errors"),
1888 s_location("location"),
1891 s_stream_context("stream_context"),
1893 s_password("password"),
1894 s_authentication("authentication"),
1895 s_proxy_host("proxy_host"),
1896 s_proxy_port("proxy_port"),
1897 s_proxy_login("proxy_login"),
1898 s_proxy_password("proxy_password"),
1900 s_exceptions("exceptions"),
1901 s_compression("compression"),
1902 s_connection_timeout("connection_timeout"),
1903 s_user_agent("user_agent"),
1904 s_soapaction("soapaction");
1906 void c_SoapServer::t___construct(const Variant
& wsdl
,
1907 const Array
& options
/* = null_array */) {
1909 SoapServerScope
ss(this);
1913 if (!wsdl
.isString() && !wsdl
.isNull()) {
1914 throw SoapException("Invalid parameters");
1919 int64_t cache_wsdl
= SOAP_GLOBAL(cache
);
1921 int version
= SOAP_1_1
;
1923 if (!options
.empty()) {
1924 if (options
[s_soap_version
].isInteger()) {
1925 int64_t tmp
= options
[s_soap_version
].toInt64();
1926 if (tmp
== SOAP_1_1
|| tmp
== SOAP_1_2
) {
1931 if (options
[s_uri
].isString()) {
1932 m_uri
= options
[s_uri
].toString();
1933 } else if (wsdl
.isNull()) {
1934 throw SoapException("'uri' option is required in nonWSDL mode");
1937 if (options
[s_actor
].isString()) {
1938 m_actor
= options
[s_actor
].toString();
1941 if (options
[s_encoding
].isString()) {
1942 String tmp
= options
[s_encoding
].toString();
1943 m_encoding
= xmlFindCharEncodingHandler(tmp
.data());
1944 if (m_encoding
== NULL
) {
1945 throw SoapException("Invalid 'encoding' option - '%s'", tmp
.data());
1947 s_soap_data
->register_encoding(m_encoding
);
1950 if (options
[s_classmap
].isArray()) {
1951 m_classmap
= options
[s_classmap
].toArray();
1954 if (options
[s_typemap
].isArray()) {
1955 typemap_ht
= options
[s_typemap
].toArray();
1958 if (options
[s_features
].isInteger()) {
1959 m_features
= options
[s_features
].toInt64();
1962 if (options
[s_cache_wsdl
].isInteger()) {
1963 cache_wsdl
= options
[s_cache_wsdl
].toInt64();
1966 if (options
[s_send_errors
].isInteger() ||
1967 options
[s_send_errors
].is(KindOfBoolean
)) {
1968 m_send_errors
= options
[s_send_errors
].toInt64();
1971 } else if (wsdl
.isNull()) {
1972 throw SoapException("'uri' option is required in nonWSDL mode");
1975 m_version
= version
;
1976 m_type
= SOAP_FUNCTIONS
;
1977 m_soap_functions
.functions_all
= false;
1979 if (!wsdl
.isNull()) {
1980 m_sdl
= s_soap_data
->get_sdl(wsdl
.toString().data(), cache_wsdl
);
1981 if (m_uri
.isNull()) {
1982 if (!m_sdl
->target_ns
.empty()) {
1983 m_uri
= String(m_sdl
->target_ns
);
1986 m_uri
= String("http://unknown-uri/");
1991 if (!typemap_ht
.empty()) {
1992 m_typemap
= soap_create_typemap(m_sdl
, typemap_ht
);
1995 } catch (Exception
&e
) {
1996 throw create_soap_fault(e
);
2000 void c_SoapServer::t_setclass(int _argc
, const String
& name
,
2001 const Array
& _argv
/* = null_array */) {
2002 SoapServerScope
ss(this);
2003 if (HHVM_FN(class_exists
)(name
, true)) {
2004 m_type
= SOAP_CLASS
;
2005 m_soap_class
.name
= name
;
2006 m_soap_class
.argv
= _argv
;
2007 m_soap_class
.persistance
= SOAP_PERSISTENCE_REQUEST
;
2009 raise_warning("Tried to set a non existant class (%s)", name
.data());
2013 void c_SoapServer::t_setobject(const Object
& obj
) {
2014 SoapServerScope
ss(this);
2015 m_type
= SOAP_OBJECT
;
2016 m_soap_object
= obj
;
2019 void c_SoapServer::t_addfunction(const Variant
& func
) {
2020 SoapServerScope
ss(this);
2023 if (func
.isString()) {
2025 } else if (func
.isArray()) {
2026 funcs
= func
.toArray();
2027 } else if (func
.isInteger()) {
2028 if (func
.toInt64() == SOAP_FUNCTIONS_ALL
) {
2029 m_soap_functions
.ft
.clear();
2030 m_soap_functions
.ftOriginal
.clear();
2031 m_soap_functions
.functions_all
= true;
2033 raise_warning("Invalid value passed");
2038 if (m_type
== SOAP_FUNCTIONS
) {
2039 for (ArrayIter
iter(funcs
); iter
; ++iter
) {
2040 if (!iter
.second().isString()) {
2041 raise_warning("Tried to add a function that isn't a string");
2044 String function_name
= iter
.second().toString();
2045 if (!f_function_exists(function_name
)) {
2046 raise_warning("Tried to add a non existant function '%s'",
2047 function_name
.data());
2050 m_soap_functions
.ft
.set(HHVM_FN(strtolower
)(function_name
), 1);
2051 m_soap_functions
.ftOriginal
.set(function_name
, 1);
2056 Variant
c_SoapServer::t_getfunctions() {
2057 SoapServerScope
ss(this);
2060 if (m_type
== SOAP_OBJECT
) {
2061 class_name
= m_soap_object
->o_getClassName();
2062 } else if (m_type
== SOAP_CLASS
) {
2063 class_name
= m_soap_class
.name
;
2064 } else if (m_soap_functions
.functions_all
) {
2065 return Unit::getSystemFunctions() + Unit::getUserFunctions();
2066 } else if (!m_soap_functions
.ft
.empty()) {
2067 return array_keys_helper(m_soap_functions
.ftOriginal
);
2070 Class
* cls
= Unit::lookupClass(class_name
.get());
2071 auto ret
= Array::attach(MixedArray::MakeReserve(cls
->numMethods()));
2072 Class::getMethodNames(cls
, nullptr, ret
);
2073 return f_array_values(ret
);
2076 static bool valid_function(c_SoapServer
*server
, Object
&soap_obj
,
2077 const String
& fn_name
) {
2078 HPHP::Class
* cls
= nullptr;
2079 if (server
->m_type
== SOAP_OBJECT
) {
2080 cls
= server
->m_soap_object
->getVMClass();
2081 } else if (server
->m_type
== SOAP_CLASS
) {
2082 cls
= soap_obj
->getVMClass();
2083 } else if (server
->m_soap_functions
.functions_all
) {
2084 return f_function_exists(fn_name
);
2085 } else if (!server
->m_soap_functions
.ft
.empty()) {
2086 return server
->m_soap_functions
.ft
.exists(HHVM_FN(strtolower
)(fn_name
));
2088 HPHP::Func
* f
= cls
? cls
->lookupMethod(fn_name
.get()) : nullptr;
2089 return (f
&& f
->isPublic()) || soap_obj
->getAttribute(ObjectData::HasCall
);
2093 s_HTTP_CONTENT_ENCODING("HTTP_CONTENT_ENCODING"),
2096 s_deflate("deflate");
2098 void c_SoapServer::t_handle(const String
& request
/* = null_string */) {
2100 SoapServerScope
ss(this);
2102 SOAP_GLOBAL(soap_version
) = m_version
;
2105 Transport
*transport
= g_context
->getTransport();
2106 if (transport
&& transport
->getMethod() == Transport::Method::GET
&&
2107 transport
->getCommand() == "wsdl") {
2109 throw_soap_server_fault("Server", "WSDL generation is not supported");
2112 header_if_not_sent("Content-Type: text/xml; charset=utf-8");
2113 Variant ret
= f_readfile(m_sdl
->source
.c_str());
2114 if (same(ret
, false)) {
2115 throw_soap_server_fault("Server", "Couldn't find WSDL");
2120 if (!HHVM_FN(ob_start
)()) {
2121 throw SoapException("ob_start failed");
2124 // 1. process request
2126 if (!request
.isNull()) {
2130 const char *data
= NULL
;
2132 data
= (const char *)transport
->getPostData(size
);
2134 if (!data
|| !*data
|| !size
) {
2137 req
= String(data
, size
, CopyString
);
2139 if (php_global(s__SERVER
).toArray().exists(s_HTTP_CONTENT_ENCODING
)) {
2140 String encoding
= php_global(s__SERVER
)
2141 .toArray()[s_HTTP_CONTENT_ENCODING
].toString();
2143 if (encoding
== s_gzip
|| encoding
== s_xgzip
) {
2144 ret
= HHVM_FN(gzinflate
)(String(data
, size
, CopyString
));
2145 } else if (encoding
== s_deflate
) {
2146 ret
= HHVM_FN(gzuncompress
)(String(data
, size
, CopyString
));
2148 raise_warning("Request is encoded with unknown compression '%s'",
2152 if (!ret
.isString()) {
2153 raise_warning("Can't uncompress compressed request");
2156 req
= ret
.toString();
2159 xmlDocPtr doc_request
= soap_xmlParseMemory(req
.data(), req
.size());
2160 if (doc_request
== NULL
) {
2161 throw_soap_server_fault("Client", "Bad Request");
2163 if (xmlGetIntSubset(doc_request
) != NULL
) {
2164 xmlNodePtr env
= get_node(doc_request
->children
,"Envelope");
2165 if (env
&& env
->ns
) {
2166 if (strcmp((char*)env
->ns
->href
, SOAP_1_1_ENV_NAMESPACE
) == 0) {
2167 SOAP_GLOBAL(soap_version
) = SOAP_1_1
;
2168 } else if (strcmp((char*)env
->ns
->href
,SOAP_1_2_ENV_NAMESPACE
) == 0) {
2169 SOAP_GLOBAL(soap_version
) = SOAP_1_2
;
2172 xmlFreeDoc(doc_request
);
2173 throw_soap_server_fault("Server", "DTD are not supported by SOAP");
2176 // 2. find out which PHP function to call with what params
2177 SoapServiceScope
sss(this);
2178 String function_name
;
2180 int soap_version
= 0;
2181 std::shared_ptr
<sdlFunction
> function
;
2183 function
= deserialize_function_call(m_sdl
, doc_request
, m_actor
.c_str(),
2184 function_name
, params
, soap_version
,
2186 } catch (Exception
&e
) {
2187 xmlFreeDoc(doc_request
);
2188 send_soap_server_fault(function
, e
, NULL
);
2191 xmlFreeDoc(doc_request
);
2193 // 3. we may need an object
2195 if (m_type
== SOAP_OBJECT
) {
2196 soap_obj
= m_soap_object
;
2197 } else if (m_type
== SOAP_CLASS
) {
2199 soap_obj
= create_object(m_soap_class
.name
,
2201 } catch (Exception
&e
) {
2202 send_soap_server_fault(function
, e
, NULL
);
2207 // 4. process soap headers
2208 for (ArrayIter
iter(m_soap_headers
); iter
; ++iter
) {
2209 soapHeader
*h
= iter
.second().toResource().getTyped
<soapHeader
>();
2210 if (m_sdl
&& !h
->function
&& !h
->hdr
) {
2211 if (h
->mustUnderstand
) {
2212 throw_soap_server_fault("MustUnderstand","Header not understood");
2217 String fn_name
= h
->function_name
;
2218 if (valid_function(this, soap_obj
, fn_name
)) {
2220 if (m_type
== SOAP_CLASS
|| m_type
== SOAP_OBJECT
) {
2221 h
->retval
= vm_call_user_func
2222 (make_packed_array(soap_obj
, fn_name
), h
->parameters
);
2224 h
->retval
= vm_call_user_func(fn_name
, h
->parameters
);
2226 } catch (Exception
&e
) {
2227 send_soap_server_fault(function
, e
, h
);
2230 if (h
->retval
.isObject() &&
2231 h
->retval
.getObjectData()->instanceof(SystemLib::s_SoapFaultClass
)) {
2232 send_soap_server_fault(function
, h
->retval
, h
);
2235 } else if (h
->mustUnderstand
) {
2236 throw_soap_server_fault("MustUnderstand","Header not understood");
2241 String fn_name
= function_name
;
2243 if (valid_function(this, soap_obj
, fn_name
)) {
2245 if (m_type
== SOAP_CLASS
|| m_type
== SOAP_OBJECT
) {
2246 retval
= vm_call_user_func
2247 (make_packed_array(soap_obj
, fn_name
), params
);
2249 retval
= vm_call_user_func(fn_name
, params
);
2251 } catch (Exception
&e
) {
2252 send_soap_server_fault(function
, e
, NULL
);
2255 if (retval
.isObject() &&
2256 retval
.toObject()->instanceof(SystemLib::s_SoapFaultClass
)) {
2257 send_soap_server_fault(function
, retval
, NULL
);
2261 throw SoapException("Function '%s' doesn't exist", fn_name
.data());
2264 // 6. serialize response
2265 String response_name
;
2266 if (function
&& !function
->responseName
.empty()) {
2267 response_name
= function
->responseName
;
2269 response_name
= function_name
+ "Response";
2271 xmlDocPtr doc_return
= NULL
;
2273 doc_return
= serialize_response_call(function
, response_name
.data(),
2274 m_uri
.c_str(), retval
, m_soap_headers
,
2276 } catch (Object
&e
) {
2277 if (e
->o_instanceof("SoapFault")) {
2278 send_soap_server_fault(function
, e
, nullptr);
2282 } catch (Exception
&e
) {
2283 send_soap_server_fault(function
, e
, NULL
);
2287 // 7. throw away all buffered output so far, so we can send back a clean
2289 HHVM_FN(ob_end_clean
)();
2292 if (doc_return
== NULL
) {
2293 header_if_not_sent("HTTP/1.1 202 Accepted");
2294 header_if_not_sent("Content-Length: 0");
2299 xmlChar
*buf
; int size
;
2300 xmlDocDumpMemory(doc_return
, &buf
, &size
);
2301 xmlFreeDoc(doc_return
);
2302 if (buf
== NULL
|| size
== 0) {
2303 if (buf
) xmlFree(buf
);
2304 throw SoapException("Dump memory failed");
2306 output_xml_header(soap_version
);
2308 g_context
->write(String((char*)buf
, size
, CopyString
));
2313 void c_SoapServer::t_setpersistence(int64_t mode
) {
2314 SoapServerScope
ss(this);
2315 if (m_type
== SOAP_CLASS
) {
2316 if (mode
== SOAP_PERSISTENCE_SESSION
|| mode
== SOAP_PERSISTENCE_REQUEST
) {
2317 m_soap_class
.persistance
= mode
;
2319 raise_warning("Tried to set persistence with bogus value (%" PRId64
")",
2323 raise_warning("Tried to set persistence when you are using you "
2324 "SOAP SERVER in function mode, no persistence needed");
2328 void c_SoapServer::t_fault(const Variant
& code
, const String
& fault
,
2329 const String
& actor
/* = null_string */,
2330 const Variant
& detail
/* = null */,
2331 const String
& name
/* = null_string */) {
2332 SoapServerScope
ss(this);
2333 Object
obj(SystemLib::AllocSoapFaultObject(code
, fault
, actor
, detail
, name
));
2334 send_soap_server_fault(std::shared_ptr
<sdlFunction
>(), obj
, NULL
);
2337 void c_SoapServer::t_addsoapheader(const Object
& fault
) {
2338 SoapServerScope
ss(this);
2339 soapHeader
*p
= NEWOBJ(soapHeader
)();
2342 p
->mustUnderstand
= false;
2345 m_soap_headers
.append(obj
);
2348 ///////////////////////////////////////////////////////////////////////////////
2351 c_SoapClient::c_SoapClient(Class
* cb
) :
2352 ExtObjectDataFlags
<ObjectData::HasCall
>(cb
),
2353 m_soap_version(SOAP_1_1
),
2359 m_use(SOAP_LITERAL
),
2360 m_authentication(SOAP_AUTHENTICATION_BASIC
),
2362 m_connection_timeout(0),
2363 m_max_redirect(HttpClient::defaultMaxRedirect
),
2365 m_compression(false),
2370 c_SoapClient::~c_SoapClient() {
2373 void c_SoapClient::t___construct(const Variant
& wsdl
,
2374 const Array
& options
/* = null_array */) {
2376 SoapClientScope
ss(this);
2380 if (!wsdl
.isString() && !wsdl
.isNull()) {
2381 throw SoapException("wsdl must be string or null");
2384 int64_t cache_wsdl
= SOAP_GLOBAL(cache
);
2385 if (!options
.empty()) {
2386 m_location
= options
[s_location
];
2388 if (wsdl
.isNull()) {
2389 /* Fetching non-WSDL mode options */
2390 m_uri
= options
[s_uri
];
2391 m_style
= options
[s_style
].toInt32(); // SOAP_RPC || SOAP_DOCUMENT
2392 m_use
= options
[s_use
].toInt32(); // SOAP_LITERAL || SOAP_ENCODED
2394 if (m_uri
.empty()) {
2395 throw SoapException("'uri' option is required in nonWSDL mode");
2397 if (m_location
.empty()) {
2398 throw SoapException("'location' option is required in nonWSDL mode");
2402 if (options
.exists(s_stream_context
)) {
2403 StreamContext
*sc
= NULL
;
2404 if (options
[s_stream_context
].isResource()) {
2405 sc
= options
[s_stream_context
].toResource()
2406 .getTyped
<StreamContext
>();
2409 throw SoapException("'stream_context' is not a StreamContext");
2411 m_stream_context_options
= sc
->getOptions();
2414 if (options
.exists(s_soap_version
)) {
2415 m_soap_version
= options
[s_soap_version
].toInt32();
2418 m_login
= options
[s_login
].toString();
2419 m_password
= options
[s_password
].toString();
2420 m_authentication
= options
[s_authentication
].toInt32();
2422 m_proxy_host
= options
[s_proxy_host
].toString();
2423 m_proxy_port
= options
[s_proxy_port
].toInt32();
2424 m_proxy_login
= options
[s_proxy_login
].toString();
2425 m_proxy_password
= options
[s_proxy_password
].toString();
2427 m_trace
= options
[s_trace
].toBoolean();
2428 if (options
.exists(s_exceptions
)) {
2429 m_exceptions
= options
[s_exceptions
].toBoolean();
2431 if (options
.exists(s_compression
)) {
2432 m_compression
= options
[s_compression
].toBoolean();
2435 String encoding
= options
[s_encoding
].toString();
2436 if (!encoding
.empty()) {
2437 m_encoding
= xmlFindCharEncodingHandler(encoding
.data());
2438 if (m_encoding
== NULL
) {
2439 throw SoapException("Invalid 'encoding' option - '%s'",
2442 s_soap_data
->register_encoding(m_encoding
);
2444 m_classmap
= options
[s_classmap
].toArray();
2445 m_features
= options
[s_features
].toInt32();
2446 m_connection_timeout
= options
[s_connection_timeout
].toInt64();
2447 m_user_agent
= options
[s_user_agent
].toString();
2449 if (options
.exists(s_cache_wsdl
)) {
2450 cache_wsdl
= options
[s_cache_wsdl
].toInt64();
2453 } else if (wsdl
.isNull()) {
2454 throw SoapException("'location' and 'uri' options are required in "
2458 if (!wsdl
.isNull()) {
2459 int old_soap_version
= SOAP_GLOBAL(soap_version
);
2460 SOAP_GLOBAL(soap_version
) = m_soap_version
;
2461 String swsdl
= wsdl
.toString();
2462 if (swsdl
.find("http://") == 0 || swsdl
.find("https://") == 0) {
2463 HttpClient
http(m_connection_timeout
, m_max_redirect
, m_use11
, true);
2464 if (!m_proxy_host
.empty() && m_proxy_port
) {
2465 http
.proxy(m_proxy_host
.data(), m_proxy_port
, m_proxy_login
.data(),
2466 m_proxy_password
.data());
2468 if (!m_login
.empty()) {
2469 http
.auth(m_login
.data(), m_password
.data(), !m_digest
);
2471 http
.setStreamContextOptions(m_stream_context_options
);
2472 m_sdl
= s_soap_data
->get_sdl(swsdl
.data(), cache_wsdl
, &http
);
2474 m_sdl
= s_soap_data
->get_sdl(swsdl
.data(), cache_wsdl
);
2476 SOAP_GLOBAL(soap_version
) = old_soap_version
;
2479 Variant v
= options
[s_typemap
];
2481 Array arr
= v
.toArray();
2483 m_typemap
= soap_create_typemap(m_sdl
, arr
);
2487 } catch (Exception
&e
) {
2488 throw create_soap_fault(e
);
2492 Variant
c_SoapClient::t___call(Variant name
, Variant args
) {
2493 return t___soapcall(name
.toString(), args
.toArray());
2496 Variant
c_SoapClient::t___soapcall(const String
& name
, const Array
& args
,
2497 const Array
& options
/* = null_array */,
2498 const Variant
& input_headers
/* = null_variant */,
2499 VRefParam output_headers_ref
/* = null */) {
2500 SoapClientScope
ss(this);
2502 String location
, soap_action
, uri
;
2503 if (!options
.isNull()) {
2504 if (options
[s_location
].isString()) {
2505 location
= options
[s_location
].toString();
2506 if (location
.isNull()) {
2507 location
= m_location
;
2510 if (options
[s_soapaction
].isString()) {
2511 soap_action
= options
[s_soapaction
].toString();
2513 if (options
[s_uri
].isString()) {
2514 uri
= options
[s_uri
].toString();
2518 Array soap_headers
= Array::Create();
2519 if (input_headers
.isNull()) {
2520 } else if (input_headers
.isArray()) {
2521 Array arr
= input_headers
.toArray();
2522 verify_soap_headers_array(arr
);
2523 soap_headers
= input_headers
;
2524 } else if (input_headers
.isObject() &&
2525 input_headers
.toObject().is
<c_SoapHeader
>()) {
2526 soap_headers
= make_packed_array(input_headers
);
2528 raise_warning("Invalid SOAP header");
2531 if (!m_default_headers
.isNull()) {
2532 soap_headers
.merge(m_default_headers
.toArray());
2535 Array output_headers
;
2537 output_headers_ref
= output_headers
;
2541 m_last_request
.unset();
2542 m_last_response
.unset();
2545 if (location
.empty()) {
2546 location
= m_location
;
2549 m_soap_fault
.unset();
2551 SoapServiceScope
sss(this);
2552 Variant return_value
;
2554 xmlDocPtr request
= NULL
;
2556 auto fn
= get_function(m_sdl
, name
.data());
2558 sdlBindingPtr binding
= fn
->binding
;
2559 bool one_way
= false;
2560 if (fn
->responseName
.empty() && fn
->responseParameters
.empty() &&
2561 soap_headers
.empty()) {
2564 if (location
.empty()) {
2565 location
= binding
->location
;
2570 if (binding
->bindingType
== BINDING_SOAP
) {
2571 sdlSoapBindingFunctionPtr fnb
= fn
->bindingAttributes
;
2572 request
= serialize_function_call
2573 (this, fn
, NULL
, fnb
->input
.ns
.c_str(), args
, soap_headers
);
2574 ret
= do_request(this, request
, location
.data(),
2575 fnb
->soapAction
.c_str(), m_soap_version
, one_way
,
2578 request
= serialize_function_call
2579 (this, fn
, NULL
, m_sdl
->target_ns
.c_str(), args
, soap_headers
);
2580 ret
= do_request(this, request
, location
.data(), NULL
,
2581 m_soap_version
, one_way
, response
);
2583 } catch (Exception
&e
) {
2584 xmlFreeDoc(request
);
2585 throw create_soap_fault(e
);
2587 xmlFreeDoc(request
);
2589 if (ret
&& response
.isString()) {
2591 String sresponse
= response
.toString();
2592 ret
= parse_packet_soap(this, sresponse
.data(), sresponse
.size(),
2593 fn
, NULL
, return_value
, output_headers
);
2598 error
.append("Function (\"");
2599 error
.append(name
.data());
2600 error
.append("\") is not a valid method for this service");
2601 m_soap_fault
= create_soap_fault("Client", error
.detach());
2605 if (m_uri
.empty()) {
2607 create_soap_fault("Client", "Error finding \"uri\" property");
2608 } else if (location
.empty()) {
2610 create_soap_fault("Client", "Error could not find \"location\" "
2613 request
= serialize_function_call
2614 (this, std::shared_ptr
<sdlFunction
>(), name
.data(), m_uri
.data(), args
,
2616 if (soap_action
.empty()) {
2617 action
+= m_uri
.data();
2619 action
+= name
.data();
2621 action
+= (std::string
) soap_action
;
2625 ret
= do_request(this, request
, location
.c_str(), action
.c_str(),
2626 m_soap_version
, 0, response
);
2627 } catch (Exception
&e
) {
2628 xmlFreeDoc(request
);
2629 throw create_soap_fault(e
);
2631 xmlFreeDoc(request
);
2632 if (ret
&& response
.isString()) {
2634 String sresponse
= response
.toString();
2635 ret
= parse_packet_soap(this, sresponse
.data(), sresponse
.size(),
2636 std::shared_ptr
<sdlFunction
>(),
2645 if (!ret
&& m_soap_fault
.isNull()) {
2646 m_soap_fault
= create_soap_fault("Client", "Unknown Error");
2648 if (!m_soap_fault
.isNull()) {
2649 throw m_soap_fault
.toObject();
2651 return return_value
;
2654 Variant
c_SoapClient::t___getlastrequest() {
2655 return m_last_request
;
2658 Variant
c_SoapClient::t___getlastresponse() {
2659 return m_last_response
;
2662 Variant
c_SoapClient::t___getlastrequestheaders() {
2663 return m_last_request_headers
;
2666 Variant
c_SoapClient::t___getlastresponseheaders() {
2667 return m_last_response_headers
;
2670 Variant
c_SoapClient::t___getfunctions() {
2671 SoapClientScope
ss(this);
2674 Array ret
= Array::Create();
2675 for (auto& func
: m_sdl
->functionsOrder
) {
2677 function_to_string(m_sdl
->functions
[func
], sb
);
2678 ret
.append(sb
.detach());
2685 Variant
c_SoapClient::t___gettypes() {
2686 SoapClientScope
ss(this);
2689 Array ret
= Array::Create();
2690 for (unsigned int i
= 0; i
< m_sdl
->types
.size(); i
++) {
2692 type_to_string(m_sdl
->types
[i
].get(), sb
, 0);
2693 ret
.append(sb
.detach());
2700 Variant
c_SoapClient::t___dorequest(const String
& buf
, const String
& location
, const String
& action
,
2701 int64_t version
, bool oneway
/* = false */) {
2702 if (location
.empty()) {
2704 Object(SystemLib::AllocSoapFaultObject("HTTP", "Unable to parse URL"));
2709 SoapClientScope
ss(this);
2716 if (m_compression
> 0) {
2717 if (m_compression
& SOAP_COMPRESSION_ACCEPT
) {
2718 headers
["Accept-Encoding"].push_back("gzip, deflate");
2720 int level
= m_compression
& 0x0f;
2721 if (level
> 9) level
= 9;
2724 if (m_compression
& SOAP_COMPRESSION_DEFLATE
) {
2725 ret
= HHVM_FN(gzcompress
)(buffer
, level
);
2726 headers
["Content-Encoding"].push_back("deflate");
2728 ret
= HHVM_FN(gzencode
)(buffer
, level
);
2729 headers
["Content-Encoding"].push_back("gzip");
2731 if (!ret
.isString()) return init_null();
2732 buffer
= ret
.toString();
2736 // prepare more headers
2737 if (!m_user_agent
.empty()) {
2738 headers
["User-Agent"].push_back(m_user_agent
.data());
2741 if (version
== SOAP_1_2
) {
2742 contentType
= "application/soap+xml; charset=utf-8";
2743 contentType
+= "; action=\"";
2744 contentType
+= action
.data();
2745 contentType
+= "\"";
2746 headers
["Content-Type"].push_back(contentType
);
2748 contentType
= "text/xml; charset=utf-8";
2749 headers
["Content-Type"].push_back(contentType
);
2750 headers
["SOAPAction"].push_back(string("\"") + action
.data() + "\"");
2754 HttpClient
http(m_connection_timeout
, m_max_redirect
, m_use11
, true);
2755 if (!m_proxy_host
.empty() && m_proxy_port
) {
2756 http
.proxy(m_proxy_host
.data(), m_proxy_port
, m_proxy_login
.data(),
2757 m_proxy_password
.data());
2759 if (!m_login
.empty()) {
2760 http
.auth(m_login
.data(), m_password
.data(), !m_digest
);
2762 http
.setStreamContextOptions(m_stream_context_options
);
2763 StringBuffer response
;
2764 int code
= http
.post(location
.data(), buffer
.data(), buffer
.size(), response
,
2767 String msg
= "Failed Sending HTTP Soap request";
2768 if (!http
.getLastError().empty()) {
2769 msg
+= ": " + http
.getLastError();
2771 m_soap_fault
= Object(SystemLib::AllocSoapFaultObject(
2775 if ((code
>= 400) &&
2776 (m_exceptions
|| response
.empty())) {
2777 String msg
= response
.detach();
2779 msg
= HttpProtocol::GetReasonString(code
);
2781 m_soap_fault
= Object(SystemLib::AllocSoapFaultObject("HTTP", msg
));
2786 if (SOAP_GLOBAL(features
) & SOAP_WAIT_ONE_WAY_CALLS
) {
2790 return empty_string_variant();
2792 return response
.detach();
2795 Variant
c_SoapClient::t___setcookie(const String
& name
,
2796 const String
& value
/* = null_string */) {
2797 if (!value
.isNull()) {
2798 m_cookies
.set(name
, make_packed_array(value
));
2800 const Variant
* t
= o_realProp("_cookies", RealPropUnchecked
);
2801 if (t
&& t
->isInitialized()) {
2802 m_cookies
.remove(name
);
2808 Variant
c_SoapClient::t___setlocation(const String
& new_location
/* = null_string */) {
2809 Variant ret
= m_location
;
2810 m_location
= new_location
;
2814 bool c_SoapClient::t___setsoapheaders(const Variant
& headers
/* = null_variant */) {
2815 if (headers
.isNull()) {
2816 m_default_headers
.unset();
2817 } else if (headers
.isArray()) {
2818 Array arr
= headers
.toArray();
2819 verify_soap_headers_array(arr
);
2820 m_default_headers
= arr
;
2821 } else if (headers
.isObject() && headers
.toObject().is
<c_SoapHeader
>()) {
2822 m_default_headers
= make_packed_array(headers
);
2824 raise_warning("Invalid SOAP header");
2829 ///////////////////////////////////////////////////////////////////////////////
2832 c_SoapVar::c_SoapVar(Class
* cb
) : ExtObjectData(cb
) {
2835 c_SoapVar::~c_SoapVar() {
2838 void c_SoapVar::t___construct(const Variant
& data
, const Variant
& type
,
2839 const String
& type_name
/* = null_string */,
2840 const String
& type_namespace
/* = null_string */,
2841 const String
& node_name
/* = null_string */,
2842 const String
& node_namespace
/* = null_string */) {
2844 if (type
.isNull()) {
2845 m_type
= UNKNOWN_TYPE
;
2847 std::map
<int, encodePtr
> &defEncIndex
= SOAP_GLOBAL(defEncIndex
);
2848 int64_t ntype
= type
.toInt64();
2849 if (defEncIndex
.find(ntype
) != defEncIndex
.end()) {
2852 raise_warning("Invalid type ID");
2857 if (data
.toBoolean()) m_value
= data
;
2858 if (!type_name
.empty()) m_stype
= type_name
;
2859 if (!type_namespace
.empty()) m_ns
= type_namespace
;
2860 if (!node_name
.empty()) m_name
= node_name
;
2861 if (!node_namespace
.empty()) m_namens
= node_namespace
;
2864 ///////////////////////////////////////////////////////////////////////////////
2867 c_SoapParam::c_SoapParam(Class
* cb
) : ExtObjectData(cb
) {
2870 c_SoapParam::~c_SoapParam() {
2873 void c_SoapParam::t___construct(const Variant
& data
, const String
& name
) {
2875 raise_warning("Invalid parameter name");
2882 ///////////////////////////////////////////////////////////////////////////////
2885 c_SoapHeader::c_SoapHeader(Class
* cb
) :
2889 c_SoapHeader::~c_SoapHeader() {
2892 static const StaticString
2893 s_namespace("namespace"),
2896 s_mustUnderstand("mustUnderstand");
2898 void c_SoapHeader::t___construct(const String
& ns
, const String
& name
,
2899 const Variant
& data
/* = null */,
2900 bool mustunderstand
/* = false */,
2901 const Variant
& actor
/* = null */) {
2903 raise_warning("Invalid namespace");
2907 raise_warning("Invalid header name");
2914 m_mustUnderstand
= mustunderstand
;
2916 o_set(s_namespace
, ns
);
2917 o_set(s_name
, name
);
2918 o_set(s_data
, data
);
2919 o_set(s_mustUnderstand
, mustunderstand
);
2921 if (actor
.isInteger() &&
2922 (actor
.toInt64() == SOAP_ACTOR_NEXT
||
2923 actor
.toInt64() == SOAP_ACTOR_NONE
||
2924 actor
.toInt64() == SOAP_ACTOR_UNLIMATERECEIVER
)) {
2925 m_actor
= actor
.toInt64();
2926 } else if (actor
.isString() && !actor
.toString().empty()) {
2927 m_actor
= actor
.toString();
2928 } else if (!actor
.isNull()) {
2929 raise_warning("Invalid actor");
2933 ///////////////////////////////////////////////////////////////////////////////