Arquivos
hhvm/hphp/runtime/ext/ext_soap.cpp
T
Edwin Smith 9f338c96f9 Remove Variant::operator[](litstr)
And its remaining call sites.
2013-05-10 10:54:41 -07:00

2868 linhas
91 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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 <runtime/ext/ext_soap.h>
#include <runtime/base/util/http_client.h>
#include <runtime/base/server/http_protocol.h>
#include <runtime/base/class_info.h>
#include <runtime/ext/soap/soap.h>
#include <runtime/ext/soap/packet.h>
#include <runtime/base/string_util.h>
#include <runtime/ext/ext_zlib.h>
#include <runtime/ext/ext_network.h>
#include <runtime/ext/ext_array.h>
#include <runtime/ext/ext_function.h>
#include <runtime/ext/ext_class.h>
#include <runtime/ext/ext_output.h>
#include <runtime/ext/ext_stream.h>
#include <system/lib/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, -1, 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) {
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) {
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)();
Object 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].toObject().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::DoubleQuotes,
"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::DoubleQuotes,
"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().toObject().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 void send_soap_server_fault(sdlFunctionPtr function, Variant fault,
soapHeader *hdr) {
USE_SOAP_GLOBAL;
bool use_http_error_status = true;
SystemGlobals *g = (SystemGlobals*)get_global_variables();
if (g->GV(_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(Object(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(VM::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::VM::Class* cls;
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::VM::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::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);
SystemGlobals *g = (SystemGlobals*)get_global_variables();
if (g->GV(_SERVER).toArray().exists(s_HTTP_CONTENT_ENCODING)) {
String encoding = g->GV(_SERVER)[s_HTTP_CONTENT_ENCODING];
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().toObject().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)();
Object 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(VM::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].isObject()) {
sc = options[s_stream_context].toObject()
.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, args);
}
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);
}
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(VM::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) 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(VM::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(VM::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");
}
}
///////////////////////////////////////////////////////////////////////////////
}