hphp/runtime/ext/soap/ext_soap.cpp (2,892 lines of code) (raw):
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/ext/soap/ext_soap.h"
#include <map>
#include <memory>
#include <folly/ScopeGuard.h>
#include "hphp/runtime/base/array-init.h"
#include "hphp/runtime/base/array-iterator.h"
#include "hphp/runtime/base/builtin-functions.h"
#include "hphp/runtime/base/comparisons.h"
#include "hphp/runtime/base/http-client.h"
#include "hphp/runtime/base/php-globals.h"
#include "hphp/runtime/base/type-variant.h"
#include "hphp/runtime/server/http-protocol.h"
#include "hphp/runtime/ext/soap/soap.h"
#include "hphp/runtime/ext/soap/packet.h"
#include "hphp/runtime/base/string-util.h"
#include "hphp/runtime/ext/zlib/ext_zlib.h"
#include "hphp/runtime/ext/std/ext_std_network.h"
#include "hphp/runtime/ext/array/ext_array.h"
#include "hphp/runtime/ext/std/ext_std_function.h"
#include "hphp/runtime/ext/std/ext_std_classobj.h"
#include "hphp/runtime/ext/std/ext_std_output.h"
#include "hphp/runtime/ext/stream/ext_stream.h"
#include "hphp/runtime/ext/string/ext_string.h"
#include "hphp/system/systemlib.h"
namespace HPHP {
const StaticString s___dorequest("__dorequest");
#define IMPLEMENT_GET_CLASS(cls) \
Class* cls::getClass() { \
if (s_class == nullptr) { \
s_class = Class::lookup(s_className.get()); \
assertx(s_class); \
} \
return s_class; \
} \
///////////////////////////////////////////////////////////////////////////////
// helper classes for setting/resetting globals within a method call
using std::string;
struct SoapScope {
SoapScope() {
USE_SOAP_GLOBAL;
m_old_handler = SOAP_GLOBAL(use_soap_error_handler);
m_old_error_code = SOAP_GLOBAL(error_code);
m_old_error_object = SOAP_GLOBAL(error_object);
m_old_soap_version = SOAP_GLOBAL(soap_version);
SOAP_GLOBAL(use_soap_error_handler) = true;
}
~SoapScope() {
USE_SOAP_GLOBAL;
SOAP_GLOBAL(use_soap_error_handler) = m_old_handler;
SOAP_GLOBAL(error_code) = m_old_error_code;
SOAP_GLOBAL(error_object) = m_old_error_object;
SOAP_GLOBAL(soap_version) = m_old_soap_version;
}
private:
bool m_old_handler;
const char *m_old_error_code;
Object m_old_error_object;
int m_old_soap_version;
};
struct SoapServerScope : SoapScope {
explicit SoapServerScope(ObjectData *server) {
USE_SOAP_GLOBAL;
SOAP_GLOBAL(error_code) = "Server";
SOAP_GLOBAL(error_object) = Object(server);
}
};
struct SoapClientScope : SoapScope {
explicit SoapClientScope(ObjectData *client) {
USE_SOAP_GLOBAL;
SOAP_GLOBAL(error_code) = "Client";
SOAP_GLOBAL(error_object) = Object(client);
}
};
struct SoapServiceScope {
explicit SoapServiceScope(SoapServer *server) {
save();
USE_SOAP_GLOBAL;
SOAP_GLOBAL(soap_version) = server->m_version;
SOAP_GLOBAL(sdl) = server->m_sdl;
SOAP_GLOBAL(encoding) = server->m_encoding;
SOAP_GLOBAL(classmap) = server->m_classmap;
SOAP_GLOBAL(typemap) = server->m_typemap;
SOAP_GLOBAL(features) = server->m_features;
}
explicit SoapServiceScope(SoapClient *client) {
save();
USE_SOAP_GLOBAL;
SOAP_GLOBAL(soap_version) = client->m_soap_version;
SOAP_GLOBAL(sdl) = client->m_sdl;
SOAP_GLOBAL(encoding) = client->m_encoding;
SOAP_GLOBAL(classmap) = client->m_classmap;
SOAP_GLOBAL(typemap) = client->m_typemap;
SOAP_GLOBAL(features) = client->m_features;
}
~SoapServiceScope() {
USE_SOAP_GLOBAL;
SOAP_GLOBAL(soap_version) = m_old_soap_version;
SOAP_GLOBAL(encoding) = m_old_encoding;
SOAP_GLOBAL(sdl) = m_old_sdl;
SOAP_GLOBAL(classmap) = m_old_classmap;
SOAP_GLOBAL(typemap) = m_old_typemap;
SOAP_GLOBAL(features) = m_old_features;
}
private:
sdl *m_old_sdl;
xmlCharEncodingHandlerPtr m_old_encoding;
Array m_old_classmap;
encodeMap *m_old_typemap;
int64_t m_old_features;
int m_old_soap_version;
void save() {
USE_SOAP_GLOBAL;
m_old_soap_version = SOAP_GLOBAL(soap_version);
m_old_sdl = SOAP_GLOBAL(sdl);
m_old_encoding = SOAP_GLOBAL(encoding);
m_old_classmap = SOAP_GLOBAL(classmap);
m_old_typemap = SOAP_GLOBAL(typemap);
m_old_features = SOAP_GLOBAL(features);
}
};
///////////////////////////////////////////////////////////////////////////////
// forward declarations
static void throw_soap_server_fault(const char* code, const char* fault);
static void model_to_string(sdlContentModelPtr model, StringBuffer &buf,
int level);
static void header_if_not_sent(const String& str);
///////////////////////////////////////////////////////////////////////////////
// client helpers
static Object create_soap_fault(const String& code, const String& fault) {
return SystemLib::AllocSoapFaultObject(code, fault);
}
static Object create_soap_fault(Exception& e) {
USE_SOAP_GLOBAL;
return create_soap_fault(SOAP_GLOBAL(error_code), String(e.getMessage()));
}
static sdlParamPtr get_param(sdlFunction *function, const char *param_name,
int index, bool response) {
sdlParamVec *ht;
if (!function) {
return sdlParamPtr();
}
if (!response) {
ht = &function->requestParameters;
} else {
ht = &function->responseParameters;
}
if (ht->empty()) {
return sdlParamPtr();
}
if (param_name) {
for (unsigned int i = 0; i < ht->size(); i++) {
sdlParamPtr p = (*ht)[i];
if (p->paramName == param_name) return p;
}
} else {
if (index >= 0 && index < (int)ht->size()) {
return (*ht)[index];
}
}
return sdlParamPtr();
}
static xmlNodePtr serialize_zval(const Variant& val, sdlParamPtr param,
const char *paramName, int style,
xmlNodePtr parent) {
xmlNodePtr xmlParam;
encodePtr enc;
Variant v = val;
if (param != nullptr) {
enc = param->encode;
if (val.isNull()) {
if (param->element) {
if (!param->element->fixed.empty()) {
v = String(param->element->fixed);
} else if (!param->element->def.empty() && !param->element->nillable) {
v = String(param->element->def);
}
}
}
} else {
enc = encodePtr();
}
xmlParam = master_to_xml(enc, val, style, parent);
if (!strcmp((char*)xmlParam->name, "BOGUS")) {
xmlNodeSetName(xmlParam, BAD_CAST(paramName));
}
return xmlParam;
}
static xmlNodePtr serialize_parameter(sdlParamPtr param, Variant value,
int index, const char *name, int style,
xmlNodePtr parent) {
if (!value.isNull() && value.isObject()) {
Object obj_value = value.toObject();
if (obj_value.instanceof(SoapParam::getClass())) {
SoapParam* p = Native::data<SoapParam>(obj_value);
value = p->m_data;
name = p->m_name.c_str();
}
}
if (param && !param->paramName.empty()) {
name = param->paramName.c_str();
} else {
if (name == nullptr) {
char paramNameBuf[10];
snprintf(paramNameBuf, sizeof(paramNameBuf), "param%d", index);
name = paramNameBuf;
}
}
return serialize_zval(value, param, name, style, parent);
}
static xmlDocPtr serialize_function_call(SoapClient *client,
std::shared_ptr<sdlFunction> function,
const char *function_name,
const char *uri,
const Array& arguments,
const Array& soap_headers) {
xmlNodePtr envelope = nullptr, body, method = nullptr, head = nullptr;
xmlNsPtr ns = nullptr;
int style, use;
sdlSoapBindingFunctionHeaderMap *hdrs = nullptr;
encode_reset_ns();
xmlDoc *doc = xmlNewDoc(BAD_CAST("1.0"));
doc->encoding = xmlCharStrdup("UTF-8");
doc->charset = XML_CHAR_ENCODING_UTF8;
if (client->m_soap_version == SOAP_1_1) {
envelope = xmlNewDocNode(doc, nullptr, BAD_CAST("Envelope"), nullptr);
ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_1_ENV_NAMESPACE),
BAD_CAST(SOAP_1_1_ENV_NS_PREFIX));
xmlSetNs(envelope, ns);
} else if (client->m_soap_version == SOAP_1_2) {
envelope = xmlNewDocNode(doc, nullptr, BAD_CAST("Envelope"), nullptr);
ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENV_NAMESPACE),
BAD_CAST(SOAP_1_2_ENV_NS_PREFIX));
xmlSetNs(envelope, ns);
} else {
throw SoapException("Unknown SOAP version");
}
xmlDocSetRootElement(doc, envelope);
if (!soap_headers.empty()) {
head = xmlNewChild(envelope, ns, BAD_CAST("Header"), nullptr);
}
body = xmlNewChild(envelope, ns, BAD_CAST("Body"), nullptr);
if (function && function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
hdrs = &fnb->input.headers;
style = fnb->style;
/*FIXME: how to pass method name if style is SOAP_DOCUMENT */
/*style = SOAP_RPC;*/
use = fnb->input.use;
if (style == SOAP_RPC) {
ns = encode_add_ns(body, fnb->input.ns.c_str());
if (!function->requestName.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->requestName.c_str()), nullptr);
} else {
method = xmlNewChild(body, ns,
BAD_CAST(function->functionName.c_str()), nullptr);
}
}
} else {
use = client->m_use;
style = client->m_style;
/*FIXME: how to pass method name if style is SOAP_DOCUMENT */
/*style = SOAP_RPC;*/
if (style == SOAP_RPC) {
ns = encode_add_ns(body, uri);
if (function_name) {
method = xmlNewChild(body, ns, BAD_CAST(function_name), nullptr);
} else if (function && !function->requestName.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->requestName.c_str()), nullptr);
} else if (function && !function->functionName.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->functionName.c_str()), nullptr);
} else {
method = body;
}
} else {
method = body;
}
}
int i = 0;
for (ArrayIter iter(arguments); iter; ++iter, ++i) {
xmlNodePtr param;
sdlParamPtr parameter;
if (function) {
parameter = get_param(function.get(), nullptr, i, false);
}
if (style == SOAP_RPC) {
if (parameter) {
param = serialize_parameter(parameter, iter.second(), i, nullptr,
use, method);
}
} else if (style == SOAP_DOCUMENT) {
param = serialize_parameter(parameter, iter.second(), i, nullptr,
use, body);
if (function && function->binding->bindingType == BINDING_SOAP) {
if (parameter && parameter->element) {
ns = encode_add_ns(param, parameter->element->namens.c_str());
xmlNodeSetName(param, BAD_CAST(parameter->element->name.c_str()));
xmlSetNs(param, ns);
}
}
}
}
if (function && !function->requestParameters.empty()) {
int n = function->requestParameters.size();
if (n > arguments.size()) {
for (i = arguments.size(); i < n; i++) {
xmlNodePtr param;
sdlParamPtr parameter = get_param(function.get(), nullptr, i, false);
if (style == SOAP_RPC) {
param = serialize_parameter(parameter, uninit_null(), i, nullptr,
use, method);
} else if (style == SOAP_DOCUMENT) {
param = serialize_parameter(parameter, uninit_null(), i, nullptr,
use, body);
if (function && function->binding->bindingType == BINDING_SOAP) {
if (parameter && parameter->element) {
ns = encode_add_ns(param, parameter->element->namens.c_str());
xmlNodeSetName(param,
BAD_CAST(parameter->element->name.c_str()));
xmlSetNs(param, ns);
}
}
}
}
}
}
if (head) {
for (ArrayIter iter(soap_headers); iter; ++iter) {
Object obj_header = iter.second().toObject();
assertx(obj_header.instanceof(SoapHeader::getClass()));
SoapHeader *header = Native::data<SoapHeader>(obj_header);
xmlNodePtr h;
xmlNsPtr nsptr;
int hdr_use = SOAP_LITERAL;
encodePtr enc;
if (hdrs) {
std::string key = header->m_namespace.data();
key += ':';
key += header->m_name.data();
sdlSoapBindingFunctionHeaderMap::iterator iter = hdrs->find(key);
if (iter != hdrs->end()) {
auto hdr = iter->second;
hdr_use = hdr->use;
enc = hdr->encode;
if (hdr_use == SOAP_ENCODED) {
use = SOAP_ENCODED;
}
}
}
if (!header->m_data.isNull()) {
h = master_to_xml(enc, header->m_data, hdr_use, head);
xmlNodeSetName(h, BAD_CAST(header->m_name.data()));
} else {
h = xmlNewNode(nullptr, BAD_CAST(header->m_name.data()));
xmlAddChild(head, h);
}
nsptr = encode_add_ns(h, header->m_namespace.data());
xmlSetNs(h, nsptr);
if (header->m_mustUnderstand) {
if (client->m_soap_version == SOAP_1_1) {
xmlSetProp(h, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX":mustUnderstand"),
BAD_CAST("1"));
} else {
xmlSetProp(h, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":mustUnderstand"),
BAD_CAST("true"));
}
}
if (!header->m_actor.isNull()) {
if (header->m_actor.isString()) {
if (client->m_soap_version == SOAP_1_1) {
xmlSetProp(h, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX":actor"),
BAD_CAST(header->m_actor.toString().data()));
} else {
xmlSetProp(h, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":role"),
BAD_CAST(header->m_actor.toString().data()));
}
} else if (header->m_actor.isInteger()) {
int64_t actor = header->m_actor.toInt64();
if (client->m_soap_version == SOAP_1_1) {
if (actor == SOAP_ACTOR_NEXT) {
xmlSetProp(h, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX":actor"),
BAD_CAST(SOAP_1_1_ACTOR_NEXT));
}
} else {
if (actor == SOAP_ACTOR_NEXT) {
xmlSetProp(h, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":role"),
BAD_CAST(SOAP_1_2_ACTOR_NEXT));
} else if (actor == SOAP_ACTOR_NONE) {
xmlSetProp(h, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":role"),
BAD_CAST(SOAP_1_2_ACTOR_NONE));
} else if (actor == SOAP_ACTOR_UNLIMATERECEIVER) {
xmlSetProp(h, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":role"),
BAD_CAST(SOAP_1_2_ACTOR_UNLIMATERECEIVER));
}
}
}
}
}
}
if (use == SOAP_ENCODED) {
xmlNewNs(envelope, BAD_CAST(XSD_NAMESPACE), BAD_CAST(XSD_NS_PREFIX));
if (client->m_soap_version == SOAP_1_1) {
xmlNewNs(envelope, BAD_CAST(SOAP_1_1_ENC_NAMESPACE),
BAD_CAST(SOAP_1_1_ENC_NS_PREFIX));
xmlSetNsProp(envelope, envelope->ns, BAD_CAST("encodingStyle"),
BAD_CAST(SOAP_1_1_ENC_NAMESPACE));
} else if (client->m_soap_version == SOAP_1_2) {
xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENC_NAMESPACE),
BAD_CAST(SOAP_1_2_ENC_NS_PREFIX));
if (method) {
xmlSetNsProp(method, envelope->ns, BAD_CAST("encodingStyle"),
BAD_CAST(SOAP_1_2_ENC_NAMESPACE));
}
}
}
encode_finish();
return doc;
}
// Forward declare
Variant HHVM_METHOD(SoapClient, __dorequest,
const String& buf,
const String& location,
const String& action,
int64_t version,
bool oneway /* = false */);
static bool do_request(ObjectData* obj_client, xmlDoc *request,
const char *location, const char *action, int version,
bool one_way, Variant &response) {
SoapClient *client = Native::data<SoapClient>(obj_client);
char *buf; int buf_size;
xmlDocDumpMemory(request, (xmlChar**)&buf, &buf_size);
if (!buf) {
client->m_soap_fault =
create_soap_fault("HTTP", "Error build soap request");
return false;
}
if (client->m_trace) {
client->m_last_request = String((char*)buf, buf_size, CopyString);
}
response = obj_client->o_invoke_few_args(s___dorequest, RuntimeCoeffects::fixme(), 5,
String(buf, buf_size, CopyString),
String(location, CopyString),
String(action, CopyString),
version, one_way);
if (!response.isString()) {
if (client->m_soap_fault.isNull()) {
client->m_soap_fault =
create_soap_fault("Client", "SoapClient::__dorequest() "
"returned non string value");
}
} else if (client->m_trace) {
client->m_last_response = response;
}
xmlFree(buf);
return client->m_soap_fault.isNull();
}
static void verify_soap_headers_array(Array &headers) {
for (ArrayIter iter(headers); iter; ++iter) {
Variant tmp = iter.second();
if (!tmp.isObject() || !tmp.toObject().instanceof(SoapHeader::getClass())) {
throw SoapException("Invalid SOAP header");
}
}
}
///////////////////////////////////////////////////////////////////////////////
// shared helpers
const StaticString
s_type_name("type_name"),
s_type_ns("type_ns"),
s_to_xml("to_xml"),
s_from_xml("from_xml");
static encodeMapPtr soap_create_typemap_impl(sdl *sdl, Array &ht) {
auto typemap = std::make_shared<encodeMap>();
for (ArrayIter iter(ht); iter; ++iter) {
Variant tmp = iter.second();
if (!tmp.isArray()) {
raise_warning("Wrong 'typemap' option");
return typemap;
}
Array ht2 = tmp.toArray();
String type_name, type_ns;
Variant to_xml, to_zval;
for (ArrayIter it(ht2); it; ++it) {
tmp = it.second();
if (it.first().isString()) {
String name = it.first().toString();
if (name == s_type_name) {
if (tmp.isString()) type_name = tmp.toString();
} else if (name == s_type_ns) {
if (tmp.isString()) type_ns = tmp.toString();
} else if (name == s_to_xml) {
to_xml = tmp;
} else if (name == s_from_xml) {
to_zval = tmp;
}
}
}
encodePtr enc, new_enc;
if (!type_name.empty()) {
if (!type_ns.empty()) {
enc = get_encoder(sdl, type_ns.data(), type_name.data());
} else {
enc = get_encoder_ex(sdl, type_name.data());
}
new_enc = std::make_shared<encode>();
if (enc) {
new_enc->details.type = enc->details.type;
new_enc->details.ns = enc->details.ns;
new_enc->details.type_str = enc->details.type_str;
new_enc->details.sdl_type = enc->details.sdl_type;
} else {
enc = get_conversion(UNKNOWN_TYPE);
new_enc->details.type = enc->details.type;
if (!type_ns.empty()) {
new_enc->details.ns = type_ns.data();
}
new_enc->details.type_str = type_name.data();
}
new_enc->to_xml = enc->to_xml;
new_enc->to_zval = enc->to_zval;
new_enc->details.map = std::make_shared<soapMapping>();
if (to_xml.toBoolean()) {
new_enc->details.map->to_xml = to_xml;
new_enc->to_xml = to_xml_user;
} else if (enc->details.map && !enc->details.map->to_xml.isNull()) {
new_enc->details.map->to_xml = enc->details.map->to_xml;
}
if (to_zval.toBoolean()) {
new_enc->details.map->to_zval = to_zval;
new_enc->to_zval = to_zval_user;
} else if (enc->details.map && !enc->details.map->to_zval.isNull()) {
new_enc->details.map->to_zval = enc->details.map->to_zval;
}
string nscat;
if (!type_ns.empty()) {
nscat += type_ns.data();
nscat += ':';
}
nscat += type_name.data();
(*typemap)[nscat] = new_enc;
}
}
return typemap;
}
static encodeMap *soap_create_typemap(sdl *sdl, Array &ht) {
return s_soap_data->register_typemap(soap_create_typemap_impl(sdl, ht));
}
static void output_xml_header(int soap_version) {
if (soap_version == SOAP_1_2) {
header_if_not_sent("Content-Type: application/soap+xml; charset=utf-8");
} else {
header_if_not_sent("Content-Type: text/xml; charset=utf-8");
}
}
///////////////////////////////////////////////////////////////////////////////
// server helpers
/**
* The PHP5 extension uses SAPI to set known headers; unlike header(), this
* silently fails if headers_sent(). We need to do the same.
*/
static void header_if_not_sent(const String& str) {
if (!HHVM_FN(headers_sent)()) {
HHVM_FN(header)(str);
}
}
static void deserialize_parameters(xmlNodePtr params, sdlFunction *function,
Array ¶meters) {
int num_of_params = 0;
int cur_param = 0;
if (function) {
bool use_names = false;
num_of_params = function->requestParameters.size();
if (num_of_params == 0) return;
for (int i = 0; i < num_of_params; i++) {
sdlParamPtr param = function->requestParameters[i];
if (get_node(params, (char*)param->paramName.c_str())) {
use_names = true;
break;
}
}
if (use_names) {
for (int i = 0; i < num_of_params; i++, cur_param++) {
sdlParamPtr param = function->requestParameters[i];
xmlNodePtr val = get_node(params, (char*)param->paramName.c_str());
if (val) {
parameters.append(master_to_zval(param->encode, val));
}
}
return;
}
}
if (params) {
num_of_params = 0;
xmlNodePtr trav = params;
while (trav != nullptr) {
if (trav->type == XML_ELEMENT_NODE) {
num_of_params++;
}
trav = trav->next;
}
if (num_of_params == 1 && function && function->binding &&
function->binding->bindingType == BINDING_SOAP &&
function->bindingAttributes->style == SOAP_DOCUMENT &&
function->requestParameters.empty() &&
strcmp((char*)params->name, function->functionName.c_str()) == 0) {
num_of_params = 0;
} else if (num_of_params > 0) {
trav = params;
while (trav != 0 && cur_param < num_of_params) {
if (trav->type == XML_ELEMENT_NODE) {
encodePtr enc;
sdlParamPtr param;
if (function) {
if (cur_param >= (int)function->requestParameters.size()) {
throw_soap_server_fault("Client","Error cannot find parameter");
}
param = function->requestParameters[cur_param];
}
if (param == nullptr) {
enc.reset();
} else {
enc = param->encode;
}
parameters.set(cur_param, master_to_zval(enc, trav));
cur_param++;
}
trav = trav->next;
}
}
}
if (num_of_params > cur_param) {
throw_soap_server_fault("Client","Missing parameter");
}
}
static std::shared_ptr<sdlFunction>
get_doc_function(sdl *sdl, xmlNodePtr params) {
if (sdl) {
for (sdlFunctionMap::iterator iter = sdl->functions.begin();
iter != sdl->functions.end(); ++iter) {
auto tmp = iter->second;
if (tmp->binding && tmp->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = tmp->bindingAttributes;
if (fnb->style == SOAP_DOCUMENT) {
if (params == nullptr) {
if (tmp->requestParameters.empty()) {
return tmp;
}
} else if (!tmp->requestParameters.empty()) {
bool ok = true;
xmlNodePtr node = params;
for (unsigned int i = 0; i < tmp->requestParameters.size(); i++) {
sdlParamPtr param = tmp->requestParameters[i];
if (param->element) {
if (param->element->name != (char*)node->name) {
ok = false;
break;
}
if (!param->element->namens.empty() && node->ns) {
if (param->element->namens != (char*)node->ns->href) {
ok = false;
break;
}
} else if ((param->element->namens.empty() && node->ns) ||
(!param->element->namens.empty() &&
node->ns == nullptr)) {
ok = false;
break;
}
} else if (param->paramName != (char*)node->name) {
ok = false;
break;
}
node = node->next;
}
if (ok /*&& node == nullptr*/) {
return tmp;
}
}
}
}
}
}
return std::shared_ptr<sdlFunction>();
}
static std::shared_ptr<sdlFunction>
get_function(sdl *sdl, const char *function_name) {
if (sdl) {
String lowered = HHVM_FN(strtolower)(function_name);
sdlFunctionMap::iterator iter = sdl->functions.find(lowered.data());
if (iter == sdl->functions.end()) {
iter = sdl->requests.find(lowered.data());
if (iter == sdl->requests.end()) {
return std::shared_ptr<sdlFunction>();
}
}
return iter->second;
}
return std::shared_ptr<sdlFunction>();
}
static std::shared_ptr<sdlFunction>
find_function(sdl *sdl, xmlNodePtr func, String &function_name) {
auto function = get_function(sdl, (char*)func->name);
if (function && function->binding &&
function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
if (fnb->style == SOAP_DOCUMENT) {
if (func->children != nullptr || !function->requestParameters.empty()) {
function.reset();
}
}
}
if (sdl != nullptr && function == nullptr) {
function = get_doc_function(sdl, func);
}
if (function != nullptr) {
function_name = String(function->functionName);
} else {
function_name = String((char *)func->name, CopyString);
}
return function;
}
static std::shared_ptr<sdlFunction> deserialize_function_call
(sdl *sdl, xmlDocPtr request, const char* actor, String &function_name,
Array ¶meters, int &version, Array &headers) {
USE_SOAP_GLOBAL;
char* envelope_ns = nullptr;
xmlNodePtr trav,env,head,body,func;
xmlAttrPtr attr;
std::shared_ptr<sdlFunction> function;
encode_reset_ns();
/* Get <Envelope> element */
env = nullptr;
trav = request->children;
while (trav != nullptr) {
if (trav->type == XML_ELEMENT_NODE) {
if (env == nullptr &&
node_is_equal_ex(trav,"Envelope",SOAP_1_1_ENV_NAMESPACE)) {
env = trav;
version = SOAP_1_1;
envelope_ns = SOAP_1_1_ENV_NAMESPACE;
SOAP_GLOBAL(soap_version) = SOAP_1_1;
} else if (env == nullptr &&
node_is_equal_ex(trav,"Envelope",SOAP_1_2_ENV_NAMESPACE)) {
env = trav;
version = SOAP_1_2;
envelope_ns = SOAP_1_2_ENV_NAMESPACE;
SOAP_GLOBAL(soap_version) = SOAP_1_2;
} else {
throw_soap_server_fault("VersionMismatch", "Wrong Version");
}
}
trav = trav->next;
}
if (env == nullptr) {
throw_soap_server_fault
("Client", "looks like we got XML without \"Envelope\" element");
}
attr = env->properties;
while (attr != nullptr) {
if (attr->ns == nullptr) {
throw_soap_server_fault("Client", "A SOAP Envelope element cannot have "
"non Namespace qualified attributes");
} else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) {
if (version == SOAP_1_2) {
throw_soap_server_fault("Client", "encodingStyle cannot be specified "
"on the Envelope");
} else if (strcmp((char*)attr->children->content,
SOAP_1_1_ENC_NAMESPACE) != 0) {
throw_soap_server_fault("Client", "Unknown data encoding style");
}
}
attr = attr->next;
}
/* Get <Header> element */
head = nullptr;
trav = env->children;
while (trav != nullptr && trav->type != XML_ELEMENT_NODE) {
trav = trav->next;
}
if (trav != nullptr && node_is_equal_ex(trav,"Header",envelope_ns)) {
head = trav;
trav = trav->next;
}
/* Get <Body> element */
body = nullptr;
while (trav != nullptr && trav->type != XML_ELEMENT_NODE) {
trav = trav->next;
}
if (trav != nullptr && node_is_equal_ex(trav,"Body",envelope_ns)) {
body = trav;
trav = trav->next;
}
while (trav != nullptr && trav->type != XML_ELEMENT_NODE) {
trav = trav->next;
}
if (body == nullptr) {
throw_soap_server_fault("Client", "Body must be present in a "
"SOAP envelope");
}
attr = body->properties;
while (attr != nullptr) {
if (attr->ns == nullptr) {
if (version == SOAP_1_2) {
throw_soap_server_fault("Client", "A SOAP Body element cannot have non"
" Namespace qualified attributes");
}
} else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) {
if (version == SOAP_1_2) {
throw_soap_server_fault("Client", "encodingStyle cannot be specified "
"on the Body");
} else if (strcmp((char*)attr->children->content,
SOAP_1_1_ENC_NAMESPACE) != 0) {
throw_soap_server_fault("Client", "Unknown data encoding style");
}
}
attr = attr->next;
}
if (trav != nullptr && version == SOAP_1_2) {
throw_soap_server_fault("Client", "A SOAP 1.2 envelope can contain only "
"Header and Body");
}
func = nullptr;
trav = body->children;
while (trav != nullptr) {
if (trav->type == XML_ELEMENT_NODE) {
/*
if (func != nullptr) {
throw_soap_server_fault("Client", "looks like we got \"Body\" with "
"several functions call", nullptr, nullptr, nullptr);
}
*/
func = trav;
break; /* FIXME: the rest of body is ignored */
}
trav = trav->next;
}
if (func == nullptr) {
function = get_doc_function(sdl, nullptr);
if (function != nullptr) {
function_name = String(function->functionName);
} else {
throw_soap_server_fault
("Client", "looks like we got \"Body\" without function call");
}
} else {
if (version == SOAP_1_1) {
attr = get_attribute_ex(func->properties,"encodingStyle",
SOAP_1_1_ENV_NAMESPACE);
if (attr && strcmp((char*)attr->children->content,
SOAP_1_1_ENC_NAMESPACE) != 0) {
throw_soap_server_fault("Client","Unknown Data Encoding Style");
}
} else {
attr = get_attribute_ex(func->properties,"encodingStyle",
SOAP_1_2_ENV_NAMESPACE);
if (attr && strcmp((char*)attr->children->content,
SOAP_1_2_ENC_NAMESPACE) != 0) {
throw_soap_server_fault
("DataEncodingUnknown","Unknown Data Encoding Style");
}
}
function = find_function(sdl, func, function_name);
if (sdl != nullptr && function == nullptr) {
if (version == SOAP_1_2) {
throw_soap_server_fault
("rpc:ProcedureNotPresent","Procedure not present");
} else {
throw SoapException("Procedure '%s' not present", func->name);
}
}
}
headers = Array::CreateVec();
if (head) {
attr = head->properties;
while (attr != nullptr) {
if (attr->ns == nullptr) {
throw_soap_server_fault("Client", "A SOAP Header element cannot have "
"non Namespace qualified attributes");
} else if (attr_is_equal_ex(attr,"encodingStyle",
SOAP_1_2_ENV_NAMESPACE)) {
if (version == SOAP_1_2) {
throw_soap_server_fault("Client", "encodingStyle cannot be specified"
" on the Header");
} else if (strcmp((char*)attr->children->content,
SOAP_1_1_ENC_NAMESPACE) != 0) {
throw_soap_server_fault("Client", "Unknown data encoding style");
}
}
attr = attr->next;
}
trav = head->children;
while (trav != nullptr) {
if (trav->type == XML_ELEMENT_NODE) {
xmlNodePtr hdr_func = trav;
int mustUnderstand = 0;
if (version == SOAP_1_1) {
attr = get_attribute_ex(hdr_func->properties,"encodingStyle",
SOAP_1_1_ENV_NAMESPACE);
if (attr && strcmp((char*)attr->children->content,
SOAP_1_1_ENC_NAMESPACE) != 0) {
throw_soap_server_fault("Client","Unknown Data Encoding Style");
}
attr = get_attribute_ex(hdr_func->properties,"actor",envelope_ns);
if (attr != nullptr) {
if (strcmp((char*)attr->children->content,
SOAP_1_1_ACTOR_NEXT) != 0 &&
(actor == nullptr ||
strcmp((char*)attr->children->content,actor) != 0)) {
goto ignore_header;
}
}
} else if (version == SOAP_1_2) {
attr = get_attribute_ex(hdr_func->properties,"encodingStyle",
SOAP_1_2_ENV_NAMESPACE);
if (attr && strcmp((char*)attr->children->content,
SOAP_1_2_ENC_NAMESPACE) != 0) {
throw_soap_server_fault
("DataEncodingUnknown","Unknown Data Encoding Style");
}
attr = get_attribute_ex(hdr_func->properties,"role",envelope_ns);
if (attr != nullptr) {
if (strcmp((char*)attr->children->content,
SOAP_1_2_ACTOR_UNLIMATERECEIVER) != 0 &&
strcmp((char*)attr->children->content,
SOAP_1_2_ACTOR_NEXT) != 0 &&
(actor == nullptr ||
strcmp((char*)attr->children->content,actor) != 0)) {
goto ignore_header;
}
}
}
attr = get_attribute_ex(hdr_func->properties,"mustUnderstand",
envelope_ns);
if (attr) {
if (strcmp((char*)attr->children->content,"1") == 0 ||
strcmp((char*)attr->children->content,"true") == 0) {
mustUnderstand = 1;
} else if (strcmp((char*)attr->children->content,"0") == 0 ||
strcmp((char*)attr->children->content,"false") == 0) {
mustUnderstand = 0;
} else {
throw_soap_server_fault("Client",
"mustUnderstand value is not boolean");
}
}
auto h = req::make<soapHeader>();
h->function = find_function(sdl, hdr_func, h->function_name).get();
h->mustUnderstand = mustUnderstand;
h->hdr = nullptr;
if (!h->function && sdl && function && function->binding &&
function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
if (!fnb->input.headers.empty()) {
string key;
if (hdr_func->ns) {
key += (char*)hdr_func->ns->href;
key += ':';
}
key += (std::string)h->function_name;
sdlSoapBindingFunctionHeaderMap::iterator iter =
fnb->input.headers.find(key);
if (iter != fnb->input.headers.end()) {
h->hdr = iter->second.get();
}
}
}
if (h->hdr) {
h->parameters.append(master_to_zval(h->hdr->encode, hdr_func));
} else {
if (h->function && h->function->binding &&
h->function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = h->function->bindingAttributes;
if (fnb->style == SOAP_RPC) {
hdr_func = hdr_func->children;
}
}
deserialize_parameters(hdr_func, h->function, h->parameters);
}
headers.append(Variant(std::move(h)));
}
ignore_header:
trav = trav->next;
}
}
if (function && function->binding &&
function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
if (fnb->style == SOAP_RPC) {
func = func->children;
}
} else {
func = func->children;
}
deserialize_parameters(func, function.get(), parameters);
encode_finish();
return function;
}
static int serialize_response_call2(xmlNodePtr body, sdlFunction *function,
const char *function_name, const char *uri,
Variant &ret, int version, int main) {
xmlNodePtr method = nullptr, param;
sdlParamPtr parameter;
int param_count;
int style, use;
xmlNsPtr ns = nullptr;
if (function && function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
style = fnb->style;
use = fnb->output.use;
ns = encode_add_ns(body, fnb->output.ns.c_str());
if (style == SOAP_RPC) {
if (!function->responseName.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->responseName.c_str()), nullptr);
} else if (!function->responseParameters.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->functionName.c_str()), nullptr);
}
}
} else {
style = main?SOAP_RPC:SOAP_DOCUMENT;
use = main?SOAP_ENCODED:SOAP_LITERAL;
if (style == SOAP_RPC) {
ns = encode_add_ns(body, uri);
method = xmlNewChild(body, ns, BAD_CAST(function_name), nullptr);
}
}
if (function) {
param_count = function->responseParameters.size();
} else {
param_count = 1;
}
if (param_count == 1) {
parameter = get_param(function, nullptr, 0, true);
if (style == SOAP_RPC) {
xmlNode *rpc_result;
if (main && version == SOAP_1_2) {
xmlNs *rpc_ns = xmlNewNs(body, BAD_CAST(RPC_SOAP12_NAMESPACE),
BAD_CAST(RPC_SOAP12_NS_PREFIX));
rpc_result = xmlNewChild(method, rpc_ns, BAD_CAST("result"), nullptr);
param = serialize_parameter(parameter, ret, 0, "return", use, method);
xmlNodeSetContent(rpc_result,param->name);
} else {
param = serialize_parameter(parameter, ret, 0, "return", use, method);
}
} else {
param = serialize_parameter(parameter, ret, 0, "return", use, body);
if (function && function->binding->bindingType == BINDING_SOAP) {
if (parameter && parameter->element) {
ns = encode_add_ns(param, parameter->element->namens.c_str());
xmlNodeSetName(param, BAD_CAST(parameter->element->name.c_str()));
xmlSetNs(param, ns);
}
} else if (strcmp((char*)param->name,"return") == 0) {
ns = encode_add_ns(param, uri);
xmlNodeSetName(param, BAD_CAST(function_name));
xmlSetNs(param, ns);
}
}
} else if (param_count > 1 && ret.isArray()) {
Array arr = ret.toArray();
int i = 0;
for (ArrayIter iter(arr); iter; ++iter, ++i) {
Variant data = iter.second();
Variant key = iter.first();
String param_name;
int64_t param_index = i;
if (key.isString()) {
param_name = key.toString();
} else {
param_index = key.toInt64();
}
parameter = get_param(function, param_name.c_str(), param_index, true);
if (style == SOAP_RPC) {
param = serialize_parameter(parameter, data, i, param_name.data(),
use, method);
} else {
param = serialize_parameter(parameter, data, i, param_name.data(),
use, body);
if (function && function->binding->bindingType == BINDING_SOAP) {
if (parameter && parameter->element) {
ns = encode_add_ns(param, parameter->element->namens.c_str());
xmlNodeSetName(param, BAD_CAST(parameter->element->name.c_str()));
xmlSetNs(param, ns);
}
}
}
}
}
if (use == SOAP_ENCODED && version == SOAP_1_2 && method) {
xmlSetNsProp(method, body->ns, BAD_CAST("encodingStyle"),
BAD_CAST(SOAP_1_2_ENC_NAMESPACE));
}
return use;
}
const StaticString s_headerfault("headerfault");
static xmlDocPtr serialize_response_call(
std::shared_ptr<sdlFunction> function,
const char *function_name,
const char *uri, Variant &ret,
const Array& headers, int version) {
xmlNodePtr envelope = nullptr, body, param;
xmlNsPtr ns = nullptr;
int use = SOAP_LITERAL;
xmlNodePtr head = nullptr;
encode_reset_ns();
xmlDocPtr doc = xmlNewDoc(BAD_CAST("1.0"));
doc->charset = XML_CHAR_ENCODING_UTF8;
doc->encoding = xmlCharStrdup("UTF-8");
if (version == SOAP_1_1) {
envelope = xmlNewDocNode(doc, nullptr, BAD_CAST("Envelope"), nullptr);
ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_1_ENV_NAMESPACE),
BAD_CAST(SOAP_1_1_ENV_NS_PREFIX));
xmlSetNs(envelope,ns);
} else if (version == SOAP_1_2) {
envelope = xmlNewDocNode(doc, nullptr, BAD_CAST("Envelope"), nullptr);
ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENV_NAMESPACE),
BAD_CAST(SOAP_1_2_ENV_NS_PREFIX));
xmlSetNs(envelope,ns);
} else {
throw_soap_server_fault("Server", "Unknown SOAP version");
}
xmlDocSetRootElement(doc, envelope);
if (ret.isObject() &&
ret.toObject()->instanceof(SystemLib::s_SoapFaultClass)) {
ObjectData* obj = ret.getObjectData();
char *detail_name;
std::shared_ptr<sdlFault> fault;
string fault_ns;
if (!headers.empty()) {
encodePtr hdr_enc;
int hdr_use = SOAP_LITERAL;
Variant hdr_ret = obj->o_get("headerfault");
auto h = cast<soapHeader>(headers[0]);
const char *hdr_ns = h->hdr ? h->hdr->ns.c_str() : nullptr;
const char *hdr_name = h->function_name.data();
head = xmlNewChild(envelope, ns, BAD_CAST("Header"), nullptr);
if (hdr_ret.isObject() &&
hdr_ret.toObject().instanceof(SoapHeader::getClass())) {
const SoapHeader *ht = Native::data<SoapHeader>(hdr_ret.toObject());
string key;
if (!ht->m_namespace.empty()) {
key += ht->m_namespace.data();
key += ':';
hdr_ns = ht->m_namespace.data();
}
if (!ht->m_name.empty()) {
key += ht->m_name.data();
hdr_name = ht->m_name.data();
}
if (h->hdr) {
sdlSoapBindingFunctionHeaderMap::iterator iter =
h->hdr->headerfaults.find(key);
if (iter != h->hdr->headerfaults.end()) {
auto hdr = iter->second;
hdr_enc = hdr->encode;
hdr_use = hdr->use;
}
}
hdr_ret = ht->m_data;
obj->setProp(nullptr, s_headerfault.get(), *hdr_ret.asTypedValue());
}
if (h->function) {
if (serialize_response_call2(head, h->function,
h->function_name.data(), uri,
hdr_ret, version, 0) == SOAP_ENCODED) {
obj->setProp(nullptr, s_headerfault.get(), *hdr_ret.asTypedValue());
use = SOAP_ENCODED;
}
} else {
xmlNodePtr xmlHdr = master_to_xml(hdr_enc, hdr_ret, hdr_use, head);
if (hdr_name) {
xmlNodeSetName(xmlHdr, BAD_CAST(hdr_name));
}
if (hdr_ns) {
xmlNsPtr nsptr = encode_add_ns(xmlHdr, hdr_ns);
xmlSetNs(xmlHdr, nsptr);
}
}
}
body = xmlNewChild(envelope, ns, BAD_CAST("Body"), nullptr);
param = xmlNewChild(body, ns, BAD_CAST("Fault"), nullptr);
fault_ns = obj->o_get("faultcodens").toString().data();
use = SOAP_LITERAL;
if (!obj->o_get("_name").toString().empty()) {
if (function) {
sdlFaultMap::iterator iter =
function->faults.find(obj->o_get("_name").toString().data());
if (iter != function->faults.end()) {
fault = iter->second;
if (function->binding &&
function->binding->bindingType == BINDING_SOAP &&
fault->bindingAttributes) {
sdlSoapBindingFunctionFaultPtr fb = fault->bindingAttributes;
use = fb->use;
if (fault_ns.empty()) {
fault_ns = fb->ns;
}
}
}
}
} else if (function && function->faults.size() == 1) {
fault = function->faults[0];
if (function->binding &&
function->binding->bindingType == BINDING_SOAP &&
fault->bindingAttributes) {
sdlSoapBindingFunctionFaultPtr fb = fault->bindingAttributes;
use = fb->use;
if (fault_ns.empty()) {
fault_ns = fb->ns;
}
}
}
if (fault_ns.empty() && fault && fault->details.size() == 1) {
sdlParamPtr sparam = fault->details[0];
if (sparam->element) {
fault_ns = sparam->element->namens;
}
}
if (version == SOAP_1_1) {
if (!obj->o_get("faultcode").toString().empty()) {
xmlNodePtr node = xmlNewNode(nullptr, BAD_CAST("faultcode"));
String str = StringUtil::HtmlEncode(obj->o_get("faultcode").toString(),
StringUtil::QuoteStyle::Double,
"UTF-8", true, true);
xmlAddChild(param, node);
if (!fault_ns.empty()) {
xmlNsPtr nsptr = encode_add_ns(node, fault_ns.c_str());
xmlChar *code = xmlBuildQName(BAD_CAST(str.data()), nsptr->prefix,
nullptr, 0);
xmlNodeSetContent(node, code);
xmlFree(code);
} else {
xmlNodeSetContentLen(node, BAD_CAST(str.data()), str.size());
}
}
if (!obj->o_get("faultstring").toString().empty()) {
xmlNodePtr node = master_to_xml(
get_conversion(dataTypeToSoap(KindOfString)),
obj->o_get("faultstring"), SOAP_LITERAL,
param
);
xmlNodeSetName(node, BAD_CAST("faultstring"));
}
if (!obj->o_get("faultactor").toString().empty()) {
xmlNodePtr node = master_to_xml(
get_conversion(dataTypeToSoap(KindOfString)),
obj->o_get("faultactor"), SOAP_LITERAL,
param
);
xmlNodeSetName(node, BAD_CAST("faultactor"));
}
detail_name = "detail";
} else {
if (!obj->o_get("faultcode").toString().empty()) {
xmlNodePtr node = xmlNewChild(param, ns, BAD_CAST("Code"), nullptr);
String str = StringUtil::HtmlEncode(obj->o_get("faultcode").toString(),
StringUtil::QuoteStyle::Double,
"UTF-8", true, true);
node = xmlNewChild(node, ns, BAD_CAST("Value"), nullptr);
if (!fault_ns.empty()) {
xmlNsPtr nsptr = encode_add_ns(node, fault_ns.c_str());
xmlChar *code = xmlBuildQName(BAD_CAST(str.data()), nsptr->prefix,
nullptr, 0);
xmlNodeSetContent(node, code);
xmlFree(code);
} else {
xmlNodeSetContentLen(node, BAD_CAST(str.data()), str.size());
}
}
if (!obj->o_get("faultstring").toString().empty()) {
xmlNodePtr node = xmlNewChild(param, ns, BAD_CAST("Reason"), nullptr);
node = master_to_xml(
get_conversion(dataTypeToSoap(KindOfString)),
obj->o_get("faultstring"),
SOAP_LITERAL, node
);
xmlNodeSetName(node, BAD_CAST("Text"));
xmlSetNs(node, ns);
}
detail_name = SOAP_1_2_ENV_NS_PREFIX":Detail";
}
if (fault && fault->details.size() == 1) {
xmlNodePtr node;
Variant detail;
sdlParamPtr sparam;
xmlNodePtr x;
if (!obj->o_get("detail").isNull()) {
detail = obj->o_get("detail");
}
node = xmlNewNode(nullptr, BAD_CAST(detail_name));
xmlAddChild(param, node);
sparam = fault->details[0];
if (detail.isObject() && sparam->element) {
Variant prop = detail.toObject()->o_get(sparam->element->name.c_str());
if (!prop.isNull()) {
detail = prop;
}
}
x = serialize_parameter(sparam, detail, 1, nullptr, use, node);
if (function &&
function->binding &&
function->binding->bindingType == BINDING_SOAP &&
function->bindingAttributes) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
if (fnb->style == SOAP_RPC && !sparam->element) {
if (fault->bindingAttributes) {
sdlSoapBindingFunctionFaultPtr fb = fault->bindingAttributes;
if (!fb->ns.empty()) {
xmlNsPtr ns2 = encode_add_ns(x, fb->ns.c_str());
xmlSetNs(x, ns2);
}
}
} else {
if (sparam->element) {
xmlNsPtr ns2 = encode_add_ns(x, sparam->element->namens.c_str());
xmlNodeSetName(x, BAD_CAST(sparam->element->name.c_str()));
xmlSetNs(x, ns2);
}
}
}
if (use == SOAP_ENCODED && version == SOAP_1_2) {
xmlSetNsProp(x, envelope->ns, BAD_CAST("encodingStyle"),
BAD_CAST(SOAP_1_2_ENC_NAMESPACE));
}
} else if (!obj->o_get("detail").isNull()) {
serialize_zval(obj->o_get("detail"), sdlParamPtr(), detail_name, use, param);
}
} else {
if (!headers.empty()) {
head = xmlNewChild(envelope, ns, BAD_CAST("Header"), nullptr);
for (ArrayIter iter(headers); iter; ++iter) {
auto h = cast<soapHeader>(iter.second());
if (!h->retval.isNull()) {
encodePtr hdr_enc;
int hdr_use = SOAP_LITERAL;
Variant &hdr_ret = h->retval;
const char *hdr_ns = h->hdr ? h->hdr->ns.c_str() : nullptr;
const char *hdr_name = h->function_name.data();
if (h->retval.isObject() &&
h->retval.toObject().instanceof(SoapHeader::getClass())) {
const SoapHeader *ht = Native::data<SoapHeader>(
h->retval.toObject());
string key;
if (!ht->m_namespace.empty()) {
key += ht->m_namespace.data();
key += ':';
hdr_ns = ht->m_namespace.data();
}
if (!ht->m_name.empty()) {
key += ht->m_name.data();
hdr_name = ht->m_name.data();
}
if (function && function->binding &&
function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
sdlSoapBindingFunctionHeaderMap::iterator iter =
fnb->output.headers.find(key);
if (iter != fnb->output.headers.end()) {
hdr_enc = iter->second->encode;
hdr_use = iter->second->use;
}
}
hdr_ret = ht->m_data;
}
if (h->function) {
if (serialize_response_call2(head, h->function,
h->function_name.data(), uri, hdr_ret,
version, 0) == SOAP_ENCODED) {
use = SOAP_ENCODED;
}
} else {
xmlNodePtr xmlHdr = master_to_xml(hdr_enc, hdr_ret, hdr_use, head);
if (hdr_name) {
xmlNodeSetName(xmlHdr, BAD_CAST(hdr_name));
}
if (hdr_ns) {
xmlNsPtr nsptr = encode_add_ns(xmlHdr,hdr_ns);
xmlSetNs(xmlHdr, nsptr);
}
}
}
}
if (head->children == nullptr) {
xmlUnlinkNode(head);
xmlFreeNode(head);
}
}
body = xmlNewChild(envelope, ns, BAD_CAST("Body"), nullptr);
if (serialize_response_call2(body, function.get(), function_name, uri, ret,
version, 1) == SOAP_ENCODED) {
use = SOAP_ENCODED;
}
}
if (use == SOAP_ENCODED) {
xmlNewNs(envelope, BAD_CAST(XSD_NAMESPACE), BAD_CAST(XSD_NS_PREFIX));
if (version == SOAP_1_1) {
xmlNewNs(envelope, BAD_CAST(SOAP_1_1_ENC_NAMESPACE),
BAD_CAST(SOAP_1_1_ENC_NS_PREFIX));
xmlSetNsProp(envelope, envelope->ns, BAD_CAST("encodingStyle"),
BAD_CAST(SOAP_1_1_ENC_NAMESPACE));
} else if (version == SOAP_1_2) {
xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENC_NAMESPACE),
BAD_CAST(SOAP_1_2_ENC_NS_PREFIX));
}
}
encode_finish();
if (function && function->responseName.empty() &&
body->children == nullptr && head == nullptr) {
xmlFreeDoc(doc);
return nullptr;
}
return doc;
}
static void function_to_string(std::shared_ptr<sdlFunction> function,
StringBuffer &sb) {
sdlParamPtr param;
sdlParamVec &res = function->responseParameters;
if (!res.empty()) {
if (res.size() == 1) {
param = res[0];
if (param->encode && !param->encode->details.type_str.empty()) {
sb.append(param->encode->details.type_str);
sb.append(' ');
} else {
sb.append("UNKNOWN ");
}
} else {
sb.append("list(");
bool started = false;
for (unsigned int i = 0; i < res.size(); i++) {
param = res[i];
if (started) {
sb.append(", ");
} else {
started = true;
}
if (param->encode && !param->encode->details.type_str.empty()) {
sb.append(param->encode->details.type_str);
} else {
sb.append("UNKNOWN");
}
sb.append(" $");
sb.append(param->paramName);
}
sb.append(") ");
}
} else {
sb.append("void ");
}
sb.append(function->functionName);
sb.append('(');
sdlParamVec &req = function->requestParameters;
bool started = false;
for (unsigned int i = 0; i < req.size(); i++) {
param = req[i];
if (started) {
sb.append(", ");
} else {
started = true;
}
if (param->encode && !param->encode->details.type_str.empty()) {
sb.append(param->encode->details.type_str);
} else {
sb.append("UNKNOWN");
}
sb.append(" $");
sb.append(param->paramName);
}
sb.append(')');
}
static void type_to_string(sdlType *type, StringBuffer &buf, int level) {
StringBuffer sbspaces;
for (int i = 0; i < level;i++) {
sbspaces.append(' ');
}
String spaces = sbspaces.detach();
buf.append(spaces);
switch (type->kind) {
case XSD_TYPEKIND_SIMPLE:
if (type->encode) {
buf.append(type->encode->details.type_str);
buf.append(' ');
} else {
buf.append("anyType ");
}
buf.append(type->name);
break;
case XSD_TYPEKIND_LIST:
buf.append("list ");
buf.append(type->name);
if (!type->elements.empty()) {
buf.append(" {");
buf.append(type->elements[0]->name);
buf.append('}');
}
break;
case XSD_TYPEKIND_UNION:
buf.append("union ");
buf.append(type->name);
if (!type->elements.empty()) {
bool first = true;
buf.append(" {");
for (unsigned int i = 0; i < type->elements.size(); i++) {
if (!first) {
buf.append(',');
first = false;
}
buf.append(type->elements[i]->name);
}
buf.append('}');
}
break;
case XSD_TYPEKIND_COMPLEX:
case XSD_TYPEKIND_RESTRICTION:
case XSD_TYPEKIND_EXTENSION:
if (type->encode && isSoapArrayType(type->encode->details.type)) {
sdlAttributeMap::iterator iter;
sdlExtraAttributeMap::iterator iterExtra;
if (!type->attributes.empty() &&
((iter = type->attributes.find(SOAP_1_1_ENC_NAMESPACE":arrayType"))
!= type->attributes.end())) {
if ((iterExtra = iter->second->extraAttributes.find
(WSDL_NAMESPACE":arrayType"))
!= iter->second->extraAttributes.end()) {
auto ext = iterExtra->second;
const char *end = strchr(ext->val.c_str(), '[');
int len;
if (end == nullptr) {
len = ext->val.size();
} else {
len = end- ext->val.c_str();
}
if (len == 0) {
buf.append("anyType");
} else {
buf.append(ext->val.c_str(), len);
}
buf.append(' ');
buf.append(type->name);
if (end) {
buf.append(end);
}
}
} else {
sdlTypePtr elementType;
sdlAttributeMap::iterator sdlIter;
sdlExtraAttributeMap::iterator sdlIterExtra;
if (!type->attributes.empty() &&
(
(sdlIter = type->attributes.find(SOAP_1_2_ENC_NAMESPACE":itemType"))
!= type->attributes.end())) {
if ((sdlIterExtra = sdlIter->second->extraAttributes.find
(WSDL_NAMESPACE":itemType"))
!= sdlIter->second->extraAttributes.end()) {
auto ext = sdlIterExtra->second;
buf.append(ext->val);
buf.append(' ');
}
} else if (type->elements.size() == 1 &&
(elementType = type->elements[0]) != nullptr &&
elementType->encode &&
!elementType->encode->details.type_str.empty()) {
buf.append(elementType->encode->details.type_str);
buf.append(' ');
} else {
buf.append("anyType ");
}
buf.append(type->name);
if (!type->attributes.empty() &&
(
(sdlIter = type->attributes.find(SOAP_1_2_ENC_NAMESPACE":arraySize"))
!= type->attributes.end())) {
if ((sdlIterExtra = sdlIter->second->extraAttributes.find
(WSDL_NAMESPACE":itemType"))
!= sdlIter->second->extraAttributes.end()) {
auto ext = sdlIterExtra->second;
buf.append('[');
buf.append(ext->val);
buf.append(']');
}
} else {
buf.append("[]");
}
}
} else {
buf.append("struct ");
buf.append(type->name);
buf.append(' ');
buf.append("{\n");
if ((type->kind == XSD_TYPEKIND_RESTRICTION ||
type->kind == XSD_TYPEKIND_EXTENSION) && type->encode) {
encodePtr enc = type->encode;
while (enc && enc->details.sdl_type &&
enc != enc->details.sdl_type->encode &&
enc->details.sdl_type->kind != XSD_TYPEKIND_SIMPLE &&
enc->details.sdl_type->kind != XSD_TYPEKIND_LIST &&
enc->details.sdl_type->kind != XSD_TYPEKIND_UNION) {
enc = enc->details.sdl_type->encode;
}
if (enc) {
buf.append(spaces);
buf.append(' ');
buf.append(type->encode->details.type_str);
buf.append(" _;\n");
}
}
if (type->model) {
model_to_string(type->model, buf, level+1);
}
if (!type->attributes.empty()) {
for (sdlAttributeMap::iterator iter = type->attributes.begin();
iter != type->attributes.end(); ++iter) {
sdlAttributePtr attr = iter->second;
buf.append(spaces);
buf.append(' ');
if (attr->encode && !attr->encode->details.type_str.empty()) {
buf.append(attr->encode->details.type_str);
buf.append(' ');
} else {
buf.append("UNKNOWN ");
}
buf.append(attr->name);
buf.append(";\n");
}
}
buf.append(spaces);
buf.append('}');
}
break;
default:
break;
}
}
static void model_to_string(sdlContentModelPtr model, StringBuffer &buf,
int level) {
switch (model->kind) {
case XSD_CONTENT_ELEMENT:
type_to_string(model->u_element, buf, level);
buf.append(";\n");
break;
case XSD_CONTENT_ANY:
for (int i = 0;i < level;i++) {
buf.append(' ');
}
buf.append("<anyXML> any;\n");
break;
case XSD_CONTENT_SEQUENCE:
case XSD_CONTENT_ALL:
case XSD_CONTENT_CHOICE: {
for (unsigned int i = 0; i < model->u_content.size(); i++) {
sdlContentModelPtr tmp = model->u_content[i];
model_to_string(tmp, buf, level);
}
break;
}
case XSD_CONTENT_GROUP:
model_to_string(model->u_group->model, buf, level);
default:
break;
}
}
///////////////////////////////////////////////////////////////////////////////
// soap fault functions
const StaticString
s_HTTP_USER_AGENT("HTTP_USER_AGENT"),
s__SERVER("_SERVER"),
s_Shockwave_Flash("Shockwave Flash");
static void send_soap_server_fault(
std::shared_ptr<sdlFunction> function,
Variant fault,
const req::ptr<soapHeader>& hdr) {
USE_SOAP_GLOBAL;
bool use_http_error_status = true;
if (php_global(s__SERVER).toArray()[s_HTTP_USER_AGENT].toString() ==
s_Shockwave_Flash) {
use_http_error_status = false;
}
if (use_http_error_status) {
header_if_not_sent("HTTP/1.1 500 Internal Service Error");
}
output_xml_header(SOAP_GLOBAL(soap_version));
Array headers;
if (hdr) headers.append(Resource(hdr));
xmlDocPtr doc_return = serialize_response_call
(function, nullptr, nullptr, fault, headers, SOAP_GLOBAL(soap_version));
HHVM_FN(ob_end_clean)(); // dump all buffered output
xmlChar *buf; int size;
xmlDocDumpMemory(doc_return, &buf, &size);
if (buf) {
g_context->write(String((const char *)buf, size, CopyString));
xmlFree(buf);
}
xmlFreeDoc(doc_return);
}
static void
send_soap_server_fault(std::shared_ptr<sdlFunction> /*function*/, Exception& e,
const req::ptr<soapHeader>& /*hdr*/) {
USE_SOAP_GLOBAL;
if (SOAP_GLOBAL(use_soap_error_handler)) {
send_soap_server_fault(
std::shared_ptr<sdlFunction>(), create_soap_fault(e), nullptr);
} else {
throw_object(create_soap_fault(e)); // assuming we are in "catch"
}
}
static void throw_soap_server_fault(const char* code, const char* fault) {
send_soap_server_fault(
std::shared_ptr<sdlFunction>(), create_soap_fault(code, fault),
nullptr);
throw ExitException(1);
}
bool HHVM_FUNCTION(use_soap_error_handler,
bool handler /* = true */) {
USE_SOAP_GLOBAL;
bool old = SOAP_GLOBAL(use_soap_error_handler);
SOAP_GLOBAL(use_soap_error_handler) = handler;
return old;
}
bool HHVM_FUNCTION(is_soap_fault,
const Variant& fault) {
return fault.isObject() &&
fault.getObjectData()->instanceof(SystemLib::s_SoapFaultClass);
}
int64_t HHVM_FUNCTION(_soap_active_version) {
USE_SOAP_GLOBAL;
return SOAP_GLOBAL(soap_version);
}
///////////////////////////////////////////////////////////////////////////////
// class SoapServer
const StaticString SoapServer::s_className("SoapServer");
SoapServer::SoapServer() :
m_type(SOAP_FUNCTIONS),
m_version(SOAP_1_1),
m_sdl(nullptr),
m_encoding(nullptr),
m_typemap(nullptr),
m_features(0),
m_send_errors(1) {
}
const StaticString
s_soap_version("soap_version"),
s_uri("uri"),
s_actor("actor"),
s_encoding("encoding"),
s_classmap("classmap"),
s_typemap("typemap"),
s_features("features"),
s_cache_wsdl("cache_wsdl"),
s_send_errors("send_errors"),
s_location("location"),
s_style("style"),
s_use("use"),
s_stream_context("stream_context"),
s_login("login"),
s_password("password"),
s_authentication("authentication"),
s_proxy_host("proxy_host"),
s_proxy_port("proxy_port"),
s_proxy_login("proxy_login"),
s_proxy_password("proxy_password"),
s_proxy_ssl_cert_path("proxy_ssl_cert_path"),
s_proxy_ssl_key_path("proxy_ssl_key_path"),
s_proxy_ssl_ca_bundle("proxy_ssl_ca_bundle"),
s_trace("trace"),
s_exceptions("exceptions"),
s_compression("compression"),
s_connection_timeout("connection_timeout"),
s_user_agent("user_agent"),
s_soapaction("soapaction"),
s_ssl_method("ssl_method");
void HHVM_METHOD(SoapServer, __construct,
const Variant& wsdl,
const Array& options /* = null_array */) {
auto* data = Native::data<SoapServer>(this_);
USE_SOAP_GLOBAL;
SoapServerScope ss(this_);
try {
if (!wsdl.isString() && !wsdl.isNull()) {
throw SoapException("Invalid parameters");
}
data->m_send_errors = 1;
int64_t cache_wsdl = SOAP_GLOBAL(cache);
int version = SOAP_1_1;
Array typemap_ht;
if (!options.empty()) {
if (options[s_soap_version].isInteger()) {
int64_t tmp = options[s_soap_version].toInt64();
if (tmp == SOAP_1_1 || tmp == SOAP_1_2) {
version = tmp;
}
}
if (options[s_uri].isString()) {
data->m_uri = options[s_uri].toString();
} else if (wsdl.isNull()) {
throw SoapException("'uri' option is required in nonWSDL mode");
}
if (options[s_actor].isString()) {
data->m_actor = options[s_actor].toString();
}
if (options[s_encoding].isString()) {
String tmp = options[s_encoding].toString();
data->m_encoding = xmlFindCharEncodingHandler(tmp.data());
if (data->m_encoding == nullptr) {
throw SoapException("Invalid 'encoding' option - '%s'", tmp.data());
}
s_soap_data->register_encoding(data->m_encoding);
}
if (options[s_classmap].isArray()) {
data->m_classmap = options[s_classmap].toArray();
}
if (options[s_typemap].isArray()) {
typemap_ht = options[s_typemap].toArray();
}
if (options[s_features].isInteger()) {
data->m_features = options[s_features].toInt64();
}
if (options[s_cache_wsdl].isInteger()) {
cache_wsdl = options[s_cache_wsdl].toInt64();
}
if (options[s_send_errors].isInteger() ||
options[s_send_errors].is(KindOfBoolean)) {
data->m_send_errors = options[s_send_errors].toInt64();
}
} else if (wsdl.isNull()) {
throw SoapException("'uri' option is required in nonWSDL mode");
}
data->m_version = version;
data->m_type = SOAP_FUNCTIONS;
data->m_soap_functions.functions_all = false;
if (!wsdl.isNull()) {
data->m_sdl = s_soap_data->get_sdl(wsdl.toString().data(), cache_wsdl);
if (data->m_uri.isNull()) {
if (!data->m_sdl->target_ns.empty()) {
data->m_uri = String(data->m_sdl->target_ns);
} else {
/*FIXME*/
data->m_uri = String("http://unknown-uri/");
}
}
}
if (!typemap_ht.empty()) {
data->m_typemap = soap_create_typemap(data->m_sdl, typemap_ht);
}
} catch (Exception& e) {
throw_object(create_soap_fault(e));
}
}
void HHVM_METHOD(SoapServer, setClass,
const String& name,
const Array& argv /* = null_array */) {
auto* data = Native::data<SoapServer>(this_);
SoapServerScope ss(this_);
if (HHVM_FN(class_exists)(name, true)) {
data->m_type = SOAP_CLASS;
data->m_soap_class.name = name;
data->m_soap_class.argv = argv;
data->m_soap_class.persistance = SOAP_PERSISTENCE_REQUEST;
} else {
raise_warning("Tried to set a non existent class (%s)", name.data());
}
}
void HHVM_METHOD(SoapServer, setobject,
const Variant& obj) {
auto* data = Native::data<SoapServer>(this_);
SoapServerScope ss(this_);
data->m_type = SOAP_OBJECT;
data->m_soap_object = obj.toObject();
}
void HHVM_METHOD(SoapServer, addFunction,
const Variant& func) {
auto* data = Native::data<SoapServer>(this_);
SoapServerScope ss(this_);
Array funcs;
if (func.isString()) {
funcs.append(func);
} else if (func.isArray()) {
funcs = func.toArray();
} else if (func.isInteger()) {
if (func.toInt64() == SOAP_FUNCTIONS_ALL) {
data->m_soap_functions.ft.clear();
data->m_soap_functions.ftOriginal.clear();
data->m_soap_functions.functions_all = true;
} else {
raise_warning("Invalid value passed");
}
return;
}
if (data->m_type == SOAP_FUNCTIONS) {
for (ArrayIter iter(funcs); iter; ++iter) {
if (!iter.second().isString()) {
raise_warning("Tried to add a function that isn't a string");
return;
}
String function_name = iter.second().toString();
if (!HHVM_FN(function_exists)(function_name)) {
raise_warning("Tried to add a non existent function '%s'",
function_name.data());
return;
}
data->m_soap_functions.ft.set(HHVM_FN(strtolower)(function_name), 1);
data->m_soap_functions.ftOriginal.set(function_name, 1);
}
}
}
Variant HHVM_METHOD(SoapServer, getfunctions) {
auto* data = Native::data<SoapServer>(this_);
SoapServerScope ss(this_);
String class_name;
if (data->m_type == SOAP_OBJECT) {
class_name = data->m_soap_object->getClassName();
} else if (data->m_type == SOAP_CLASS) {
class_name = data->m_soap_class.name;
} else if (data->m_soap_functions.functions_all) {
auto funcs1 = Unit::getSystemFunctions();
auto funcs2 = Unit::getUserFunctions();
VecInit init(funcs1.size() + funcs2.size());
IterateV(funcs1.get(), [&](TypedValue tv) { init.append(tv); });
IterateV(funcs2.get(), [&](TypedValue tv) { init.append(tv); });
return init.toArray();
} else if (!data->m_soap_functions.ft.empty()) {
return Variant::attach(
HHVM_FN(array_keys)(
make_array_like_tv(data->m_soap_functions.ftOriginal.get())
)
);
} else {
return empty_vec_array();
}
assertx(class_name.get());
Class* cls = Class::lookup(class_name.get());
assertx(cls);
auto ret = DictInit(cls->numMethods()).toArray();
Class::getMethodNames(cls, nullptr, ret);
return ret.toVec();
}
static bool valid_function(SoapServer *server, Object &soap_obj,
const String& fn_name) {
HPHP::Class* cls = nullptr;
if (server->m_type == SOAP_OBJECT) {
cls = server->m_soap_object->getVMClass();
} else if (server->m_type == SOAP_CLASS) {
cls = soap_obj->getVMClass();
} else if (server->m_soap_functions.functions_all) {
return HHVM_FN(function_exists)(fn_name);
} else if (!server->m_soap_functions.ft.empty()) {
return server->m_soap_functions.ft.exists(HHVM_FN(strtolower)(fn_name));
} else {
return false;
}
assertx(cls);
HPHP::Func* f = cls->lookupMethod(fn_name.get());
return (f && f->isPublic());
}
const StaticString
s_HTTP_CONTENT_ENCODING("HTTP_CONTENT_ENCODING"),
s_gzip("gzip"),
s_xgzip("x-gzip"),
s_deflate("deflate");
void HHVM_METHOD(SoapServer, handle,
const Variant& request /* = null */) {
auto* data = Native::data<SoapServer>(this_);
USE_SOAP_GLOBAL;
SoapServerScope ss(this_);
SOAP_GLOBAL(soap_version) = data->m_version;
// 0. serving WSDL
Transport *transport = g_context->getTransport();
if (transport && transport->getMethod() == Transport::Method::GET &&
transport->getCommand() == "wsdl") {
if (!data->m_sdl) {
throw_soap_server_fault("Server", "WSDL generation is not supported");
}
header_if_not_sent("Content-Type: text/xml; charset=utf-8");
Variant ret = HHVM_FN(readfile)(data->m_sdl->source.c_str());
if (same(ret, false)) {
throw_soap_server_fault("Server", "Couldn't find WSDL");
}
return;
}
if (!HHVM_FN(ob_start)()) {
throw SoapException("ob_start failed");
}
// 1. process request
String req;
if (!request.isNull()) {
req = request.toString();
} else {
size_t size;
const char *data = nullptr;
if (transport) {
data = (const char *)transport->getPostData(size);
}
if (!data || !*data || !size) {
return;
}
req = String(data, size, CopyString);
if (php_global(s__SERVER).toArray().exists(s_HTTP_CONTENT_ENCODING)) {
String encoding = php_global(s__SERVER)
.toArray()[s_HTTP_CONTENT_ENCODING].toString();
Variant ret;
if (encoding == s_gzip || encoding == s_xgzip) {
ret = HHVM_FN(gzinflate)(String(data, size, CopyString));
} else if (encoding == s_deflate) {
ret = HHVM_FN(gzuncompress)(String(data, size, CopyString));
} else {
raise_warning("Request is encoded with unknown compression '%s'",
encoding.data());
return;
}
if (!ret.isString()) {
raise_warning("Can't uncompress compressed request");
return;
}
req = ret.toString();
}
}
xmlDocPtr doc_request = soap_xmlParseMemory(req.data(), req.size());
if (doc_request == nullptr) {
throw_soap_server_fault("Client", "Bad Request");
}
if (xmlGetIntSubset(doc_request) != nullptr) {
xmlNodePtr env = get_node(doc_request->children,"Envelope");
if (env && env->ns) {
if (strcmp((char*)env->ns->href, SOAP_1_1_ENV_NAMESPACE) == 0) {
SOAP_GLOBAL(soap_version) = SOAP_1_1;
} else if (strcmp((char*)env->ns->href,SOAP_1_2_ENV_NAMESPACE) == 0) {
SOAP_GLOBAL(soap_version) = SOAP_1_2;
}
}
xmlFreeDoc(doc_request);
throw_soap_server_fault("Server", "DTD are not supported by SOAP");
}
// 2. find out which PHP function to call with what params
SoapServiceScope sss(data);
String function_name;
Array params;
int soap_version = 0;
std::shared_ptr<sdlFunction> function;
try {
function = deserialize_function_call(data->m_sdl, doc_request,
data->m_actor.c_str(),
function_name, params, soap_version,
data->m_soap_headers);
} catch (Exception& e) {
xmlFreeDoc(doc_request);
send_soap_server_fault(function, e, nullptr);
return;
}
xmlFreeDoc(doc_request);
// 3. we may need an object
Object soap_obj;
if (data->m_type == SOAP_OBJECT) {
soap_obj = data->m_soap_object;
} else if (data->m_type == SOAP_CLASS) {
try {
soap_obj = create_object(data->m_soap_class.name,
data->m_soap_class.argv);
} catch (Exception& e) {
send_soap_server_fault(function, e, nullptr);
return;
}
}
// 4. process soap headers
for (ArrayIter iter(data->m_soap_headers); iter; ++iter) {
auto h = cast<soapHeader>(iter.second());
if (data->m_sdl && !h->function && !h->hdr) {
if (h->mustUnderstand) {
throw_soap_server_fault("MustUnderstand","Header not understood");
}
continue;
}
String fn_name = h->function_name;
if (valid_function(data, soap_obj, fn_name)) {
try {
if (data->m_type == SOAP_CLASS || data->m_type == SOAP_OBJECT) {
h->retval = vm_call_user_func
(make_vec_array(soap_obj, fn_name), h->parameters);
} else {
h->retval = vm_call_user_func(fn_name, h->parameters);
}
} catch (Exception& e) {
send_soap_server_fault(function, e, h);
return;
}
if (h->retval.isObject() &&
h->retval.getObjectData()->instanceof(SystemLib::s_SoapFaultClass)) {
send_soap_server_fault(function, h->retval, h);
return;
}
} else if (h->mustUnderstand) {
throw_soap_server_fault("MustUnderstand","Header not understood");
}
}
// 5. main call
String fn_name = function_name;
Variant retval;
if (valid_function(data, soap_obj, fn_name)) {
try {
if (data->m_type == SOAP_CLASS || data->m_type == SOAP_OBJECT) {
retval = vm_call_user_func
(make_vec_array(soap_obj, fn_name), params);
} else {
retval = vm_call_user_func(fn_name, params);
}
} catch (Exception& e) {
send_soap_server_fault(function, e, nullptr);
return;
}
if (retval.isObject() &&
retval.toObject()->instanceof(SystemLib::s_SoapFaultClass)) {
send_soap_server_fault(function, retval, nullptr);
return;
}
} else {
throw SoapException("Function '%s' doesn't exist", fn_name.data());
}
// 6. serialize response
String response_name;
if (function && !function->responseName.empty()) {
response_name = function->responseName;
} else {
response_name = function_name + "Response";
}
xmlDocPtr doc_return = nullptr;
try {
doc_return = serialize_response_call(function, response_name.data(),
data->m_uri.c_str(), retval,
data->m_soap_headers,
soap_version);
} catch (Object& e) {
if (e->instanceof(SystemLib::s_SoapFaultClass)) {
send_soap_server_fault(function, e, nullptr);
return;
}
throw_object(e);
} catch (Exception& e) {
send_soap_server_fault(function, e, nullptr);
return;
}
// 7. throw away all buffered output so far, so we can send back a clean
// soap resposne
HHVM_FN(ob_end_clean)();
// 8. special case
if (doc_return == nullptr) {
header_if_not_sent("HTTP/1.1 202 Accepted");
header_if_not_sent("Content-Length: 0");
return;
}
// 9. XML response
xmlChar *buf; int size;
xmlDocDumpMemory(doc_return, &buf, &size);
xmlFreeDoc(doc_return);
if (buf == nullptr || size == 0) {
if (buf) xmlFree(buf);
throw SoapException("Dump memory failed");
}
output_xml_header(soap_version);
if (buf) {
g_context->write(String((char*)buf, size, CopyString));
xmlFree(buf);
}
}
void HHVM_METHOD(SoapServer, setpersistence,
int64_t mode) {
auto* data = Native::data<SoapServer>(this_);
SoapServerScope ss(this_);
if (data->m_type == SOAP_CLASS) {
if (mode == SOAP_PERSISTENCE_SESSION || mode == SOAP_PERSISTENCE_REQUEST) {
data->m_soap_class.persistance = mode;
} else {
raise_warning("Tried to set persistence with bogus value (%" PRId64 ")",
mode);
}
} else {
raise_warning("Tried to set persistence when you are using you "
"SOAP SERVER in function mode, no persistence needed");
}
}
void HHVM_METHOD(SoapServer, fault,
const Variant& code,
const String& fault,
const Variant& actor /* = null */,
const Variant& detail /* = null */,
const Variant& name /* = null */) {
SoapServerScope ss(this_);
const String& str_actor = actor.isNull() ? null_string : actor.toString();
const String& str_name = name.isNull() ? null_string : name.toString();
auto obj =
SystemLib::AllocSoapFaultObject(code,
fault,
str_actor,
detail,
str_name);
send_soap_server_fault(std::shared_ptr<sdlFunction>(), obj, nullptr);
}
void HHVM_METHOD(SoapServer, addSoapHeader,
const Variant& fault) {
auto* data = Native::data<SoapServer>(this_);
SoapServerScope ss(this_);
auto p = req::make<soapHeader>();
p->function = nullptr;
p->mustUnderstand = false;
p->retval = fault.toObject();
p->hdr = nullptr;
data->m_soap_headers.append(Variant(std::move(p)));
}
///////////////////////////////////////////////////////////////////////////////
// class SoapClient
const StaticString SoapClient::s_className("SoapClient");
SoapClient::SoapClient() :
m_soap_version(SOAP_1_1),
m_sdl(nullptr),
m_encoding(nullptr),
m_typemap(nullptr),
m_features(0),
m_style(SOAP_RPC),
m_use(SOAP_LITERAL),
m_authentication(SOAP_AUTHENTICATION_BASIC),
m_proxy_port(0),
m_connection_timeout(0),
m_max_redirect(HttpClient::defaultMaxRedirect),
m_use11(true),
m_compression(0),
m_exceptions(true),
m_trace(false) {
}
void HHVM_METHOD(SoapClient, __construct,
const Variant& wsdl,
const Array& options /* = null_array */) {
auto* data = Native::data<SoapClient>(this_);
USE_SOAP_GLOBAL;
SoapClientScope ss(this_);
try {
if (!wsdl.isString() && !wsdl.isNull()) {
throw SoapException("wsdl must be string or null");
}
int64_t cache_wsdl = SOAP_GLOBAL(cache);
if (!options.empty()) {
data->m_location = options[s_location].toString();
if (wsdl.isNull()) {
/* Fetching non-WSDL mode options */
data->m_uri = options[s_uri].toString();
data->m_style = options[s_style].toInt32(); // SOAP_RPC || SOAP_DOCUMENT
data->m_use = options[s_use].toInt32(); // SOAP_LITERAL || SOAP_ENCODED
if (data->m_uri.empty()) {
throw SoapException("'uri' option is required in nonWSDL mode");
}
if (data->m_location.empty()) {
throw SoapException("'location' option is required in nonWSDL mode");
}
}
if (options.exists(s_stream_context)) {
req::ptr<StreamContext> sc;
if (options[s_stream_context].isResource()) {
sc = cast<StreamContext>(options[s_stream_context]);
}
if (!sc) {
throw SoapException("'stream_context' is not a StreamContext");
}
data->m_stream_context_options = sc->getOptions();
}
if (options.exists(s_soap_version)) {
data->m_soap_version = options[s_soap_version].toInt32();
}
data->m_login = options[s_login].toString();
data->m_password = options[s_password].toString();
data->m_authentication = options[s_authentication].toInt32();
data->m_proxy_host = options[s_proxy_host].toString();
data->m_proxy_port = options[s_proxy_port].toInt32();
data->m_proxy_login = options[s_proxy_login].toString();
data->m_proxy_password = options[s_proxy_password].toString();
data->m_proxy_ssl_cert_path = options[s_proxy_ssl_cert_path].toString();
data->m_proxy_ssl_key_path = options[s_proxy_ssl_key_path].toString();
data->m_proxy_ssl_ca_bundle = options[s_proxy_ssl_ca_bundle].toString();
data->m_trace = options[s_trace].toBoolean();
if (options.exists(s_exceptions)) {
data->m_exceptions = options[s_exceptions].toBoolean();
}
if (options.exists(s_compression)) {
data->m_compression = options[s_compression].toInt32();
}
String encoding = options[s_encoding].toString();
if (!encoding.empty()) {
data->m_encoding = xmlFindCharEncodingHandler(encoding.data());
if (data->m_encoding == nullptr) {
throw SoapException("Invalid 'encoding' option - '%s'",
encoding.data());
}
s_soap_data->register_encoding(data->m_encoding);
}
data->m_classmap = options[s_classmap].toArray();
data->m_features = options[s_features].toInt32();
data->m_ssl_method = options[s_ssl_method].toInt32();
data->m_connection_timeout = options[s_connection_timeout].toInt64();
data->m_user_agent = options[s_user_agent].toString();
if (options.exists(s_cache_wsdl)) {
cache_wsdl = options[s_cache_wsdl].toInt64();
}
} else if (wsdl.isNull()) {
throw SoapException("'location' and 'uri' options are required in "
"nonWSDL mode");
}
if (!wsdl.isNull()) {
int old_soap_version = SOAP_GLOBAL(soap_version);
SOAP_GLOBAL(soap_version) = data->m_soap_version;
String swsdl = wsdl.toString();
if (swsdl.find("http://") == 0 || swsdl.find("https://") == 0) {
HttpClient http(data->m_connection_timeout, data->m_max_redirect,
data->m_use11, true);
if (!data->m_proxy_host.empty() && data->m_proxy_port) {
http.proxy(data->m_proxy_host.data(), data->m_proxy_port,
data->m_proxy_login.data(), data->m_proxy_password.data());
if (!data->m_proxy_ssl_ca_bundle.empty()) {
http.setHttpsProxy(data->m_proxy_ssl_ca_bundle.data(),
data->m_proxy_ssl_cert_path.data(),
data->m_proxy_ssl_key_path.data());
}
}
if (!data->m_login.empty()) {
http.auth(data->m_login.data(), data->m_password.data(),
!data->m_digest);
}
http.setStreamContextOptions(data->m_stream_context_options);
data->m_sdl = s_soap_data->get_sdl(swsdl.data(), cache_wsdl, &http);
} else {
data->m_sdl = s_soap_data->get_sdl(swsdl.data(), cache_wsdl);
}
SOAP_GLOBAL(soap_version) = old_soap_version;
}
Variant v = options[s_typemap];
if (v.isArray()) {
Array arr = v.toArray();
if (!arr.empty()) {
data->m_typemap = soap_create_typemap(data->m_sdl, arr);
}
}
} catch (Exception& e) {
throw_object(create_soap_fault(e));
}
}
Variant HHVM_METHOD(SoapClient, soapcallImpl,
const String& name,
const Array& args,
const Array& options = null_array,
const Variant& input_headers = uninit_variant) {
auto* data = Native::data<SoapClient>(this_);
SoapClientScope ss(this_);
String location, soap_action, uri;
if (!options.isNull()) {
if (options[s_location].isString()) {
location = options[s_location].toString();
if (location.isNull()) {
location = data->m_location;
}
}
if (options[s_soapaction].isString()) {
soap_action = options[s_soapaction].toString();
}
if (options[s_uri].isString()) {
uri = options[s_uri].toString();
}
}
Array soap_headers = Array::CreateVec();
if (input_headers.isNull()) {
} else if (input_headers.isArray()) {
Array arr = input_headers.toArray();
verify_soap_headers_array(arr);
soap_headers = input_headers;
} else if (input_headers.isObject() &&
input_headers.toObject().instanceof(SoapHeader::getClass())) {
soap_headers = make_vec_array(input_headers);
} else{
raise_warning("Invalid SOAP header");
return init_null();
}
if (!data->m_default_headers.isNull()) {
IterateV(data->m_default_headers.toArray().get(), [&](auto val) {
soap_headers.append(val);
});
}
Array output_headers;
if (data->m_trace) {
data->m_last_request.unset();
data->m_last_response.unset();
}
if (location.empty()) {
location = data->m_location;
}
data->m_soap_fault.unset();
SoapServiceScope sss(data);
Variant return_value;
bool ret = false;
xmlDocPtr request = nullptr;
if (data->m_sdl) {
auto fn = get_function(data->m_sdl, name.data());
if (fn) {
sdlBindingPtr binding = fn->binding;
bool one_way = false;
if (fn->responseName.empty() && fn->responseParameters.empty() &&
soap_headers.empty()) {
one_way = true;
}
if (location.empty()) {
location = binding->location;
}
Variant response;
try {
if (binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = fn->bindingAttributes;
request = serialize_function_call(data, fn, nullptr,
fnb->input.ns.c_str(), args,
soap_headers);
ret = do_request(this_, request, location.data(),
fnb->soapAction.c_str(), data->m_soap_version,
one_way, response);
} else {
request = serialize_function_call(data, fn, nullptr,
data->m_sdl->target_ns.c_str(),
args, soap_headers);
ret = do_request(this_, request, location.data(), nullptr,
data->m_soap_version, one_way, response);
}
} catch (Exception& e) {
xmlFreeDoc(request);
throw_object(create_soap_fault(e));
}
xmlFreeDoc(request);
if (ret && response.isString()) {
encode_reset_ns();
String sresponse = response.toString();
ret = parse_packet_soap(data, sresponse.data(), sresponse.size(),
fn, nullptr, return_value, output_headers);
encode_finish();
}
} else {
StringBuffer error;
error.append("Function (\"");
error.append(name.data());
error.append("\") is not a valid method for this service");
data->m_soap_fault = create_soap_fault("Client", error.detach());
}
} else {
string action;
if (data->m_uri.empty()) {
data->m_soap_fault =
create_soap_fault("Client", "Error finding \"uri\" property");
} else if (location.empty()) {
data->m_soap_fault =
create_soap_fault("Client", "Error could not find \"location\" "
"property");
} else {
request = serialize_function_call(data, std::shared_ptr<sdlFunction>(),
name.data(), data->m_uri.data(), args,
soap_headers);
if (soap_action.empty()) {
action += data->m_uri.data();
action += '#';
action += name.data();
} else {
action += (std::string) soap_action;
}
Variant response;
try {
ret = do_request(this_, request, location.c_str(), action.c_str(),
data->m_soap_version, 0, response);
} catch (Exception& e) {
xmlFreeDoc(request);
throw_object(create_soap_fault(e));
}
xmlFreeDoc(request);
if (ret && response.isString()) {
encode_reset_ns();
String sresponse = response.toString();
ret = parse_packet_soap(data, sresponse.data(), sresponse.size(),
std::shared_ptr<sdlFunction>(),
name.data(),
return_value,
output_headers);
encode_finish();
}
}
}
if (!ret && data->m_soap_fault.isNull()) {
data->m_soap_fault = create_soap_fault("Client", "Unknown Error");
}
if (!data->m_soap_fault.isNull()) {
throw_object(data->m_soap_fault.toObject());
}
return return_value;
}
Variant HHVM_METHOD(SoapClient, __getlastrequest) {
auto* data = Native::data<SoapClient>(this_);
return data->m_last_request;
}
Variant HHVM_METHOD(SoapClient, __getlastresponse) {
auto* data = Native::data<SoapClient>(this_);
return data->m_last_response;
}
Variant HHVM_METHOD(SoapClient, __getlastrequestheaders) {
auto* data = Native::data<SoapClient>(this_);
return data->m_last_request_headers;
}
Variant HHVM_METHOD(SoapClient, __getlastresponseheaders) {
auto* data = Native::data<SoapClient>(this_);
return data->m_last_response_headers;
}
Variant HHVM_METHOD(SoapClient, __getFunctions) {
auto* data = Native::data<SoapClient>(this_);
SoapClientScope ss(this_);
if (data->m_sdl) {
VecInit ret(data->m_sdl->functionsOrder.size());
for (const auto& func: data->m_sdl->functionsOrder) {
StringBuffer sb;
function_to_string(data->m_sdl->functions[func], sb);
ret.append(sb.detach());
}
return ret.toArray();
}
return init_null();
}
Variant HHVM_METHOD(SoapClient, __getTypes) {
auto* data = Native::data<SoapClient>(this_);
SoapClientScope ss(this_);
if (data->m_sdl) {
VecInit ret(data->m_sdl->types.size());
for (const auto& type: data->m_sdl->types) {
StringBuffer sb;
type_to_string(type.get(), sb, 0);
ret.append(sb.detach());
}
return ret.toArray();
}
return init_null();
}
static bool content_type_is_xml(const String& response,
const req::vector<String>& responseHeaders) {
if (response.empty()) return false;
for (auto header : responseHeaders) {
auto cheader = header.c_str();
auto cheader_len = header.size();
if ((cheader_len > sizeof("Content-type: ")) &&
!strncasecmp(cheader, "Content-type: ", strlen("Content-type: "))) {
// We got an explicit content-type header, trust if it says soap or xml
const char* s = cheader + strlen("Content-type: ");
const char* e = strchr(s, ';');
if (!e) e = cheader + cheader_len;
if (!strncmp("text/xml", s, e - s) ||
!strncmp("application/soap+xml", s, e - s)) {
return true;
}
}
}
// If we didn't see a header we like, just ignore the ones we don't like
// and see if the response looks xmlish
const char* p = response.c_str();
const char* e = p + response.size();
while ((p < e) && (*p == ' ')) ++p;
if (((p - e) >= 5) &&
!strncmp(p, "<?xml", strlen("<?xml"))) {
return true;
}
return false;
}
Variant HHVM_METHOD(SoapClient, __dorequest,
const String& buf,
const String& location,
const String& action,
int64_t version,
bool oneway /* = false */) {
auto* data = Native::data<SoapClient>(this_);
if (location.empty()) {
data->m_soap_fault =
SystemLib::AllocSoapFaultObject("HTTP", "Unable to parse URL");
return init_null();
}
USE_SOAP_GLOBAL;
SoapClientScope ss(this_);
HeaderMap headers;
String buffer(buf);
// compression
if (data->m_compression > 0) {
if (data->m_compression & SOAP_COMPRESSION_ACCEPT) {
headers["Accept-Encoding"].push_back("gzip, deflate");
}
int level = data->m_compression & 0x0f;
if (level > 9) level = 9;
if (level > 0) {
Variant ret;
if (data->m_compression & SOAP_COMPRESSION_DEFLATE) {
ret = HHVM_FN(gzcompress)(buffer, level);
headers["Content-Encoding"].push_back("deflate");
} else {
ret = HHVM_FN(gzencode)(buffer, level);
headers["Content-Encoding"].push_back("gzip");
}
if (!ret.isString()) return init_null();
buffer = ret.toString();
}
}
// prepare more headers
if (!data->m_user_agent.empty()) {
headers["User-Agent"].push_back(data->m_user_agent.data());
}
string contentType;
if (version == SOAP_1_2) {
contentType = "application/soap+xml; charset=utf-8";
contentType += "; action=\"";
contentType += action.data();
contentType += "\"";
headers["Content-Type"].push_back(contentType);
} else {
contentType = "text/xml; charset=utf-8";
headers["Content-Type"].push_back(contentType);
headers["SOAPAction"].push_back(string("\"") + action.data() + "\"");
}
// post the request
HttpClient http(data->m_connection_timeout, data->m_max_redirect,
data->m_use11, true);
if (!data->m_proxy_host.empty() && data->m_proxy_port) {
http.proxy(data->m_proxy_host.data(), data->m_proxy_port,
data->m_proxy_login.data(), data->m_proxy_password.data());
if (!data->m_proxy_ssl_ca_bundle.empty()) {
http.setHttpsProxy(data->m_proxy_ssl_ca_bundle.data(),
data->m_proxy_ssl_cert_path.data(),
data->m_proxy_ssl_key_path.data());
}
}
if (!data->m_login.empty()) {
http.auth(data->m_login.data(), data->m_password.data(), !data->m_digest);
}
http.setStreamContextOptions(data->m_stream_context_options);
if(data->m_ssl_method > -1) {
http.setUseSSL(CURLUSESSL_ALL);
switch (data->m_ssl_method) {
case SOAP_SSL_METHOD_TLS : http.setSSLVersion(CURL_SSLVERSION_TLSv1);
break;
case SOAP_SSL_METHOD_SSLv2 : http.setSSLVersion(CURL_SSLVERSION_SSLv2);
break;
case SOAP_SSL_METHOD_SSLv3 : http.setSSLVersion(CURL_SSLVERSION_SSLv3);
break;
case SOAP_SSL_METHOD_SSLv23 : http.setSSLVersion(CURL_SSLVERSION_SSLv2);
break;
default: raise_warning("SoapClient(): ssl_method unrecognized"); break;
}
// Not done by PHP5/7, but using SSL nowadays is a very bad idea.
if (data->m_ssl_method != SOAP_SSL_METHOD_TLS) {
raise_warning(
"SoapClient() ssl_method: SSL is flawed and vulnerable;"
"Migrate to TLS as soon as possible."
);
}
}
StringBuffer responseBuffer;
req::vector<String> responseHeaders;
int code = http.post(location.data(), buffer.data(), buffer.size(),
responseBuffer, &headers, &responseHeaders);
String response = responseBuffer.detach();
if (code == 0) {
String msg = "Failed Sending HTTP Soap request";
auto const& error = http.getLastError();
if (!error.empty()) {
msg += ": ";
msg += error;
}
data->m_soap_fault = SystemLib::AllocSoapFaultObject("HTTP", msg);
return init_null();
}
if ((code >= 400) && !content_type_is_xml(response, responseHeaders)) {
if (response.empty()) {
response = HttpProtocol::GetReasonString(code);
}
if (data->m_exceptions) {
data->m_soap_fault = SystemLib::AllocSoapFaultObject("HTTP", response);
return init_null();
}
}
// return response
if (SOAP_GLOBAL(features) & SOAP_WAIT_ONE_WAY_CALLS) {
oneway = false;
}
if (oneway) {
return empty_string_variant();
}
return response;
}
Variant HHVM_METHOD(SoapClient, __setcookie,
const String& name,
const Variant& value /* = null_string */) {
auto* data = Native::data<SoapClient>(this_);
// FIXME: data->m_cookies is a write-only value
if (!value.isNull()) {
data->m_cookies.set(name, make_vec_array(value.toString()));
} else {
data->m_cookies.remove(name);
}
return init_null();
}
Variant HHVM_METHOD(SoapClient, __setlocation,
const Variant& new_location /* = null_string */) {
auto* data = Native::data<SoapClient>(this_);
Variant ret = data->m_location;
data->m_location = new_location.isNull()
? null_string
: new_location.toString();
return ret;
}
bool HHVM_METHOD(SoapClient, __setsoapheaders,
const Variant& headers /* = uninit_variant */) {
auto* data = Native::data<SoapClient>(this_);
if (headers.isNull()) {
data->m_default_headers.unset();
} else if (headers.isArray()) {
Array arr = headers.toArray();
verify_soap_headers_array(arr);
data->m_default_headers = arr;
} else if (headers.isObject() &&
headers.toObject().instanceof(SoapHeader::getClass())) {
data->m_default_headers = make_vec_array(headers);
} else {
raise_warning("Invalid SOAP header");
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// class SoapVar
Class* SoapVar::s_class = nullptr;
IMPLEMENT_GET_CLASS(SoapVar)
const StaticString
s_enc_type("enc_type"),
s_enc_value("enc_value"),
s_enc_stype("enc_stype"),
s_enc_ns("enc_ns"),
s_enc_name("enc_name"),
s_enc_namens("enc_namens"),
SoapVar::s_className("SoapVar");
void HHVM_METHOD(SoapVar, __construct,
const Variant& data,
const Variant& type,
const String& type_name /* = null_string */,
const String& type_namespace /* = null_string */,
const String& node_name /* = null_string */,
const String& node_namespace /* = null_string */) {
USE_SOAP_GLOBAL;
int64_t ntype;
if (type.isNull()) {
ntype = UNKNOWN_TYPE;
} else {
std::map<int, encodePtr> &defEncIndex = SOAP_GLOBAL(defEncIndex);
ntype = type.toInt64();
if (defEncIndex.find(ntype) == defEncIndex.end()) {
raise_warning("Invalid type ID");
return;
}
}
this_->setProp(nullptr, s_enc_type.get(), make_tv<KindOfInt64>(ntype));
if (data.toBoolean()) this_->setProp(nullptr, s_enc_value.get(), *data.asTypedValue());
if (!type_name.empty()) {
this_->setProp(nullptr, s_enc_stype.get(), type_name.asTypedValue());
}
if (!type_namespace.empty()) {
this_->setProp(nullptr, s_enc_ns.get(), type_namespace.asTypedValue());
}
if (!node_name.empty()) {
this_->setProp(nullptr, s_enc_name.get(), node_name.asTypedValue());
}
if (!node_namespace.empty()) {
this_->setProp(nullptr, s_enc_namens.get(), node_namespace.asTypedValue());
}
}
///////////////////////////////////////////////////////////////////////////////
// class SoapParam
Class* SoapParam::s_class = nullptr;
const StaticString SoapParam::s_className("SoapParam");
IMPLEMENT_GET_CLASS(SoapParam)
void HHVM_METHOD(SoapParam, __construct,
const Variant& data,
const String& name) {
auto* nativeData = Native::data<SoapParam>(this_);
if (name.empty()) {
raise_warning("Invalid parameter name");
return;
}
nativeData->m_name = name;
nativeData->m_data = data.toString();
}
///////////////////////////////////////////////////////////////////////////////
// class SoapHeader
Class* SoapHeader::s_class = nullptr;
const StaticString SoapHeader::s_className("SoapHeader");
IMPLEMENT_GET_CLASS(SoapHeader)
static const StaticString
s_namespace("namespace"),
s_name("name"),
s_data("data"),
s_mustUnderstand("mustUnderstand");
void HHVM_METHOD(SoapHeader, __construct,
const String& ns,
const String& name,
const Variant& data /* = null */,
bool mustunderstand /* = false */,
const Variant& actor /* = null */) {
auto* nativeData = Native::data<SoapHeader>(this_);
if (ns.empty()) {
raise_warning("Invalid namespace");
return;
}
if (name.empty()) {
raise_warning("Invalid header name");
return;
}
nativeData->m_namespace = ns;
nativeData->m_name = name;
nativeData->m_data = data;
nativeData->m_mustUnderstand = mustunderstand;
this_->setProp(nullptr, s_namespace.get(), ns.asTypedValue());
this_->setProp(nullptr, s_name.get(), name.asTypedValue());
this_->setProp(nullptr, s_data.get(), data.asInitTVTmp());
this_->setProp(nullptr, s_mustUnderstand.get(),
make_tv<KindOfBoolean>(mustunderstand));
if (actor.isInteger() &&
(actor.toInt64() == SOAP_ACTOR_NEXT ||
actor.toInt64() == SOAP_ACTOR_NONE ||
actor.toInt64() == SOAP_ACTOR_UNLIMATERECEIVER)) {
nativeData->m_actor = actor.toInt64();
} else if (actor.isString() && !actor.toString().empty()) {
nativeData->m_actor = actor.toString();
} else if (!actor.isNull()) {
raise_warning("Invalid actor");
}
}
///////////////////////////////////////////////////////////////////////////////
static struct SoapExtension final : Extension {
SoapExtension() : Extension("soap", NO_EXTENSION_VERSION_YET) {}
void moduleInit() override {
HHVM_ME(SoapServer, __construct);
HHVM_ME(SoapServer, setClass);
HHVM_ME(SoapServer, setobject);
HHVM_ME(SoapServer, addFunction);
HHVM_ME(SoapServer, getfunctions);
HHVM_ME(SoapServer, handle);
HHVM_ME(SoapServer, setpersistence);
HHVM_ME(SoapServer, fault);
HHVM_ME(SoapServer, addSoapHeader);
Native::registerNativeDataInfo<SoapServer>(SoapServer::s_className.get(),
Native::NDIFlags::NO_SWEEP);
HHVM_ME(SoapClient, __construct);
HHVM_ME(SoapClient, soapcallImpl);
HHVM_ME(SoapClient, __getlastrequest);
HHVM_ME(SoapClient, __getlastresponse);
HHVM_ME(SoapClient, __getlastrequestheaders);
HHVM_ME(SoapClient, __getlastresponseheaders);
HHVM_ME(SoapClient, __getFunctions);
HHVM_ME(SoapClient, __getTypes);
HHVM_ME(SoapClient, __dorequest);
HHVM_ME(SoapClient, __setcookie);
HHVM_ME(SoapClient, __setlocation);
HHVM_ME(SoapClient, __setsoapheaders);
Native::registerNativeDataInfo<SoapClient>(SoapClient::s_className.get(),
Native::NDIFlags::NO_SWEEP);
HHVM_ME(SoapVar, __construct);
HHVM_ME(SoapParam, __construct);
Native::registerNativeDataInfo<SoapParam>(SoapParam::s_className.get(),
Native::NDIFlags::NO_SWEEP);
HHVM_ME(SoapHeader, __construct);
Native::registerNativeDataInfo<SoapHeader>(SoapHeader::s_className.get(),
Native::NDIFlags::NO_SWEEP);
HHVM_FE(use_soap_error_handler);
HHVM_FE(is_soap_fault);
HHVM_FE(_soap_active_version);
HHVM_RC_INT_SAME(SOAP_1_1);
HHVM_RC_INT_SAME(SOAP_1_2);
HHVM_RC_INT_SAME(SOAP_ACTOR_NEXT);
HHVM_RC_INT_SAME(SOAP_ACTOR_NONE);
HHVM_RC_INT_SAME(SOAP_ACTOR_UNLIMATERECEIVER);
HHVM_RC_INT_SAME(SOAP_AUTHENTICATION_BASIC);
HHVM_RC_INT_SAME(SOAP_AUTHENTICATION_DIGEST);
HHVM_RC_INT_SAME(SOAP_COMPRESSION_ACCEPT);
HHVM_RC_INT_SAME(SOAP_COMPRESSION_DEFLATE);
HHVM_RC_INT_SAME(SOAP_COMPRESSION_GZIP);
HHVM_RC_INT_SAME(SOAP_DOCUMENT);
HHVM_RC_INT_SAME(SOAP_ENCODED);
HHVM_RC_INT_SAME(SOAP_ENC_ARRAY);
HHVM_RC_INT_SAME(SOAP_ENC_OBJECT);
HHVM_RC_INT_SAME(SOAP_FUNCTIONS_ALL);
HHVM_RC_INT_SAME(SOAP_LITERAL);
HHVM_RC_INT_SAME(SOAP_PERSISTENCE_REQUEST);
HHVM_RC_INT_SAME(SOAP_PERSISTENCE_SESSION);
HHVM_RC_INT_SAME(SOAP_RPC);
HHVM_RC_INT_SAME(SOAP_SINGLE_ELEMENT_ARRAYS);
HHVM_RC_INT_SAME(SOAP_USE_XSI_ARRAY_TYPE);
HHVM_RC_INT_SAME(SOAP_WAIT_ONE_WAY_CALLS);
HHVM_RC_INT_SAME(UNKNOWN_TYPE);
HHVM_RC_INT_SAME(WSDL_CACHE_NONE);
HHVM_RC_INT_SAME(WSDL_CACHE_DISK);
HHVM_RC_INT_SAME(WSDL_CACHE_MEMORY);
HHVM_RC_INT_SAME(WSDL_CACHE_BOTH);
HHVM_RC_INT_SAME(XSD_STRING);
HHVM_RC_INT_SAME(XSD_BOOLEAN);
HHVM_RC_INT_SAME(XSD_DECIMAL);
HHVM_RC_INT_SAME(XSD_FLOAT);
HHVM_RC_INT_SAME(XSD_DOUBLE);
HHVM_RC_INT_SAME(XSD_DURATION);
HHVM_RC_INT_SAME(XSD_DATETIME);
HHVM_RC_INT_SAME(XSD_TIME);
HHVM_RC_INT_SAME(XSD_DATE);
HHVM_RC_INT_SAME(XSD_GYEARMONTH);
HHVM_RC_INT_SAME(XSD_GYEAR);
HHVM_RC_INT_SAME(XSD_GMONTHDAY);
HHVM_RC_INT_SAME(XSD_GDAY);
HHVM_RC_INT_SAME(XSD_GMONTH);
HHVM_RC_INT_SAME(XSD_HEXBINARY);
HHVM_RC_INT_SAME(XSD_BASE64BINARY);
HHVM_RC_INT_SAME(XSD_ANYURI);
HHVM_RC_INT_SAME(XSD_QNAME);
HHVM_RC_INT_SAME(XSD_NOTATION);
HHVM_RC_INT_SAME(XSD_NORMALIZEDSTRING);
HHVM_RC_INT_SAME(XSD_TOKEN);
HHVM_RC_INT_SAME(XSD_LANGUAGE);
HHVM_RC_INT_SAME(XSD_NMTOKEN);
HHVM_RC_INT_SAME(XSD_NAME);
HHVM_RC_INT_SAME(XSD_NCNAME);
HHVM_RC_INT_SAME(XSD_ID);
HHVM_RC_INT_SAME(XSD_IDREF);
HHVM_RC_INT_SAME(XSD_IDREFS);
HHVM_RC_INT_SAME(XSD_ENTITY);
HHVM_RC_INT_SAME(XSD_ENTITIES);
HHVM_RC_INT_SAME(XSD_INTEGER);
HHVM_RC_INT_SAME(XSD_NONPOSITIVEINTEGER);
HHVM_RC_INT_SAME(XSD_NEGATIVEINTEGER);
HHVM_RC_INT_SAME(XSD_LONG);
HHVM_RC_INT_SAME(XSD_INT);
HHVM_RC_INT_SAME(XSD_SHORT);
HHVM_RC_INT_SAME(XSD_BYTE);
HHVM_RC_INT_SAME(XSD_NONNEGATIVEINTEGER);
HHVM_RC_INT_SAME(XSD_UNSIGNEDLONG);
HHVM_RC_INT_SAME(XSD_UNSIGNEDINT);
HHVM_RC_INT_SAME(XSD_UNSIGNEDSHORT);
HHVM_RC_INT_SAME(XSD_UNSIGNEDBYTE);
HHVM_RC_INT_SAME(XSD_POSITIVEINTEGER);
HHVM_RC_INT_SAME(XSD_NMTOKENS);
HHVM_RC_INT_SAME(XSD_ANYTYPE);
HHVM_RC_INT_SAME(XSD_ANYXML);
HHVM_RC_INT_SAME(XSD_1999_TIMEINSTANT);
HHVM_RC_INT_SAME(SOAP_SSL_METHOD_TLS);
HHVM_RC_INT_SAME(SOAP_SSL_METHOD_SSLv2);
HHVM_RC_INT_SAME(SOAP_SSL_METHOD_SSLv3);
HHVM_RC_INT_SAME(SOAP_SSL_METHOD_SSLv23);
HHVM_RC_STR_SAME(XSD_NAMESPACE);
HHVM_RC_STR_SAME(XSD_1999_NAMESPACE);
loadSystemlib();
}
} s_soap_extension;
///////////////////////////////////////////////////////////////////////////////
}