Arquivos
hhvm/hphp/runtime/ext/ext_soap.cpp
T
Drew Paroski 84b9d9a3a2 Separate resources from objects, part 1
In HHVM (and HPHPc before it) we've been piggybacking resources on the
KindOfObject machinery. At the language level, resource is considered to
be a different type than object, and there are a number of differences
in behavior between objects and resources (ex. resources don't allow for
dynamic properties, resources don't work with the clone operator, the
"(object)" cast behaves differently for resources vs. objects, etc).

Piggybacking resources on the KindOfObject machinery has some downsides.
Code that deals with KindOfObject values often needs to check if the value
is a resource and go down a different code path. This makes things harder
to maintain and harder to keep parity with Zend. Also, these extra branches
hurt performance a little, and they make it harder for the JIT to do a good
job in some cases when its generating machine code that operates on objects.

This diff prepares the code base for a new KindOfResource type by adding a
new "Resource" smart pointer type (currently a typedef for the Object smart
pointer type) and it updates the C++ code and the idl files appropriately.
This diff is essentially a cosmetic change and should not impact run time
behavior. In the next diff (part 2) we'll actually add a new KindOfResource
type, detach ResourceData from the ObjectData inheritence hierarchy, and
provide a real implementation for the Resource smart pointer type (instead
of just aliasing the Object smart pointer type).
2013-07-10 11:16:33 -07:00

2869 linhas
91 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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/ext_soap.h"
#include "hphp/runtime/base/util/http_client.h"
#include "hphp/runtime/base/server/http_protocol.h"
#include "hphp/runtime/base/class_info.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/ext_zlib.h"
#include "hphp/runtime/ext/ext_network.h"
#include "hphp/runtime/ext/ext_array.h"
#include "hphp/runtime/ext/ext_function.h"
#include "hphp/runtime/ext/ext_class.h"
#include "hphp/runtime/ext/ext_output.h"
#include "hphp/runtime/ext/ext_stream.h"
#include "hphp/system/systemlib.h"
namespace HPHP {
static StaticString s___dorequest("__dorequest");
IMPLEMENT_DEFAULT_EXTENSION(soap);
///////////////////////////////////////////////////////////////////////////////
// helper classes for setting/resetting globals within a method call
class SoapScope {
public:
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;
};
class SoapServerScope : public SoapScope {
public:
explicit SoapServerScope(c_SoapServer *server) {
USE_SOAP_GLOBAL;
SOAP_GLOBAL(error_code) = "Server";
SOAP_GLOBAL(error_object) = Object(server);
}
};
class SoapClientScope : public SoapScope {
public:
explicit SoapClientScope(c_SoapClient *client) {
USE_SOAP_GLOBAL;
SOAP_GLOBAL(error_code) = "Client";
SOAP_GLOBAL(error_object) = Object(client);
}
};
class SoapServiceScope {
public:
explicit SoapServiceScope(c_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(c_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(litstr code, litstr fault);
static void model_to_string(sdlContentModelPtr model, StringBuffer &buf,
int level);
///////////////////////////////////////////////////////////////////////////////
// client helpers
static Object create_soap_fault(CStrRef code, CStrRef fault) {
return Object(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(CVarRef val, sdlParamPtr param,
const char *paramName, int style,
xmlNodePtr parent) {
xmlNodePtr xmlParam;
encodePtr enc;
Variant v = val;
if (param != NULL) {
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()) {
c_SoapParam *p = value.toObject().getTyped<c_SoapParam>(true, true);
if (p) {
value = p->m_data;
name = p->m_name.c_str();
}
}
if (param && !param->paramName.empty()) {
name = param->paramName.c_str();
} else {
if (name == NULL) {
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
(c_SoapClient *client, sdlFunctionPtr function, const char *function_name,
const char *uri, CArrRef arguments, CArrRef soap_headers) {
xmlNodePtr envelope = NULL, body, method = NULL, head = NULL;
xmlNsPtr ns = NULL;
int style, use;
sdlSoapBindingFunctionHeaderMap *hdrs = NULL;
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, NULL, BAD_CAST("Envelope"), NULL);
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, NULL, BAD_CAST("Envelope"), NULL);
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"), NULL);
}
body = xmlNewChild(envelope, ns, BAD_CAST("Body"), NULL);
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()), NULL);
} else {
method = xmlNewChild(body, ns,
BAD_CAST(function->functionName.c_str()), NULL);
}
}
} 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), NULL);
} else if (function && !function->requestName.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->requestName.c_str()), NULL);
} else if (function && !function->functionName.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->functionName.c_str()), NULL);
} 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(), NULL, i, false);
}
if (style == SOAP_RPC) {
if (parameter) {
param = serialize_parameter(parameter, iter.second(), i, NULL,
use, method);
}
} else if (style == SOAP_DOCUMENT) {
param = serialize_parameter(parameter, iter.second(), i, NULL,
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(), NULL, i, false);
if (style == SOAP_RPC) {
param = serialize_parameter(parameter, uninit_null(), i, NULL, use, method);
} else if (style == SOAP_DOCUMENT) {
param = serialize_parameter(parameter, uninit_null(), i, NULL, 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) {
c_SoapHeader *header = iter.second().toObject().getTyped<c_SoapHeader>();
xmlNodePtr h;
xmlNsPtr nsptr;
int hdr_use = SOAP_LITERAL;
encodePtr enc;
if (hdrs) {
string key = header->m_namespace.data();
key += ':';
key += header->m_name.data();
sdlSoapBindingFunctionHeaderMap::iterator iter = hdrs->find(key);
if (iter != hdrs->end()) {
sdlSoapBindingFunctionHeaderPtr 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(NULL, 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;
}
static bool do_request(c_SoapClient *client, xmlDoc *request,
const char *location, const char *action, int version,
bool one_way, Variant &response) {
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 = client->o_invoke_few_args(s___dorequest, 5,
String(buf, buf_size, AttachLiteral),
String(location, AttachLiteral),
String(action, AttachLiteral),
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().is<c_SoapHeader>()) {
throw SoapException("Invalid SOAP header");
}
}
}
///////////////////////////////////////////////////////////////////////////////
// shared helpers
static encodeMapPtr soap_create_typemap_impl(sdl *sdl, Array &ht) {
encodeMapPtr typemap(new 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 == "type_name") {
if (tmp.isString()) type_name = tmp.toString();
} else if (name == "type_ns") {
if (tmp.isString()) type_ns = tmp.toString();
} else if (name == "to_xml") {
to_xml = tmp;
} else if (name == "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 = encodePtr(new 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 = soapMappingPtr(new 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) {
f_header("Content-Type: application/soap+xml; charset=utf-8");
} else {
f_header("Content-Type: text/xml; charset=utf-8");
}
}
///////////////////////////////////////////////////////////////////////////////
// server helpers
static void deserialize_parameters(xmlNodePtr params, sdlFunction *function,
Array &parameters) {
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) {
int num_of_params = 0;
xmlNodePtr trav = params;
while (trav != NULL) {
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 == NULL) {
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 sdlFunctionPtr get_doc_function(sdl *sdl, xmlNodePtr params) {
if (sdl) {
for (sdlFunctionMap::iterator iter = sdl->functions.begin();
iter != sdl->functions.end(); ++iter) {
sdlFunctionPtr tmp = iter->second;
if (tmp->binding && tmp->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = tmp->bindingAttributes;
if (fnb->style == SOAP_DOCUMENT) {
if (params == NULL) {
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 == NULL)) {
ok = false;
break;
}
} else if (param->paramName != (char*)node->name) {
ok = false;
break;
}
node = node->next;
}
if (ok /*&& node == NULL*/) {
return tmp;
}
}
}
}
}
}
return sdlFunctionPtr();
}
static sdlFunctionPtr get_function(sdl *sdl, const char *function_name) {
if (sdl) {
String lowered = StringUtil::ToLower(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 sdlFunctionPtr();
}
}
return iter->second;
}
return sdlFunctionPtr();
}
static sdlFunctionPtr find_function(sdl *sdl, xmlNodePtr func,
String &function_name) {
sdlFunctionPtr 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 != NULL || !function->requestParameters.empty()) {
function.reset();
}
}
}
if (sdl != NULL && function == NULL) {
function = get_doc_function(sdl, func);
}
if (function != NULL) {
function_name = String(function->functionName);
} else {
function_name = String((char *)func->name, CopyString);
}
return function;
}
static sdlFunctionPtr deserialize_function_call
(sdl *sdl, xmlDocPtr request, const char* actor, String &function_name,
Array &parameters, int &version, Array &headers) {
USE_SOAP_GLOBAL;
char* envelope_ns = NULL;
xmlNodePtr trav,env,head,body,func;
xmlAttrPtr attr;
sdlFunctionPtr function;
encode_reset_ns();
/* Get <Envelope> element */
env = NULL;
trav = request->children;
while (trav != NULL) {
if (trav->type == XML_ELEMENT_NODE) {
if (env == NULL &&
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 == NULL &&
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 == NULL) {
throw_soap_server_fault
("Client", "looks like we got XML without \"Envelope\" element");
}
attr = env->properties;
while (attr != NULL) {
if (attr->ns == NULL) {
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 = NULL;
trav = env->children;
while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
trav = trav->next;
}
if (trav != NULL && node_is_equal_ex(trav,"Header",envelope_ns)) {
head = trav;
trav = trav->next;
}
/* Get <Body> element */
body = NULL;
while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
trav = trav->next;
}
if (trav != NULL && node_is_equal_ex(trav,"Body",envelope_ns)) {
body = trav;
trav = trav->next;
}
while (trav != NULL && trav->type != XML_ELEMENT_NODE) {
trav = trav->next;
}
if (body == NULL) {
throw_soap_server_fault("Client", "Body must be present in a "
"SOAP envelope");
}
attr = body->properties;
while (attr != NULL) {
if (attr->ns == NULL) {
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 != NULL && version == SOAP_1_2) {
throw_soap_server_fault("Client", "A SOAP 1.2 envelope can contain only "
"Header and Body");
}
func = NULL;
trav = body->children;
while (trav != NULL) {
if (trav->type == XML_ELEMENT_NODE) {
/*
if (func != NULL) {
throw_soap_server_fault("Client", "looks like we got \"Body\" with "
"several functions call", NULL, NULL, NULL);
}
*/
func = trav;
break; /* FIXME: the rest of body is ignored */
}
trav = trav->next;
}
if (func == NULL) {
function = get_doc_function(sdl, NULL);
if (function != NULL) {
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 != NULL && function == NULL) {
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::Create();
if (head) {
soapHeader *h = NULL;
attr = head->properties;
while (attr != NULL) {
if (attr->ns == NULL) {
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 != NULL) {
if (trav->type == XML_ELEMENT_NODE) {
xmlNodePtr hdr_func = trav;
xmlAttrPtr attr;
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 != NULL) {
if (strcmp((char*)attr->children->content,
SOAP_1_1_ACTOR_NEXT) != 0 &&
(actor == NULL ||
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 != NULL) {
if (strcmp((char*)attr->children->content,
SOAP_1_2_ACTOR_UNLIMATERECEIVER) != 0 &&
strcmp((char*)attr->children->content,
SOAP_1_2_ACTOR_NEXT) != 0 &&
(actor == NULL ||
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");
}
}
h = NEWOBJ(soapHeader)();
Resource hobj(h);
h->function = find_function(sdl, hdr_func, h->function_name).get();
h->mustUnderstand = mustUnderstand;
h->hdr = NULL;
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(hobj);
}
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 = NULL, param;
sdlParamPtr parameter;
int param_count;
int style, use;
xmlNsPtr ns = NULL;
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()), NULL);
} else if (!function->responseParameters.empty()) {
method = xmlNewChild(body, ns,
BAD_CAST(function->functionName.c_str()), NULL);
}
}
} 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), NULL);
}
}
if (function) {
param_count = function->responseParameters.size();
} else {
param_count = 1;
}
if (param_count == 1) {
parameter = get_param(function, NULL, 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"), NULL);
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;
}
static xmlDocPtr serialize_response_call(sdlFunctionPtr function,
const char *function_name,
const char *uri, Variant &ret,
CArrRef headers, int version) {
xmlNodePtr envelope = NULL, body, param;
xmlNsPtr ns = NULL;
int use = SOAP_LITERAL;
xmlNodePtr head = NULL;
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, NULL, BAD_CAST("Envelope"), NULL);
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, NULL, BAD_CAST("Envelope"), NULL);
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;
sdlFaultPtr fault;
string fault_ns;
if (!headers.empty()) {
xmlNodePtr head;
encodePtr hdr_enc;
int hdr_use = SOAP_LITERAL;
Variant hdr_ret = obj->o_get("headerfault");
soapHeader *h = headers[0].toResource().getTyped<soapHeader>();
const char *hdr_ns = h->hdr ? h->hdr->ns.c_str() : NULL;
const char *hdr_name = h->function_name.data();
head = xmlNewChild(envelope, ns, BAD_CAST("Header"), NULL);
if (hdr_ret.isObject() && hdr_ret.toObject().is<c_SoapHeader>()) {
c_SoapHeader *ht = hdr_ret.toObject().getTyped<c_SoapHeader>();
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()) {
sdlSoapBindingFunctionHeaderPtr hdr = iter->second;
hdr_enc = hdr->encode;
hdr_use = hdr->use;
}
}
hdr_ret = ht->m_data;
obj->o_set("headerfault", hdr_ret);
}
if (h->function) {
if (serialize_response_call2(head, h->function,
h->function_name.data(), uri,
hdr_ret, version, 0) == SOAP_ENCODED) {
obj->o_set("headerfault", hdr_ret);
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"), NULL);
param = xmlNewChild(body, ns, BAD_CAST("Fault"), NULL);
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(NULL, BAD_CAST("faultcode"));
String str = StringUtil::HtmlEncode(obj->o_get("faultcode"),
StringUtil::QuoteStyle::Double,
"UTF-8", 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,
NULL, 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(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(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"), NULL);
String str = StringUtil::HtmlEncode(obj->o_get("faultcode"),
StringUtil::QuoteStyle::Double,
"UTF-8", true);
node = xmlNewChild(node, ns, BAD_CAST("Value"), NULL);
if (!fault_ns.empty()) {
xmlNsPtr nsptr = encode_add_ns(node, fault_ns.c_str());
xmlChar *code = xmlBuildQName(BAD_CAST(str.data()), nsptr->prefix,
NULL, 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"), NULL);
node = master_to_xml(get_conversion(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(NULL, 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, NULL, 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 ns = encode_add_ns(x, fb->ns.c_str());
xmlSetNs(x, ns);
}
}
} else {
if (sparam->element) {
xmlNsPtr ns = encode_add_ns(x, sparam->element->namens.c_str());
xmlNodeSetName(x, BAD_CAST(sparam->element->name.c_str()));
xmlSetNs(x, ns);
}
}
}
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"), NULL);
for (ArrayIter iter(headers); iter; ++iter) {
soapHeader *h = iter.second().toResource().getTyped<soapHeader>();
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() : NULL;
const char *hdr_name = h->function_name.data();
if (h->retval.isObject() &&
h->retval.toObject().is<c_SoapHeader>()) {
c_SoapHeader *ht = h->retval.toObject().getTyped<c_SoapHeader>();
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 == NULL) {
xmlUnlinkNode(head);
xmlFreeNode(head);
}
}
body = xmlNewChild(envelope, ns, BAD_CAST("Body"), NULL);
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 == NULL && head == NULL) {
xmlFreeDoc(doc);
return NULL;
}
return doc;
}
static void function_to_string(sdlFunctionPtr 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 &&
(type->encode->details.type == KindOfArray ||
type->encode->details.type == SOAP_ENC_ARRAY)) {
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()) {
sdlExtraAttributePtr ext = iterExtra->second;
const char *end = strchr(ext->val.c_str(), '[');
int len;
if (end == NULL) {
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 iter;
sdlExtraAttributeMap::iterator iterExtra;
if (!type->attributes.empty() &&
((iter = type->attributes.find(SOAP_1_2_ENC_NAMESPACE":itemType"))
!= type->attributes.end())) {
if ((iterExtra = iter->second->extraAttributes.find
(WSDL_NAMESPACE":itemType"))
!= iter->second->extraAttributes.end()) {
sdlExtraAttributePtr ext = iterExtra->second;
buf.append(ext->val);
buf.append(' ');
}
} else if (type->elements.size() == 1 &&
(elementType = type->elements[0]) != NULL &&
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() &&
((iter = type->attributes.find(SOAP_1_2_ENC_NAMESPACE":arraySize"))
!= type->attributes.end())) {
if ((iterExtra = iter->second->extraAttributes.find
(WSDL_NAMESPACE":itemType"))
!= iter->second->extraAttributes.end()) {
sdlExtraAttributePtr ext = iterExtra->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) {
int i;
switch (model->kind) {
case XSD_CONTENT_ELEMENT:
type_to_string(model->u_element, buf, level);
buf.append(";\n");
break;
case XSD_CONTENT_ANY:
for (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
static const StaticString s_HTTP_USER_AGENT("HTTP_USER_AGENT");
static const StaticString s__SERVER("_SERVER");
static void send_soap_server_fault(sdlFunctionPtr function, Variant fault,
soapHeader *hdr) {
USE_SOAP_GLOBAL;
bool use_http_error_status = true;
GlobalVariables *g = get_global_variables();
if (g->get(s__SERVER)[s_HTTP_USER_AGENT].toString() == "Shockwave Flash") {
use_http_error_status = false;
}
if (use_http_error_status) {
f_header("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, NULL, NULL, fault, headers, SOAP_GLOBAL(soap_version));
f_ob_end_clean(); // dump all buffered output
xmlChar *buf; int size;
xmlDocDumpMemory(doc_return, &buf, &size);
if (buf) {
echo(String((const char *)buf, size, AttachLiteral));
xmlFree(buf);
}
xmlFreeDoc(doc_return);
}
static void send_soap_server_fault(sdlFunctionPtr function, Exception &e,
soapHeader *hdr) {
USE_SOAP_GLOBAL;
if (SOAP_GLOBAL(use_soap_error_handler)) {
send_soap_server_fault(sdlFunctionPtr(), create_soap_fault(e), NULL);
} else {
throw create_soap_fault(e); // assuming we are in "catch"
}
}
static void throw_soap_server_fault(litstr code, litstr fault) {
send_soap_server_fault(sdlFunctionPtr(), create_soap_fault(code, fault),
NULL);
throw ExitException(1);
}
bool f_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 f_is_soap_fault(CVarRef fault) {
return fault.instanceof(SystemLib::s_SoapFaultClass);
}
int64_t f__soap_active_version() {
USE_SOAP_GLOBAL;
return SOAP_GLOBAL(soap_version);
}
///////////////////////////////////////////////////////////////////////////////
// class SoapServer
c_SoapServer::c_SoapServer(Class* cb) :
ExtObjectData(cb),
m_type(SOAP_FUNCTIONS),
m_version(SOAP_1_1),
m_sdl(NULL),
m_encoding(NULL),
m_typemap(NULL),
m_features(0),
m_send_errors(1) {
}
c_SoapServer::~c_SoapServer() {
}
static const StaticString s_soap_version("soap_version");
static const StaticString s_uri("uri");
static const StaticString s_actor("actor");
static const StaticString s_encoding("encoding");
static const StaticString s_classmap("classmap");
static const StaticString s_typemap("typemap");
static const StaticString s_features("features");
static const StaticString s_cache_wsdl("cache_wsdl");
static const StaticString s_send_errors("send_errors");
static const StaticString s_location("location");
static const StaticString s_style("style");
static const StaticString s_use("use");
static const StaticString s_stream_context("stream_context");
static const StaticString s_login("login");
static const StaticString s_password("password");
static const StaticString s_authentication("authentication");
static const StaticString s_proxy_host("proxy_host");
static const StaticString s_proxy_port("proxy_port");
static const StaticString s_proxy_login("proxy_login");
static const StaticString s_proxy_password("proxy_password");
static const StaticString s_trace("trace");
static const StaticString s_exceptions("exceptions");
static const StaticString s_compression("compression");
static const StaticString s_connection_timeout("connection_timeout");
static const StaticString s_user_agent("user_agent");
static const StaticString s_soapaction("soapaction");
void c_SoapServer::t___construct(CVarRef wsdl,
CArrRef options /* = null_array */) {
USE_SOAP_GLOBAL;
SoapServerScope ss(this);
try {
if (!wsdl.isString() && !wsdl.isNull()) {
throw SoapException("Invalid parameters");
}
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()) {
m_uri = options[s_uri].toString();
} else if (wsdl.isNull()) {
throw SoapException("'uri' option is required in nonWSDL mode");
}
if (options[s_actor].isString()) {
m_actor = options[s_actor].toString();
}
if (options[s_encoding].isString()) {
String tmp = options[s_encoding].toString();
m_encoding = xmlFindCharEncodingHandler(tmp.data());
if (m_encoding == NULL) {
throw SoapException("Invalid 'encoding' option - '%s'", tmp.data());
}
s_soap_data->register_encoding(m_encoding);
}
if (options[s_classmap].isArray()) {
m_classmap = options[s_classmap].toArray();
}
if (options[s_typemap].isArray()) {
typemap_ht = options[s_typemap].toArray();
}
if (options[s_features].isInteger()) {
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)) {
m_send_errors = options[s_send_errors].toInt64();
}
} else if (wsdl.isNull()) {
throw SoapException("'uri' option is required in nonWSDL mode");
}
m_version = version;
m_type = SOAP_FUNCTIONS;
m_soap_functions.functions_all = false;
if (!wsdl.isNull()) {
m_sdl = s_soap_data->get_sdl(wsdl.toString().data(), cache_wsdl);
if (m_uri.isNull()) {
if (!m_sdl->target_ns.empty()) {
m_uri = String(m_sdl->target_ns);
} else {
/*FIXME*/
m_uri = String("http://unknown-uri/");
}
}
}
if (!typemap_ht.empty()) {
m_typemap = soap_create_typemap(m_sdl, typemap_ht);
}
} catch (Exception &e) {
throw create_soap_fault(e);
}
}
void c_SoapServer::t_setclass(int _argc, CStrRef name,
CArrRef _argv /* = null_array */) {
SoapServerScope ss(this);
if (f_class_exists(name, true)) {
m_type = SOAP_CLASS;
m_soap_class.name = name;
m_soap_class.argv = _argv;
m_soap_class.persistance = SOAP_PERSISTENCE_REQUEST;
} else {
raise_warning("Tried to set a non existant class (%s)", name.data());
}
}
void c_SoapServer::t_setobject(CObjRef obj) {
SoapServerScope ss(this);
m_type = SOAP_OBJECT;
m_soap_object = obj;
}
void c_SoapServer::t_addfunction(CVarRef func) {
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) {
m_soap_functions.ft.clear();
m_soap_functions.ftOriginal.clear();
m_soap_functions.functions_all = true;
} else {
raise_warning("Invalid value passed");
}
return;
}
if (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 (!f_function_exists(function_name)) {
raise_warning("Tried to add a non existant function '%s'",
function_name.data());
return;
}
m_soap_functions.ft.set(StringUtil::ToLower(function_name), 1);
m_soap_functions.ftOriginal.set(function_name, 1);
}
}
}
Variant c_SoapServer::t_getfunctions() {
SoapServerScope ss(this);
String class_name;
if (m_type == SOAP_OBJECT) {
class_name = m_soap_object->o_getClassName();
} else if (m_type == SOAP_CLASS) {
class_name = m_soap_class.name;
} else if (m_soap_functions.functions_all) {
return ClassInfo::GetSystemFunctions() + ClassInfo::GetUserFunctions();
} else if (!m_soap_functions.ft.empty()) {
return f_array_keys(m_soap_functions.ftOriginal);
}
ClassInfo::MethodVec methods;
ClassInfo::GetClassMethods(methods, class_name.data());
Array ret = Array::Create();
for (unsigned int i = 0; i < methods.size(); i++) {
ClassInfo::MethodInfo *info = methods[i];
if (info->attribute & ClassInfo::IsPublic) {
ret.append(info->name);
}
}
return ret;
}
static bool valid_function(c_SoapServer *server, Object &soap_obj,
CStrRef fn_name) {
HPHP::Class* cls = nullptr;
if (server->m_type == SOAP_OBJECT || server->m_type == SOAP_CLASS) {
cls = server->m_soap_object->getVMClass();
} else if (server->m_soap_functions.functions_all) {
return f_function_exists(fn_name);
} else if (!server->m_soap_functions.ft.empty()) {
return server->m_soap_functions.ft.exists(StringUtil::ToLower(fn_name));
}
HPHP::Func* f = cls->lookupMethod(fn_name.get());
return (f && f->isPublic());
}
static const StaticString s_HTTP_CONTENT_ENCODING("HTTP_CONTENT_ENCODING");
void c_SoapServer::t_handle(CStrRef request /* = null_string */) {
USE_SOAP_GLOBAL;
SoapServerScope ss(this);
SOAP_GLOBAL(soap_version) = m_version;
// 0. serving WSDL
Transport *transport = g_context->getTransport();
if (transport && transport->getMethod() == Transport::Method::GET &&
transport->getCommand() == "wsdl") {
if (!m_sdl) {
throw_soap_server_fault("Server", "WSDL generation is not supported");
}
f_header("Content-Type: text/xml; charset=utf-8");
Variant ret = f_readfile(m_sdl->source.c_str());
if (same(ret, false)) {
throw_soap_server_fault("Server", "Couldn't find WSDL");
}
return;
}
if (!f_ob_start()) {
throw SoapException("ob_start failed");
}
// 1. process request
String req;
if (!request.isNull()) {
req = request;
} else {
int size;
const char *data = NULL;
if (transport) {
data = (const char *)transport->getPostData(size);
}
if (!data || !*data || !size) {
return;
}
req = String(data, size, AttachLiteral);
GlobalVariables *g = get_global_variables();
if (g->get(s__SERVER).toArray().exists(s_HTTP_CONTENT_ENCODING)) {
String encoding = g->get(s__SERVER)[s_HTTP_CONTENT_ENCODING].toString();
Variant ret;
if (encoding == "gzip" || encoding == "x-gzip") {
ret = f_gzinflate(String(data, size, AttachLiteral));
} else if (encoding == "deflate") {
ret = f_gzuncompress(String(data, size, AttachLiteral));
} 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 == NULL) {
throw_soap_server_fault("Client", "Bad Request");
}
if (xmlGetIntSubset(doc_request) != NULL) {
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(this);
String function_name;
Array params;
int soap_version = 0;
sdlFunctionPtr function;
try {
function = deserialize_function_call(m_sdl, doc_request, m_actor.c_str(),
function_name, params, soap_version,
m_soap_headers);
} catch (Exception &e) {
xmlFreeDoc(doc_request);
send_soap_server_fault(function, e, NULL);
return;
}
xmlFreeDoc(doc_request);
// 3. we may need an object
Object soap_obj;
if (m_type == SOAP_OBJECT) {
soap_obj = m_soap_object;
} else if (m_type == SOAP_CLASS) {
try {
soap_obj = create_object(m_soap_class.name,
m_soap_class.argv);
} catch (Exception &e) {
send_soap_server_fault(function, e, NULL);
return;
}
}
// 4. process soap headers
for (ArrayIter iter(m_soap_headers); iter; ++iter) {
soapHeader *h = iter.second().toResource().getTyped<soapHeader>();
if (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(this, soap_obj, fn_name)) {
try {
if (m_type == SOAP_CLASS || m_type == SOAP_OBJECT) {
h->retval = vm_call_user_func
(CREATE_VECTOR2(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(this, soap_obj, fn_name)) {
try {
if (m_type == SOAP_CLASS || m_type == SOAP_OBJECT) {
retval = vm_call_user_func
(CREATE_VECTOR2(soap_obj, fn_name), params);
} else {
retval = vm_call_user_func(fn_name, params);
}
} catch (Exception &e) {
send_soap_server_fault(function, e, NULL);
return;
}
if (retval.isObject() &&
retval.toObject()->instanceof(SystemLib::s_SoapFaultClass)) {
send_soap_server_fault(function, retval, NULL);
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 = NULL;
try {
doc_return = serialize_response_call(function, response_name.data(),
m_uri.c_str(), retval, m_soap_headers,
soap_version);
} catch (Exception &e) {
send_soap_server_fault(function, e, NULL);
return;
}
// 7. throw away all buffered output so far, so we can send back a clean
// soap resposne
f_ob_end_clean();
// 8. special case
if (doc_return == NULL) {
f_header("HTTP/1.1 202 Accepted");
f_header("Content-Length: 0");
return;
}
// 9. XML response
xmlChar *buf; int size;
xmlDocDumpMemory(doc_return, &buf, &size);
xmlFreeDoc(doc_return);
if (buf == NULL || size == 0) {
if (buf) xmlFree(buf);
throw SoapException("Dump memory failed");
}
output_xml_header(soap_version);
if (buf) {
echo(String((char*)buf, size, AttachLiteral));
xmlFree(buf);
}
}
void c_SoapServer::t_setpersistence(int64_t mode) {
SoapServerScope ss(this);
if (m_type == SOAP_CLASS) {
if (mode == SOAP_PERSISTENCE_SESSION || mode == SOAP_PERSISTENCE_REQUEST) {
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 c_SoapServer::t_fault(CVarRef code, CStrRef fault,
CStrRef actor /* = null_string */,
CVarRef detail /* = null */,
CStrRef name /* = null_string */) {
SoapServerScope ss(this);
Object obj(SystemLib::AllocSoapFaultObject(code, fault, actor, detail, name));
send_soap_server_fault(sdlFunctionPtr(), obj, NULL);
}
void c_SoapServer::t_addsoapheader(CObjRef fault) {
SoapServerScope ss(this);
soapHeader *p = NEWOBJ(soapHeader)();
Resource obj(p);
p->function = NULL;
p->mustUnderstand = false;
p->retval = fault;
p->hdr = NULL;
m_soap_headers.append(obj);
}
///////////////////////////////////////////////////////////////////////////////
// class SoapClient
c_SoapClient::c_SoapClient(Class* cb) :
ExtObjectDataFlags<ObjectData::HasCall>(cb),
m_soap_version(SOAP_1_1),
m_sdl(NULL),
m_encoding(NULL),
m_typemap(NULL),
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(0),
m_use11(true),
m_compression(false),
m_exceptions(true),
m_trace(false) {
}
c_SoapClient::~c_SoapClient() {
}
void c_SoapClient::t___construct(CVarRef wsdl,
CArrRef options /* = null_array */) {
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()) {
m_location = options[s_location];
if (wsdl.isNull()) {
/* Fetching non-WSDL mode options */
m_uri = options[s_uri];
m_style = options[s_style].toInt32(); // SOAP_RPC || SOAP_DOCUMENT
m_use = options[s_use].toInt32(); // SOAP_LITERAL || SOAP_ENCODED
if (m_uri.empty()) {
throw SoapException("'uri' option is required in nonWSDL mode");
}
if (m_location.empty()) {
throw SoapException("'location' option is required in nonWSDL mode");
}
}
if (options.exists(s_stream_context)) {
StreamContext *sc = NULL;
if (options[s_stream_context].isResource()) {
sc = options[s_stream_context].toResource()
.getTyped<StreamContext>();
}
if (!sc) {
throw SoapException("'stream_context' is not a StreamContext");
}
m_stream_context_options = sc->m_options;
}
if (options.exists(s_soap_version)) {
m_soap_version = options[s_soap_version].toInt32();
}
m_login = options[s_login].toString();
m_password = options[s_password].toString();
m_authentication = options[s_authentication].toInt32();
m_proxy_host = options[s_proxy_host].toString();
m_proxy_port = options[s_proxy_port].toInt32();
m_proxy_login = options[s_proxy_login].toString();
m_proxy_password = options[s_proxy_password].toString();
m_trace = options[s_trace].toBoolean();
if (options.exists(s_exceptions)) {
m_exceptions = options[s_exceptions].toBoolean();
}
if (options.exists(s_compression)) {
m_compression = options[s_compression].toBoolean();
}
String encoding = options[s_encoding].toString();
if (!encoding.empty()) {
m_encoding = xmlFindCharEncodingHandler(encoding.data());
if (m_encoding == NULL) {
throw SoapException("Invalid 'encoding' option - '%s'",
encoding.data());
}
s_soap_data->register_encoding(m_encoding);
}
m_classmap = options[s_classmap].toArray();
m_features = options[s_features].toInt32();
m_connection_timeout = options[s_connection_timeout].toInt64();
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) = m_soap_version;
String swsdl = wsdl.toString();
if (swsdl.find("http://") == 0 || swsdl.find("https://") == 0) {
HttpClient http(m_connection_timeout, m_max_redirect, m_use11, true);
if (!m_proxy_host.empty() && m_proxy_port) {
http.proxy(m_proxy_host.data(), m_proxy_port, m_proxy_login.data(),
m_proxy_password.data());
}
if (!m_login.empty()) {
http.auth(m_login.data(), m_password.data(), !m_digest);
}
http.setStreamContextOptions(m_stream_context_options);
m_sdl = s_soap_data->get_sdl(swsdl.data(), cache_wsdl, &http);
} else {
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()) {
m_typemap = soap_create_typemap(m_sdl, arr);
}
}
} catch (Exception &e) {
throw create_soap_fault(e);
}
}
Variant c_SoapClient::t___call(Variant name, Variant args) {
return t___soapcall(name.toString(), args.toArray());
}
Variant c_SoapClient::t___soapcall(CStrRef name, CArrRef args,
CArrRef options /* = null_array */,
CVarRef input_headers /* = null_variant */,
VRefParam output_headers /* = null */) {
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 = 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::Create();
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().is<c_SoapHeader>()) {
soap_headers = CREATE_VECTOR1(input_headers);
} else{
raise_warning("Invalid SOAP header");
return uninit_null();
}
if (!m_default_headers.isNull()) {
soap_headers.merge(m_default_headers.toArray());
}
output_headers = Array::Create();
if (m_trace) {
m_last_request.reset();
m_last_response.reset();
}
if (location.empty()) {
location = m_location;
}
m_soap_fault.reset();
SoapServiceScope sss(this);
Variant return_value;
bool ret = false;
xmlDocPtr request = NULL;
if (m_sdl) {
sdlFunctionPtr fn = get_function(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
(this, fn, NULL, fnb->input.ns.c_str(), args, soap_headers);
ret = do_request(this, request, location.data(),
fnb->soapAction.c_str(), m_soap_version, one_way,
response);
} else {
request = serialize_function_call
(this, fn, NULL, m_sdl->target_ns.c_str(), args, soap_headers);
ret = do_request(this, request, location.data(), NULL,
m_soap_version, one_way, response);
}
} catch (Exception &e) {
xmlFreeDoc(request);
throw create_soap_fault(e);
}
xmlFreeDoc(request);
if (ret && response.isString()) {
encode_reset_ns();
String sresponse = response.toString();
ret = parse_packet_soap(this, sresponse.data(), sresponse.size(),
fn, NULL, 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");
m_soap_fault = create_soap_fault("Client", error.detach());
}
} else {
string action;
if (m_uri.empty()) {
m_soap_fault =
create_soap_fault("Client", "Error finding \"uri\" property");
} else if (location.empty()) {
m_soap_fault =
create_soap_fault("Client", "Error could not find \"location\" "
"property");
} else {
request = serialize_function_call
(this, sdlFunctionPtr(), name.data(), m_uri.data(), args,
soap_headers);
if (soap_action.empty()) {
action += 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(),
m_soap_version, 0, response);
} catch (Exception &e) {
xmlFreeDoc(request);
throw create_soap_fault(e);
}
xmlFreeDoc(request);
if (ret && response.isString()) {
encode_reset_ns();
String sresponse = response.toString();
ret = parse_packet_soap(this, sresponse.data(), sresponse.size(),
sdlFunctionPtr(), name.data(), return_value,
output_headers);
encode_finish();
}
}
}
if (!ret && m_soap_fault.isNull()) {
m_soap_fault = create_soap_fault("Client", "Unknown Error");
}
if (!m_soap_fault.isNull()) {
throw m_soap_fault.toObject();
}
return return_value;
}
Variant c_SoapClient::t___getlastrequest() {
return m_last_request;
}
Variant c_SoapClient::t___getlastresponse() {
return m_last_response;
}
Variant c_SoapClient::t___getlastrequestheaders() {
return m_last_request_headers;
}
Variant c_SoapClient::t___getlastresponseheaders() {
return m_last_response_headers;
}
Variant c_SoapClient::t___getfunctions() {
SoapClientScope ss(this);
if (m_sdl) {
Array ret = Array::Create();
for (sdlFunctionMap::iterator iter = m_sdl->functions.begin();
iter != m_sdl->functions.end(); ++iter) {
StringBuffer sb;
function_to_string(iter->second, sb);
ret.append(sb.detach());
}
return ret;
}
return uninit_null();
}
Variant c_SoapClient::t___gettypes() {
SoapClientScope ss(this);
if (m_sdl) {
Array ret = Array::Create();
for (unsigned int i = 0; i < m_sdl->types.size(); i++) {
StringBuffer sb;
type_to_string(m_sdl->types[i].get(), sb, 0);
ret.append(sb.detach());
}
return ret;
}
return uninit_null();
}
Variant c_SoapClient::t___dorequest(CStrRef buf, CStrRef location, CStrRef action,
int64_t version, bool oneway /* = false */) {
if (location.empty()) {
m_soap_fault =
Object(SystemLib::AllocSoapFaultObject("HTTP", "Unable to parse URL"));
return uninit_null();
}
USE_SOAP_GLOBAL;
SoapClientScope ss(this);
HeaderMap headers;
String buffer(buf);
// compression
if (m_compression > 0) {
if (m_compression & SOAP_COMPRESSION_ACCEPT) {
headers["Accept-Encoding"].push_back("gzip, deflate");
}
int level = m_compression & 0x0f;
if (level > 9) level = 9;
if (level > 0) {
Variant ret;
if (m_compression & SOAP_COMPRESSION_DEFLATE) {
ret = f_gzcompress(buffer, level);
headers["Content-Encoding"].push_back("deflate");
} else {
ret = f_gzencode(buffer, level);
headers["Content-Encoding"].push_back("gzip");
}
if (!ret.isString()) return uninit_null();
buffer = ret.toString();
}
}
// prepare more headers
if (!m_user_agent.empty()) {
headers["User-Agent"].push_back(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(m_connection_timeout, m_max_redirect, m_use11, true);
if (!m_proxy_host.empty() && m_proxy_port) {
http.proxy(m_proxy_host.data(), m_proxy_port, m_proxy_login.data(),
m_proxy_password.data());
}
if (!m_login.empty()) {
http.auth(m_login.data(), m_password.data(), !m_digest);
}
http.setStreamContextOptions(m_stream_context_options);
StringBuffer response;
int code = http.post(location.data(), buffer.data(), buffer.size(), response,
&headers);
if (code == 0) {
String msg = "Failed Sending HTTP Soap request";
if (!http.getLastError().empty()) {
msg += ": " + http.getLastError();
}
m_soap_fault = Object(SystemLib::AllocSoapFaultObject(
"HTTP", msg));
return uninit_null();
}
if (code != 200) {
String msg = response.detach();
if (msg.empty()) {
msg = HttpProtocol::GetReasonString(code);
}
m_soap_fault = Object(SystemLib::AllocSoapFaultObject("HTTP", msg));
return uninit_null();
}
// return response
if (SOAP_GLOBAL(features) & SOAP_WAIT_ONE_WAY_CALLS) {
oneway = false;
}
if (oneway) {
return "";
}
return response.detach();
}
Variant c_SoapClient::t___setcookie(CStrRef name,
CStrRef value /* = null_string */) {
if (!value.isNull()) {
m_cookies.set(name, CREATE_VECTOR1(value));
} else {
const Variant* t = o_realProp("_cookies", RealPropUnchecked);
if (t && t->isInitialized()) {
m_cookies.remove(name);
}
}
return uninit_null();
}
Variant c_SoapClient::t___setlocation(CStrRef new_location /* = null_string */){
Variant ret = m_location;
m_location = new_location;
return ret;
}
bool c_SoapClient::t___setsoapheaders(CVarRef headers /* = null_variant */) {
if (headers.isNull()) {
m_default_headers = uninit_null();
} else if (headers.isArray()) {
Array arr = headers.toArray();
verify_soap_headers_array(arr);
m_default_headers = arr;
} else if (headers.isObject() && headers.toObject().is<c_SoapHeader>()) {
m_default_headers = CREATE_VECTOR1(headers);
} else {
raise_warning("Invalid SOAP header");
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// class SoapVar
c_SoapVar::c_SoapVar(Class* cb) : ExtObjectData(cb) {
}
c_SoapVar::~c_SoapVar() {
}
void c_SoapVar::t___construct(CVarRef data, CVarRef type,
CStrRef type_name /* = null_string */,
CStrRef type_namespace /* = null_string */,
CStrRef node_name /* = null_string */,
CStrRef node_namespace /* = null_string */) {
USE_SOAP_GLOBAL;
if (type.isNull()) {
m_type = UNKNOWN_TYPE;
} else {
std::map<int, encodePtr> &defEncIndex = SOAP_GLOBAL(defEncIndex);
int64_t ntype = type.toInt64();
if (defEncIndex.find(ntype) != defEncIndex.end()) {
m_type = ntype;
} else {
raise_warning("Invalid type ID");
return;
}
}
if (data.toBoolean()) m_value = data;
if (!type_name.empty()) m_stype = type_name;
if (!type_namespace.empty()) m_ns = type_namespace;
if (!node_name.empty()) m_name = node_name;
if (!node_namespace.empty()) m_namens = node_namespace;
}
///////////////////////////////////////////////////////////////////////////////
// class SoapParam
c_SoapParam::c_SoapParam(Class* cb) : ExtObjectData(cb) {
}
c_SoapParam::~c_SoapParam() {
}
void c_SoapParam::t___construct(CVarRef data, CStrRef name) {
if (name.empty()) {
raise_warning("Invalid parameter name");
return;
}
m_name = name;
m_data = data;
}
///////////////////////////////////////////////////////////////////////////////
// class SoapHeader
c_SoapHeader::c_SoapHeader(Class* cb) :
ExtObjectData(cb) {
}
c_SoapHeader::~c_SoapHeader() {
}
void c_SoapHeader::t___construct(CStrRef ns, CStrRef name,
CVarRef data /* = null */,
bool mustunderstand /* = false */,
CVarRef actor /* = null */) {
if (ns.empty()) {
raise_warning("Invalid namespace");
return;
}
if (name.empty()) {
raise_warning("Invalid header name");
return;
}
m_namespace = ns;
m_name = name;
m_data = data;
m_mustUnderstand = mustunderstand;
if (actor.isInteger() &&
(actor.toInt64() == SOAP_ACTOR_NEXT ||
actor.toInt64() == SOAP_ACTOR_NONE ||
actor.toInt64() == SOAP_ACTOR_UNLIMATERECEIVER)) {
m_actor = actor.toInt64();
} else if (actor.isString() && !actor.toString().empty()) {
m_actor = actor.toString();
} else if (!actor.isNull()) {
raise_warning("Invalid actor");
}
}
///////////////////////////////////////////////////////////////////////////////
}