Convert string to HNI
[hiphop-php.git] / hphp / runtime / ext / ext_soap.cpp
blob553bbf8cef325e062aceb25b30ffdc2f361fc6d1
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
20 #include <map>
21 #include <memory>
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"
43 namespace HPHP {
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
52 using std::string;
54 class SoapScope {
55 public:
56 SoapScope() {
57 USE_SOAP_GLOBAL;
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;
66 ~SoapScope() {
67 USE_SOAP_GLOBAL;
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;
74 private:
75 bool m_old_handler;
76 const char *m_old_error_code;
77 Object m_old_error_object;
78 int m_old_soap_version;
81 class SoapServerScope : public SoapScope {
82 public:
83 explicit SoapServerScope(c_SoapServer *server) {
84 USE_SOAP_GLOBAL;
85 SOAP_GLOBAL(error_code) = "Server";
86 SOAP_GLOBAL(error_object) = Object(server);
90 class SoapClientScope : public SoapScope {
91 public:
92 explicit SoapClientScope(c_SoapClient *client) {
93 USE_SOAP_GLOBAL;
94 SOAP_GLOBAL(error_code) = "Client";
95 SOAP_GLOBAL(error_object) = Object(client);
99 class SoapServiceScope {
100 public:
101 explicit SoapServiceScope(c_SoapServer *server) {
102 save();
103 USE_SOAP_GLOBAL;
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) {
113 save();
114 USE_SOAP_GLOBAL;
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() {
124 USE_SOAP_GLOBAL;
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;
133 private:
134 sdl *m_old_sdl;
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;
141 void save() {
142 USE_SOAP_GLOBAL;
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,
157 int level);
158 static void header_if_not_sent(const String& str);
160 ///////////////////////////////////////////////////////////////////////////////
161 // client helpers
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) {
168 USE_SOAP_GLOBAL;
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) {
174 sdlParamVec *ht;
176 if (!function) {
177 return sdlParamPtr();
180 if (!response) {
181 ht = &function->requestParameters;
182 } else {
183 ht = &function->responseParameters;
186 if (ht->empty()) {
187 return sdlParamPtr();
190 if (param_name) {
191 for (unsigned int i = 0; i < ht->size(); i++) {
192 sdlParamPtr p = (*ht)[i];
193 if (p->paramName == param_name) return p;
195 } else {
196 if (index >= 0 && index < (int)ht->size()) {
197 return (*ht)[index];
200 return sdlParamPtr();
203 static xmlNodePtr serialize_zval(const Variant& val, sdlParamPtr param,
204 const char *paramName, int style,
205 xmlNodePtr parent) {
206 xmlNodePtr xmlParam;
207 encodePtr enc;
209 Variant v = val;
210 if (param != NULL) {
211 enc = param->encode;
212 if (val.isNull()) {
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);
221 } else {
222 enc = encodePtr();
224 xmlParam = master_to_xml(enc, val, style, parent);
225 if (!strcmp((char*)xmlParam->name, "BOGUS")) {
226 xmlNodeSetName(xmlParam, BAD_CAST(paramName));
228 return xmlParam;
231 static xmlNodePtr serialize_parameter(sdlParamPtr param, Variant value,
232 int index, const char *name, int style,
233 xmlNodePtr parent) {
234 if (!value.isNull() && value.isObject()) {
235 c_SoapParam *p = value.toObject().getTyped<c_SoapParam>(true, true);
236 if (p) {
237 value = p->m_data;
238 name = p->m_name.c_str();
242 if (param && !param->paramName.empty()) {
243 name = param->paramName.c_str();
244 } else {
245 if (name == NULL) {
246 char paramNameBuf[10];
247 snprintf(paramNameBuf, sizeof(paramNameBuf), "param%d", index);
248 name = paramNameBuf;
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;
260 xmlNsPtr ns = NULL;
261 int style, use;
262 sdlSoapBindingFunctionHeaderMap *hdrs = NULL;
264 encode_reset_ns();
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);
279 } else {
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;
294 style = fnb->style;
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);
303 } else {
304 method = xmlNewChild(body, ns,
305 BAD_CAST(function->functionName.c_str()), NULL);
308 } else {
309 use = client->m_use;
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);
315 if (function_name) {
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);
323 } else {
324 method = body;
326 } else {
327 method = body;
331 int i = 0;
332 for (ArrayIter iter(arguments); iter; ++iter, ++i) {
333 xmlNodePtr param;
334 sdlParamPtr parameter;
335 if (function) {
336 parameter = get_param(function.get(), NULL, i, false);
339 if (style == SOAP_RPC) {
340 if (parameter) {
341 param = serialize_parameter(parameter, iter.second(), i, NULL,
342 use, method);
344 } else if (style == SOAP_DOCUMENT) {
345 param = serialize_parameter(parameter, iter.second(), i, NULL,
346 use, body);
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()));
351 xmlSetNs(param, ns);
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++) {
361 xmlNodePtr param;
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()));
373 xmlSetNs(param, ns);
381 if (head) {
382 for (ArrayIter iter(soap_headers); iter; ++iter) {
383 c_SoapHeader *header = iter.second().toObject().getTyped<c_SoapHeader>();
385 xmlNodePtr h;
386 xmlNsPtr nsptr;
387 int hdr_use = SOAP_LITERAL;
388 encodePtr enc;
390 if (hdrs) {
391 std::string key = header->m_namespace.data();
392 key += ':';
393 key += header->m_name.data();
394 sdlSoapBindingFunctionHeaderMap::iterator iter = hdrs->find(key);
395 if (iter != hdrs->end()) {
396 auto hdr = iter->second;
397 hdr_use = hdr->use;
398 enc = hdr->encode;
399 if (hdr_use == SOAP_ENCODED) {
400 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()));
408 } else {
409 h = xmlNewNode(NULL, BAD_CAST(header->m_name.data()));
410 xmlAddChild(head, h);
412 nsptr = encode_add_ns(h, header->m_namespace.data());
413 xmlSetNs(h, nsptr);
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"),
418 BAD_CAST("1"));
419 } else {
420 xmlSetProp(h, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":mustUnderstand"),
421 BAD_CAST("true"));
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()));
429 } else {
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));
440 } else {
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));
467 if (method) {
468 xmlSetNsProp(method, envelope->ns, BAD_CAST("encodingStyle"),
469 BAD_CAST(SOAP_1_2_ENC_NAMESPACE));
474 encode_finish();
475 return doc;
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);
483 if (!buf) {
484 client->m_soap_fault =
485 create_soap_fault("HTTP", "Error build soap request");
486 return false;
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),
496 version, one_way);
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;
506 xmlFree(buf);
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 ///////////////////////////////////////////////////////////////////////////////
521 // shared helpers
523 const StaticString
524 s_type_name("type_name"),
525 s_type_ns("type_ns"),
526 s_to_xml("to_xml"),
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");
535 return typemap;
537 Array ht2 = tmp.toArray();
539 String type_name, type_ns;
540 Variant to_xml, to_zval;
541 for (ArrayIter it(ht2); it; ++it) {
542 tmp = it.second();
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) {
550 to_xml = tmp;
551 } else if (name == s_from_xml) {
552 to_zval = tmp;
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());
561 } else {
562 enc = get_encoder_ex(sdl, type_name.data());
565 new_enc = encodePtr(new encode());
566 if (enc) {
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;
571 } else {
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;
595 string nscat;
596 if (!type_ns.empty()) {
597 nscat += type_ns.data();
598 nscat += ':';
600 nscat += type_name.data();
601 (*typemap)[nscat] = new_enc;
604 return typemap;
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");
614 } else {
615 header_if_not_sent("Content-Type: text/xml; charset=utf-8");
619 ///////////////////////////////////////////////////////////////////////////////
620 // server helpers
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,
633 Array &parameters) {
634 int num_of_params = 0;
635 int cur_param = 0;
636 if (function) {
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())) {
643 use_names = true;
644 break;
647 if (use_names) {
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());
651 if (val) {
652 parameters.append(master_to_zval(param->encode, val));
655 return;
658 if (params) {
659 int num_of_params = 0;
660 xmlNodePtr trav = params;
661 while (trav != NULL) {
662 if (trav->type == XML_ELEMENT_NODE) {
663 num_of_params++;
665 trav = trav->next;
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) {
673 num_of_params = 0;
674 } else if (num_of_params > 0) {
675 trav = params;
676 while (trav != 0 && cur_param < num_of_params) {
677 if (trav->type == XML_ELEMENT_NODE) {
678 encodePtr enc;
679 sdlParamPtr param;
680 if (function) {
681 if (cur_param >= (int)function->requestParameters.size()) {
682 throw_soap_server_fault("Client","Error cannot find parameter");
684 param = function->requestParameters[cur_param];
686 if (param == NULL) {
687 enc.reset();
688 } else {
689 enc = param->encode;
691 parameters.set(cur_param, master_to_zval(enc, trav));
692 cur_param++;
694 trav = trav->next;
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) {
705 if (sdl) {
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()) {
714 return tmp;
716 } else if (!tmp->requestParameters.empty()) {
717 bool ok = true;
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) {
723 ok = false;
724 break;
726 if (!param->element->namens.empty() && node->ns) {
727 if (param->element->namens != (char*)node->ns->href) {
728 ok = false;
729 break;
731 } else if ((param->element->namens.empty() && node->ns) ||
732 (!param->element->namens.empty() &&
733 node->ns == NULL)) {
734 ok = false;
735 break;
737 } else if (param->paramName != (char*)node->name) {
738 ok = false;
739 break;
741 node = node->next;
743 if (ok /*&& node == NULL*/) {
744 return tmp;
751 return std::shared_ptr<sdlFunction>();
754 static std::shared_ptr<sdlFunction>
755 get_function(sdl *sdl, const char *function_name) {
756 if (sdl) {
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>();
765 return iter->second;
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()) {
778 function.reset();
782 if (sdl != NULL && function == NULL) {
783 function = get_doc_function(sdl, func);
786 if (function != NULL) {
787 function_name = String(function->functionName);
788 } else {
789 function_name = String((char *)func->name, CopyString);
792 return function;
795 static std::shared_ptr<sdlFunction> deserialize_function_call
796 (sdl *sdl, xmlDocPtr request, const char* actor, String &function_name,
797 Array &parameters, int &version, Array &headers) {
798 USE_SOAP_GLOBAL;
799 char* envelope_ns = NULL;
800 xmlNodePtr trav,env,head,body,func;
801 xmlAttrPtr attr;
802 std::shared_ptr<sdlFunction> function;
804 encode_reset_ns();
806 /* Get <Envelope> element */
807 env = NULL;
808 trav = request->children;
809 while (trav != NULL) {
810 if (trav->type == XML_ELEMENT_NODE) {
811 if (env == NULL &&
812 node_is_equal_ex(trav,"Envelope",SOAP_1_1_ENV_NAMESPACE)) {
813 env = trav;
814 version = SOAP_1_1;
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)) {
819 env = trav;
820 version = SOAP_1_2;
821 envelope_ns = SOAP_1_2_ENV_NAMESPACE;
822 SOAP_GLOBAL(soap_version) = SOAP_1_2;
823 } else {
824 throw_soap_server_fault("VersionMismatch", "Wrong Version");
827 trav = trav->next;
829 if (env == NULL) {
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 "
842 "on the Envelope");
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");
848 attr = attr->next;
851 /* Get <Header> element */
852 head = NULL;
853 trav = env->children;
854 while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
855 trav = trav->next;
857 if (trav != NULL && node_is_equal_ex(trav,"Header",envelope_ns)) {
858 head = trav;
859 trav = trav->next;
862 /* Get <Body> element */
863 body = NULL;
864 while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
865 trav = trav->next;
867 if (trav != NULL && node_is_equal_ex(trav,"Body",envelope_ns)) {
868 body = trav;
869 trav = trav->next;
871 while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
872 trav = trav->next;
874 if (body == NULL) {
875 throw_soap_server_fault("Client", "Body must be present in a "
876 "SOAP envelope");
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 "
888 "on the Body");
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");
894 attr = attr->next;
897 if (trav != NULL && version == SOAP_1_2) {
898 throw_soap_server_fault("Client", "A SOAP 1.2 envelope can contain only "
899 "Header and Body");
902 func = NULL;
903 trav = body->children;
904 while (trav != NULL) {
905 if (trav->type == XML_ELEMENT_NODE) {
907 if (func != NULL) {
908 throw_soap_server_fault("Client", "looks like we got \"Body\" with "
909 "several functions call", NULL, NULL, NULL);
912 func = trav;
913 break; /* FIXME: the rest of body is ignored */
915 trav = trav->next;
917 if (func == NULL) {
918 function = get_doc_function(sdl, NULL);
919 if (function != NULL) {
920 function_name = String(function->functionName);
921 } else {
922 throw_soap_server_fault
923 ("Client", "looks like we got \"Body\" without function call");
925 } else {
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");
933 } else {
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");
947 } else {
948 throw SoapException("Procedure '%s' not present", func->name);
953 headers = Array::Create();
954 if (head) {
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"
965 " on the Header");
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");
971 attr = attr->next;
973 trav = head->children;
974 while (trav != NULL) {
975 if (trav->type == XML_ELEMENT_NODE) {
976 xmlNodePtr hdr_func = trav;
977 xmlAttrPtr attr;
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);
988 if (attr != NULL) {
989 if (strcmp((char*)attr->children->content,
990 SOAP_1_1_ACTOR_NEXT) != 0 &&
991 (actor == NULL ||
992 strcmp((char*)attr->children->content,actor) != 0)) {
993 goto ignore_header;
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);
1005 if (attr != NULL) {
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 &&
1010 (actor == NULL ||
1011 strcmp((char*)attr->children->content,actor) != 0)) {
1012 goto ignore_header;
1016 attr = get_attribute_ex(hdr_func->properties,"mustUnderstand",
1017 envelope_ns);
1018 if (attr) {
1019 if (strcmp((char*)attr->children->content,"1") == 0 ||
1020 strcmp((char*)attr->children->content,"true") == 0) {
1021 mustUnderstand = 1;
1022 } else if (strcmp((char*)attr->children->content,"0") == 0 ||
1023 strcmp((char*)attr->children->content,"false") == 0) {
1024 mustUnderstand = 0;
1025 } else {
1026 throw_soap_server_fault("Client",
1027 "mustUnderstand value is not boolean");
1030 h = NEWOBJ(soapHeader)();
1031 Resource hobj(h);
1032 h->function = find_function(sdl, hdr_func, h->function_name).get();
1033 h->mustUnderstand = mustUnderstand;
1034 h->hdr = NULL;
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()) {
1039 string key;
1040 if (hdr_func->ns) {
1041 key += (char*)hdr_func->ns->href;
1042 key += ':';
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();
1052 if (h->hdr) {
1053 h->parameters.append(master_to_zval(h->hdr->encode, hdr_func));
1054 } else {
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);
1066 ignore_header:
1067 trav = trav->next;
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;
1077 } else {
1078 func = func->children;
1080 deserialize_parameters(func, function.get(), parameters);
1082 encode_finish();
1084 return function;
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;
1092 int param_count;
1093 int style, use;
1094 xmlNsPtr ns = NULL;
1096 if (function && function->binding->bindingType == BINDING_SOAP) {
1097 sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
1098 style = fnb->style;
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);
1110 } else {
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);
1119 if (function) {
1120 param_count = function->responseParameters.size();
1121 } else {
1122 param_count = 1;
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);
1136 } else {
1137 param = serialize_parameter(parameter, ret, 0, "return", use, method);
1139 } else {
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();
1155 int i = 0;
1156 for (ArrayIter iter(arr); iter; ++iter, ++i) {
1157 Variant data = iter.second();
1158 Variant key = iter.first();
1159 String param_name;
1160 int64_t param_index = i;
1161 if (key.isString()) {
1162 param_name = key.toString();
1163 } else {
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(),
1170 use, method);
1171 } else {
1172 param = serialize_parameter(parameter, data, i, param_name.data(),
1173 use, body);
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));
1188 return use;
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;
1197 xmlNsPtr ns = NULL;
1198 int use = SOAP_LITERAL;
1199 xmlNodePtr head = NULL;
1201 encode_reset_ns();
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);
1217 } else {
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();
1226 char *detail_name;
1227 std::shared_ptr<sdlFault> fault;
1228 string fault_ns;
1230 if (!headers.empty()) {
1231 xmlNodePtr head;
1232 encodePtr hdr_enc;
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>();
1243 string key;
1244 if (!ht->m_namespace.empty()) {
1245 key += ht->m_namespace.data();
1246 key += ':';
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();
1253 if (h->hdr) {
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;
1259 hdr_use = hdr->use;
1262 hdr_ret = ht->m_data;
1263 obj->o_set("headerfault", hdr_ret);
1266 if (h->function) {
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);
1271 use = SOAP_ENCODED;
1273 } else {
1274 xmlNodePtr xmlHdr = master_to_xml(hdr_enc, hdr_ret, hdr_use, head);
1275 if (hdr_name) {
1276 xmlNodeSetName(xmlHdr, BAD_CAST(hdr_name));
1278 if (hdr_ns) {
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();
1289 use = SOAP_LITERAL;
1290 if (!obj->o_get("_name").toString().empty()) {
1291 if (function) {
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;
1300 use = fb->use;
1301 if (fault_ns.empty()) {
1302 fault_ns = fb->ns;
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;
1313 use = fb->use;
1314 if (fault_ns.empty()) {
1315 fault_ns = fb->ns;
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,
1337 NULL, 0);
1338 xmlNodeSetContent(node, code);
1339 xmlFree(code);
1340 } else {
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,
1347 param);
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,
1353 param);
1354 xmlNodeSetName(node, BAD_CAST("faultactor"));
1356 detail_name = "detail";
1357 } else {
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,
1367 NULL, 0);
1368 xmlNodeSetContent(node, code);
1369 xmlFree(code);
1370 } else {
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"));
1379 xmlSetNs(node, ns);
1381 detail_name = SOAP_1_2_ENV_NS_PREFIX":Detail";
1383 if (fault && fault->details.size() == 1) {
1384 xmlNodePtr node;
1385 Variant detail;
1386 sdlParamPtr sparam;
1387 xmlNodePtr x;
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()) {
1400 detail = prop;
1404 x = serialize_parameter(sparam, detail, 1, NULL, use, node);
1406 if (function &&
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());
1416 xmlSetNs(x, ns);
1419 } else {
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()));
1423 xmlSetNs(x, ns);
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);
1434 } else {
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()) {
1441 encodePtr hdr_enc;
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>();
1450 string key;
1451 if (!ht->m_namespace.empty()) {
1452 key += ht->m_namespace.data();
1453 key += ':';
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;
1473 if (h->function) {
1474 if (serialize_response_call2(head, h->function,
1475 h->function_name.data(), uri, hdr_ret,
1476 version, 0) == SOAP_ENCODED) {
1477 use = SOAP_ENCODED;
1479 } else {
1480 xmlNodePtr xmlHdr = master_to_xml(hdr_enc, hdr_ret, hdr_use, head);
1481 if (hdr_name) {
1482 xmlNodeSetName(xmlHdr, BAD_CAST(hdr_name));
1484 if (hdr_ns) {
1485 xmlNsPtr nsptr = encode_add_ns(xmlHdr,hdr_ns);
1486 xmlSetNs(xmlHdr, nsptr);
1492 if (head->children == NULL) {
1493 xmlUnlinkNode(head);
1494 xmlFreeNode(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) {
1502 use = 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));
1519 encode_finish();
1521 if (function && function->responseName.empty() &&
1522 body->children == NULL && head == NULL) {
1523 xmlFreeDoc(doc);
1524 return NULL;
1526 return doc;
1529 static void function_to_string(std::shared_ptr<sdlFunction> function,
1530 StringBuffer &sb) {
1531 sdlParamPtr param;
1532 sdlParamVec &res = function->responseParameters;
1533 if (!res.empty()) {
1534 if (res.size() == 1) {
1535 param = res[0];
1536 if (param->encode && !param->encode->details.type_str.empty()) {
1537 sb.append(param->encode->details.type_str);
1538 sb.append(' ');
1539 } else {
1540 sb.append("UNKNOWN ");
1542 } else {
1543 sb.append("list(");
1544 bool started = false;
1545 for (unsigned int i = 0; i < res.size(); i++) {
1546 param = res[i];
1547 if (started) {
1548 sb.append(", ");
1549 } else {
1550 started = true;
1552 if (param->encode && !param->encode->details.type_str.empty()) {
1553 sb.append(param->encode->details.type_str);
1554 } else {
1555 sb.append("UNKNOWN");
1557 sb.append(" $");
1558 sb.append(param->paramName);
1560 sb.append(") ");
1562 } else {
1563 sb.append("void ");
1566 sb.append(function->functionName);
1568 sb.append('(');
1569 sdlParamVec &req = function->requestParameters;
1570 bool started = false;
1571 for (unsigned int i = 0; i < req.size(); i++) {
1572 param = req[i];
1573 if (started) {
1574 sb.append(", ");
1575 } else {
1576 started = true;
1579 if (param->encode && !param->encode->details.type_str.empty()) {
1580 sb.append(param->encode->details.type_str);
1581 } else {
1582 sb.append("UNKNOWN");
1584 sb.append(" $");
1585 sb.append(param->paramName);
1587 sb.append(')');
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();
1596 buf.append(spaces);
1598 switch (type->kind) {
1599 case XSD_TYPEKIND_SIMPLE:
1600 if (type->encode) {
1601 buf.append(type->encode->details.type_str);
1602 buf.append(' ');
1603 } else {
1604 buf.append("anyType ");
1606 buf.append(type->name);
1607 break;
1608 case XSD_TYPEKIND_LIST:
1609 buf.append("list ");
1610 buf.append(type->name);
1611 if (!type->elements.empty()) {
1612 buf.append(" {");
1613 buf.append(type->elements[0]->name);
1614 buf.append('}');
1616 break;
1617 case XSD_TYPEKIND_UNION:
1618 buf.append("union ");
1619 buf.append(type->name);
1620 if (!type->elements.empty()) {
1621 bool first = true;
1622 buf.append(" {");
1623 for (unsigned int i = 0; i < type->elements.size(); i++) {
1624 if (!first) {
1625 buf.append(',');
1626 first = false;
1628 buf.append(type->elements[i]->name);
1630 buf.append('}');
1632 break;
1633 case XSD_TYPEKIND_COMPLEX:
1634 case XSD_TYPEKIND_RESTRICTION:
1635 case XSD_TYPEKIND_EXTENSION:
1636 if (type->encode &&
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(), '[');
1649 int len;
1650 if (end == NULL) {
1651 len = ext->val.size();
1652 } else {
1653 len = end- ext->val.c_str();
1655 if (len == 0) {
1656 buf.append("anyType");
1657 } else {
1658 buf.append(ext->val.c_str(), len);
1660 buf.append(' ');
1661 buf.append(type->name);
1662 if (end) {
1663 buf.append(end);
1666 } else {
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);
1678 buf.append(' ');
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);
1685 buf.append(' ');
1686 } else {
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;
1697 buf.append('[');
1698 buf.append(ext->val);
1699 buf.append(']');
1701 } else {
1702 buf.append("[]");
1705 } else {
1706 buf.append("struct ");
1707 buf.append(type->name);
1708 buf.append(' ');
1709 buf.append("{\n");
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;
1720 if (enc) {
1721 buf.append(spaces);
1722 buf.append(' ');
1723 buf.append(type->encode->details.type_str);
1724 buf.append(" _;\n");
1727 if (type->model) {
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;
1734 buf.append(spaces);
1735 buf.append(' ');
1736 if (attr->encode && !attr->encode->details.type_str.empty()) {
1737 buf.append(attr->encode->details.type_str);
1738 buf.append(' ');
1739 } else {
1740 buf.append("UNKNOWN ");
1742 buf.append(attr->name);
1743 buf.append(";\n");
1746 buf.append(spaces);
1747 buf.append('}');
1749 break;
1750 default:
1751 break;
1755 static void model_to_string(sdlContentModelPtr model, StringBuffer &buf,
1756 int level) {
1757 int i;
1758 switch (model->kind) {
1759 case XSD_CONTENT_ELEMENT:
1760 type_to_string(model->u_element, buf, level);
1761 buf.append(";\n");
1762 break;
1763 case XSD_CONTENT_ANY:
1764 for (i = 0;i < level;i++) {
1765 buf.append(' ');
1767 buf.append("<anyXML> any;\n");
1768 break;
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);
1776 break;
1778 case XSD_CONTENT_GROUP:
1779 model_to_string(model->u_group->model, buf, level);
1780 default:
1781 break;
1785 ///////////////////////////////////////////////////////////////////////////////
1786 // soap fault functions
1788 const StaticString
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,
1795 Variant fault,
1796 soapHeader *hdr) {
1797 USE_SOAP_GLOBAL;
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));
1808 Array headers;
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);
1817 if (buf) {
1818 g_context->write(String((const char *)buf, size, CopyString));
1819 xmlFree(buf);
1821 xmlFreeDoc(doc_return);
1824 static void send_soap_server_fault(
1825 std::shared_ptr<sdlFunction> function,
1826 Exception &e,
1827 soapHeader *hdr) {
1828 USE_SOAP_GLOBAL;
1829 if (SOAP_GLOBAL(use_soap_error_handler)) {
1830 send_soap_server_fault(
1831 std::shared_ptr<sdlFunction>(), create_soap_fault(e), NULL);
1832 } else {
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),
1840 NULL);
1841 throw ExitException(1);
1844 bool f_use_soap_error_handler(bool handler /* = true */) {
1845 USE_SOAP_GLOBAL;
1846 bool old = SOAP_GLOBAL(use_soap_error_handler);
1847 SOAP_GLOBAL(use_soap_error_handler) = handler;
1848 return old;
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() {
1857 USE_SOAP_GLOBAL;
1858 return SOAP_GLOBAL(soap_version);
1861 ///////////////////////////////////////////////////////////////////////////////
1862 // class SoapServer
1864 c_SoapServer::c_SoapServer(Class* cb) :
1865 ExtObjectData(cb),
1866 m_type(SOAP_FUNCTIONS),
1867 m_version(SOAP_1_1),
1868 m_sdl(NULL),
1869 m_encoding(NULL),
1870 m_typemap(NULL),
1871 m_features(0),
1872 m_send_errors(1) {
1875 c_SoapServer::~c_SoapServer() {
1878 const StaticString
1879 s_soap_version("soap_version"),
1880 s_uri("uri"),
1881 s_actor("actor"),
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"),
1889 s_style("style"),
1890 s_use("use"),
1891 s_stream_context("stream_context"),
1892 s_login("login"),
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"),
1899 s_trace("trace"),
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 */) {
1908 USE_SOAP_GLOBAL;
1909 SoapServerScope ss(this);
1911 try {
1913 if (!wsdl.isString() && !wsdl.isNull()) {
1914 throw SoapException("Invalid parameters");
1917 m_send_errors = 1;
1919 int64_t cache_wsdl = SOAP_GLOBAL(cache);
1921 int version = SOAP_1_1;
1922 Array typemap_ht;
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) {
1927 version = tmp;
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);
1984 } else {
1985 /*FIXME*/
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;
2008 } else {
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);
2022 Array funcs;
2023 if (func.isString()) {
2024 funcs.append(func);
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;
2032 } else {
2033 raise_warning("Invalid value passed");
2035 return;
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");
2042 return;
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());
2048 return;
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);
2059 String class_name;
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);
2092 const StaticString
2093 s_HTTP_CONTENT_ENCODING("HTTP_CONTENT_ENCODING"),
2094 s_gzip("gzip"),
2095 s_xgzip("x-gzip"),
2096 s_deflate("deflate");
2098 void c_SoapServer::t_handle(const String& request /* = null_string */) {
2099 USE_SOAP_GLOBAL;
2100 SoapServerScope ss(this);
2102 SOAP_GLOBAL(soap_version) = m_version;
2104 // 0. serving WSDL
2105 Transport *transport = g_context->getTransport();
2106 if (transport && transport->getMethod() == Transport::Method::GET &&
2107 transport->getCommand() == "wsdl") {
2108 if (!m_sdl) {
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");
2117 return;
2120 if (!HHVM_FN(ob_start)()) {
2121 throw SoapException("ob_start failed");
2124 // 1. process request
2125 String req;
2126 if (!request.isNull()) {
2127 req = request;
2128 } else {
2129 int size;
2130 const char *data = NULL;
2131 if (transport) {
2132 data = (const char *)transport->getPostData(size);
2134 if (!data || !*data || !size) {
2135 return;
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();
2142 Variant ret;
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));
2147 } else {
2148 raise_warning("Request is encoded with unknown compression '%s'",
2149 encoding.data());
2150 return;
2152 if (!ret.isString()) {
2153 raise_warning("Can't uncompress compressed request");
2154 return;
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;
2179 Array params;
2180 int soap_version = 0;
2181 std::shared_ptr<sdlFunction> function;
2182 try {
2183 function = deserialize_function_call(m_sdl, doc_request, m_actor.c_str(),
2184 function_name, params, soap_version,
2185 m_soap_headers);
2186 } catch (Exception &e) {
2187 xmlFreeDoc(doc_request);
2188 send_soap_server_fault(function, e, NULL);
2189 return;
2191 xmlFreeDoc(doc_request);
2193 // 3. we may need an object
2194 Object soap_obj;
2195 if (m_type == SOAP_OBJECT) {
2196 soap_obj = m_soap_object;
2197 } else if (m_type == SOAP_CLASS) {
2198 try {
2199 soap_obj = create_object(m_soap_class.name,
2200 m_soap_class.argv);
2201 } catch (Exception &e) {
2202 send_soap_server_fault(function, e, NULL);
2203 return;
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");
2214 continue;
2217 String fn_name = h->function_name;
2218 if (valid_function(this, soap_obj, fn_name)) {
2219 try {
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);
2223 } else {
2224 h->retval = vm_call_user_func(fn_name, h->parameters);
2226 } catch (Exception &e) {
2227 send_soap_server_fault(function, e, h);
2228 return;
2230 if (h->retval.isObject() &&
2231 h->retval.getObjectData()->instanceof(SystemLib::s_SoapFaultClass)) {
2232 send_soap_server_fault(function, h->retval, h);
2233 return;
2235 } else if (h->mustUnderstand) {
2236 throw_soap_server_fault("MustUnderstand","Header not understood");
2240 // 5. main call
2241 String fn_name = function_name;
2242 Variant retval;
2243 if (valid_function(this, soap_obj, fn_name)) {
2244 try {
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);
2248 } else {
2249 retval = vm_call_user_func(fn_name, params);
2251 } catch (Exception &e) {
2252 send_soap_server_fault(function, e, NULL);
2253 return;
2255 if (retval.isObject() &&
2256 retval.toObject()->instanceof(SystemLib::s_SoapFaultClass)) {
2257 send_soap_server_fault(function, retval, NULL);
2258 return;
2260 } else {
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;
2268 } else {
2269 response_name = function_name + "Response";
2271 xmlDocPtr doc_return = NULL;
2272 try {
2273 doc_return = serialize_response_call(function, response_name.data(),
2274 m_uri.c_str(), retval, m_soap_headers,
2275 soap_version);
2276 } catch (Object &e) {
2277 if (e->o_instanceof("SoapFault")) {
2278 send_soap_server_fault(function, e, nullptr);
2279 return;
2281 throw e;
2282 } catch (Exception &e) {
2283 send_soap_server_fault(function, e, NULL);
2284 return;
2287 // 7. throw away all buffered output so far, so we can send back a clean
2288 // soap resposne
2289 HHVM_FN(ob_end_clean)();
2291 // 8. special case
2292 if (doc_return == NULL) {
2293 header_if_not_sent("HTTP/1.1 202 Accepted");
2294 header_if_not_sent("Content-Length: 0");
2295 return;
2298 // 9. XML response
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);
2307 if (buf) {
2308 g_context->write(String((char*)buf, size, CopyString));
2309 xmlFree(buf);
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;
2318 } else {
2319 raise_warning("Tried to set persistence with bogus value (%" PRId64 ")",
2320 mode);
2322 } else {
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)();
2340 Resource obj(p);
2341 p->function = NULL;
2342 p->mustUnderstand = false;
2343 p->retval = fault;
2344 p->hdr = NULL;
2345 m_soap_headers.append(obj);
2348 ///////////////////////////////////////////////////////////////////////////////
2349 // class SoapClient
2351 c_SoapClient::c_SoapClient(Class* cb) :
2352 ExtObjectDataFlags<ObjectData::HasCall>(cb),
2353 m_soap_version(SOAP_1_1),
2354 m_sdl(NULL),
2355 m_encoding(NULL),
2356 m_typemap(NULL),
2357 m_features(0),
2358 m_style(SOAP_RPC),
2359 m_use(SOAP_LITERAL),
2360 m_authentication(SOAP_AUTHENTICATION_BASIC),
2361 m_proxy_port(0),
2362 m_connection_timeout(0),
2363 m_max_redirect(HttpClient::defaultMaxRedirect),
2364 m_use11(true),
2365 m_compression(false),
2366 m_exceptions(true),
2367 m_trace(false) {
2370 c_SoapClient::~c_SoapClient() {
2373 void c_SoapClient::t___construct(const Variant& wsdl,
2374 const Array& options /* = null_array */) {
2375 USE_SOAP_GLOBAL;
2376 SoapClientScope ss(this);
2378 try {
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>();
2408 if (!sc) {
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'",
2440 encoding.data());
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 "
2455 "nonWSDL mode");
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);
2473 } else {
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];
2480 if (v.isArray()) {
2481 Array arr = v.toArray();
2482 if (!arr.empty()) {
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);
2527 } else{
2528 raise_warning("Invalid SOAP header");
2529 return init_null();
2531 if (!m_default_headers.isNull()) {
2532 soap_headers.merge(m_default_headers.toArray());
2535 Array output_headers;
2536 SCOPE_EXIT {
2537 output_headers_ref = output_headers;
2540 if (m_trace) {
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;
2553 bool ret = false;
2554 xmlDocPtr request = NULL;
2555 if (m_sdl) {
2556 auto fn = get_function(m_sdl, name.data());
2557 if (fn) {
2558 sdlBindingPtr binding = fn->binding;
2559 bool one_way = false;
2560 if (fn->responseName.empty() && fn->responseParameters.empty() &&
2561 soap_headers.empty()) {
2562 one_way = true;
2564 if (location.empty()) {
2565 location = binding->location;
2568 Variant response;
2569 try {
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,
2576 response);
2577 } else {
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()) {
2590 encode_reset_ns();
2591 String sresponse = response.toString();
2592 ret = parse_packet_soap(this, sresponse.data(), sresponse.size(),
2593 fn, NULL, return_value, output_headers);
2594 encode_finish();
2596 } else {
2597 StringBuffer error;
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());
2603 } else {
2604 string action;
2605 if (m_uri.empty()) {
2606 m_soap_fault =
2607 create_soap_fault("Client", "Error finding \"uri\" property");
2608 } else if (location.empty()) {
2609 m_soap_fault =
2610 create_soap_fault("Client", "Error could not find \"location\" "
2611 "property");
2612 } else {
2613 request = serialize_function_call
2614 (this, std::shared_ptr<sdlFunction>(), name.data(), m_uri.data(), args,
2615 soap_headers);
2616 if (soap_action.empty()) {
2617 action += m_uri.data();
2618 action += '#';
2619 action += name.data();
2620 } else {
2621 action += (std::string) soap_action;
2623 Variant response;
2624 try {
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()) {
2633 encode_reset_ns();
2634 String sresponse = response.toString();
2635 ret = parse_packet_soap(this, sresponse.data(), sresponse.size(),
2636 std::shared_ptr<sdlFunction>(),
2637 name.data(),
2638 return_value,
2639 output_headers);
2640 encode_finish();
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);
2673 if (m_sdl) {
2674 Array ret = Array::Create();
2675 for (auto& func: m_sdl->functionsOrder) {
2676 StringBuffer sb;
2677 function_to_string(m_sdl->functions[func], sb);
2678 ret.append(sb.detach());
2680 return ret;
2682 return init_null();
2685 Variant c_SoapClient::t___gettypes() {
2686 SoapClientScope ss(this);
2688 if (m_sdl) {
2689 Array ret = Array::Create();
2690 for (unsigned int i = 0; i < m_sdl->types.size(); i++) {
2691 StringBuffer sb;
2692 type_to_string(m_sdl->types[i].get(), sb, 0);
2693 ret.append(sb.detach());
2695 return ret;
2697 return init_null();
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()) {
2703 m_soap_fault =
2704 Object(SystemLib::AllocSoapFaultObject("HTTP", "Unable to parse URL"));
2705 return init_null();
2708 USE_SOAP_GLOBAL;
2709 SoapClientScope ss(this);
2711 HeaderMap headers;
2713 String buffer(buf);
2715 // compression
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;
2722 if (level > 0) {
2723 Variant ret;
2724 if (m_compression & SOAP_COMPRESSION_DEFLATE) {
2725 ret = HHVM_FN(gzcompress)(buffer, level);
2726 headers["Content-Encoding"].push_back("deflate");
2727 } else {
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());
2740 string contentType;
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);
2747 } else {
2748 contentType = "text/xml; charset=utf-8";
2749 headers["Content-Type"].push_back(contentType);
2750 headers["SOAPAction"].push_back(string("\"") + action.data() + "\"");
2753 // post the request
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,
2765 &headers);
2766 if (code == 0) {
2767 String msg = "Failed Sending HTTP Soap request";
2768 if (!http.getLastError().empty()) {
2769 msg += ": " + http.getLastError();
2771 m_soap_fault = Object(SystemLib::AllocSoapFaultObject(
2772 "HTTP", msg));
2773 return init_null();
2775 if ((code >= 400) &&
2776 (m_exceptions || response.empty())) {
2777 String msg = response.detach();
2778 if (msg.empty()) {
2779 msg = HttpProtocol::GetReasonString(code);
2781 m_soap_fault = Object(SystemLib::AllocSoapFaultObject("HTTP", msg));
2782 return init_null();
2785 // return response
2786 if (SOAP_GLOBAL(features) & SOAP_WAIT_ONE_WAY_CALLS) {
2787 oneway = false;
2789 if (oneway) {
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));
2799 } else {
2800 const Variant* t = o_realProp("_cookies", RealPropUnchecked);
2801 if (t && t->isInitialized()) {
2802 m_cookies.remove(name);
2805 return init_null();
2808 Variant c_SoapClient::t___setlocation(const String& new_location /* = null_string */) {
2809 Variant ret = m_location;
2810 m_location = new_location;
2811 return ret;
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);
2823 } else {
2824 raise_warning("Invalid SOAP header");
2826 return true;
2829 ///////////////////////////////////////////////////////////////////////////////
2830 // class SoapVar
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 */) {
2843 USE_SOAP_GLOBAL;
2844 if (type.isNull()) {
2845 m_type = UNKNOWN_TYPE;
2846 } else {
2847 std::map<int, encodePtr> &defEncIndex = SOAP_GLOBAL(defEncIndex);
2848 int64_t ntype = type.toInt64();
2849 if (defEncIndex.find(ntype) != defEncIndex.end()) {
2850 m_type = ntype;
2851 } else {
2852 raise_warning("Invalid type ID");
2853 return;
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 ///////////////////////////////////////////////////////////////////////////////
2865 // class SoapParam
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) {
2874 if (name.empty()) {
2875 raise_warning("Invalid parameter name");
2876 return;
2878 m_name = name;
2879 m_data = data;
2882 ///////////////////////////////////////////////////////////////////////////////
2883 // class SoapHeader
2885 c_SoapHeader::c_SoapHeader(Class* cb) :
2886 ExtObjectData(cb) {
2889 c_SoapHeader::~c_SoapHeader() {
2892 static const StaticString
2893 s_namespace("namespace"),
2894 s_name("name"),
2895 s_data("data"),
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 */) {
2902 if (ns.empty()) {
2903 raise_warning("Invalid namespace");
2904 return;
2906 if (name.empty()) {
2907 raise_warning("Invalid header name");
2908 return;
2911 m_namespace = ns;
2912 m_name = name;
2913 m_data = data;
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 ///////////////////////////////////////////////////////////////////////////////