static const char rcsid[] = "#(@) $Id:";
#ifdef _WIN32
#include "xmlrpc_win32.h"
#endif
#include <string.h>
#include <stdlib.h>
#include "xml_to_soap.h"
#include "base64.h"
#define TOKEN_ANY "xsd:ur-type"
#define TOKEN_ARRAY "SOAP-ENC:Array"
#define TOKEN_ARRAY_TYPE "SOAP-ENC:arrayType"
#define TOKEN_BASE64 "SOAP-ENC:base64"
#define TOKEN_BOOLEAN "xsd:boolean"
#define TOKEN_DATETIME "xsd:timeInstant"
#define TOKEN_DOUBLE "xsd:double"
#define TOKEN_FLOAT "xsd:float"
#define TOKEN_ID "id"
#define TOKEN_INT "xsd:int"
#define TOKEN_NULL "xsi:null"
#define TOKEN_STRING "xsd:string"
#define TOKEN_STRUCT "xsd:struct"
#define TOKEN_TYPE "xsi:type"
#define TOKEN_FAULT "SOAP-ENV:Fault"
#define TOKEN_MUSTUNDERSTAND "SOAP-ENV:mustUnderstand"
#define TOKEN_ACTOR "SOAP-ENV:actor"
#define TOKEN_ACTOR_NEXT "http://schemas.xmlsoap.org/soap/actor/next"
#define TOKEN_XMLRPC_FAULTCODE "faultCode"
#define TOKEN_XMLRPC_FAULTSTRING "faultString"
#define TOKEN_SOAP_FAULTCODE "faultcode"
#define TOKEN_SOAP_FAULTSTRING "faultstring"
#define TOKEN_SOAP_FAULTDETAILS "details"
#define TOKEN_SOAP_FAULTACTOR "actor"
static inline int is_soap_type(const char* soap_type) {
return(strstr(soap_type, "SOAP-ENC:") || strstr(soap_type, "xsd:")) ? 1 : 0;
}
static xml_element_attr* new_attr(const char* key, const char* val) {
xml_element_attr* attr = malloc(sizeof(xml_element_attr));
if (attr) {
attr->key = key ? strdup(key) : NULL;
attr->val = val ? strdup(val) : NULL;
}
return attr;
}
struct array_info {
char kids_type[128];
unsigned long size;
};
static struct array_info* parse_array_type_info(const char* array_type) {
struct array_info* ai = NULL;
if (array_type) {
ai = (struct array_info*)calloc(1, sizeof(struct array_info));
if (ai) {
char buf[128], *p;
snprintf(buf, sizeof(buf), "%s", array_type);
p = strchr(buf, '[');
if (p) {
*p = 0;
}
strcpy(ai->kids_type, buf);
}
}
return ai;
}
static const char* get_array_soap_type(XMLRPC_VALUE node) {
XMLRPC_VALUE_TYPE_EASY type = xmlrpc_type_none;
XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
int loopCount = 0;
const char* soapType = TOKEN_ANY;
type = XMLRPC_GetValueTypeEasy(xIter);
xIter = XMLRPC_VectorNext(node);
while (xIter) {
if ( (XMLRPC_GetValueTypeEasy(xIter) != type) || loopCount >= 50) {
type = xmlrpc_type_none;
break;
}
loopCount ++;
xIter = XMLRPC_VectorNext(node);
}
switch (type) {
case xmlrpc_type_none:
soapType = TOKEN_ANY;
break;
case xmlrpc_type_empty:
soapType = TOKEN_NULL;
break;
case xmlrpc_type_int:
soapType = TOKEN_INT;
break;
case xmlrpc_type_double:
soapType = TOKEN_DOUBLE;
break;
case xmlrpc_type_boolean:
soapType = TOKEN_BOOLEAN;
break;
case xmlrpc_type_string:
soapType = TOKEN_STRING;
break;
case xmlrpc_type_base64:
soapType = TOKEN_BASE64;
break;
case xmlrpc_type_datetime:
soapType = TOKEN_DATETIME;
break;
case xmlrpc_type_struct:
soapType = TOKEN_STRUCT;
break;
case xmlrpc_type_array:
soapType = TOKEN_ARRAY;
break;
case xmlrpc_type_mixed:
soapType = TOKEN_STRUCT;
break;
}
return soapType;
}
static inline int get_fault_type(XMLRPC_VALUE node) {
if (XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTCODE) &&
XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTSTRING)) {
return 1;
}
else if (XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTCODE) &&
XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTSTRING)) {
return 2;
}
return 0;
}
static XMLRPC_VALUE gen_fault_xmlrpc(XMLRPC_VALUE node, xml_element* el_target) {
XMLRPC_VALUE xDup = XMLRPC_DupValueNew(node);
XMLRPC_VALUE xCode = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTCODE);
XMLRPC_VALUE xStr = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTSTRING);
XMLRPC_SetValueID(xCode, TOKEN_SOAP_FAULTCODE, 0);
XMLRPC_SetValueID(xStr, TOKEN_SOAP_FAULTSTRING, 0);
switch (XMLRPC_GetValueInt(xCode)) {
case -32700:
case -32701:
case -32702:
case -32600:
case -32601:
case -32602:
XMLRPC_SetValueString(xCode, "SOAP-ENV:Client", 0);
break;
case -32603:
case -32500:
case -32400:
case -32300:
XMLRPC_SetValueString(xCode, "SOAP-ENV:Server", 0);
break;
}
return xDup;
}
static XMLRPC_VALUE gen_soap_fault(const char* fault_code, const char* fault_string,
const char* actor, const char* details) {
XMLRPC_VALUE xReturn = XMLRPC_CreateVector(TOKEN_FAULT, xmlrpc_vector_struct);
XMLRPC_AddValuesToVector(xReturn,
XMLRPC_CreateValueString(TOKEN_SOAP_FAULTCODE, fault_code, 0),
XMLRPC_CreateValueString(TOKEN_SOAP_FAULTSTRING, fault_string, 0),
XMLRPC_CreateValueString(TOKEN_SOAP_FAULTACTOR, actor, 0),
XMLRPC_CreateValueString(TOKEN_SOAP_FAULTDETAILS, details, 0),
NULL);
return xReturn;
}
XMLRPC_VALUE xml_element_to_SOAP_REQUEST_worker(XMLRPC_REQUEST request,
XMLRPC_VALUE xParent,
struct array_info* parent_array,
XMLRPC_VALUE xCurrent,
xml_element* el,
int depth) {
XMLRPC_REQUEST_TYPE rtype = xmlrpc_request_none;
if (!xCurrent) {
xCurrent = XMLRPC_CreateValueEmpty();
}
depth ++;
if (el && el->name) {
const char* id = NULL;
const char* type = NULL, *arrayType=NULL, *actor = NULL;
xml_element_attr* attr_iter = Q_Head(&el->attrs);
int b_must_understand = 0;
if (is_soap_type(el->name)) {
type = el->name;
}
else if (XMLRPC_GetVectorType(xParent) != xmlrpc_vector_array) {
id = el->name;
if(!strcmp(id, "item")) {
}
}
while (attr_iter) {
if (!strcmp(attr_iter->key, TOKEN_TYPE)) {
type = attr_iter->val;
}
else if (!strcmp(attr_iter->key, TOKEN_ARRAY_TYPE)) {
arrayType = attr_iter->val;
}
else if (!strcmp(attr_iter->key, TOKEN_MUSTUNDERSTAND)) {
b_must_understand = strchr(attr_iter->val, '1') ? 1 : 0;
}
else if (!strcmp(attr_iter->key, TOKEN_ACTOR)) {
actor = attr_iter->val;
}
attr_iter = Q_Next(&el->attrs);
}
if (b_must_understand) {
if (!actor || !strcmp(actor, TOKEN_ACTOR_NEXT)) {
XMLRPC_RequestSetError(request,
gen_soap_fault("SOAP-ENV:MustUnderstand",
"SOAP Must Understand Error",
"", ""));
return xCurrent;
}
}
if (id) {
XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact);
}
if (depth == 3) {
const char* methodname = el->name;
char* p = NULL;
rtype =
#ifdef strcasestr
strcasestr(el->name, "response") ? xmlrpc_request_response : xmlrpc_request_call;
#else
strstr(el->name, "esponse") ? xmlrpc_request_response : xmlrpc_request_call;
#endif
XMLRPC_RequestSetRequestType(request, rtype);
p = strchr(el->name, ':');
if (p) {
methodname = p + 1;
}
if (rtype == xmlrpc_request_call) {
XMLRPC_RequestSetMethodName(request, methodname);
}
}
if (!Q_Size(&el->children)) {
if (!type && parent_array && parent_array->kids_type[0]) {
type = parent_array->kids_type;
}
if (!type || !strcmp(type, TOKEN_STRING)) {
XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len);
}
else if (!strcmp(type, TOKEN_INT)) {
XMLRPC_SetValueInt(xCurrent, atoi(el->text.str));
}
else if (!strcmp(type, TOKEN_BOOLEAN)) {
XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str));
}
else if (!strcmp(type, TOKEN_DOUBLE) ||
!strcmp(type, TOKEN_FLOAT)) {
XMLRPC_SetValueDouble(xCurrent, atof(el->text.str));
}
else if (!strcmp(type, TOKEN_NULL)) {
}
else if (!strcmp(type, TOKEN_DATETIME)) {
XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str);
}
else if (!strcmp(type, TOKEN_BASE64)) {
struct buffer_st buf;
base64_decode_xmlrpc(&buf, el->text.str, el->text.len);
XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset);
buffer_delete(&buf);
}
}
else {
struct array_info* ai = NULL;
xml_element* iter = (xml_element*)Q_Head(&el->children);
if (!type || !strcmp(type, TOKEN_STRUCT)) {
XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct);
}
else if (!strcmp(type, TOKEN_ARRAY) || arrayType != NULL) {
ai = parse_array_type_info(arrayType); XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array);
}
else {
XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
}
while ( iter && !XMLRPC_RequestGetError(request) ) {
XMLRPC_VALUE xNext = NULL;
if ( depth <= 2 ||
(rtype == xmlrpc_request_response && depth <= 3) ) {
xml_element_to_SOAP_REQUEST_worker(request, NULL, ai, xCurrent, iter, depth);
}
else {
xNext = XMLRPC_CreateValueEmpty();
xml_element_to_SOAP_REQUEST_worker(request, xCurrent, ai, xNext, iter, depth);
XMLRPC_AddValueToVector(xCurrent, xNext);
}
iter = (xml_element*)Q_Next(&el->children);
}
if (ai) {
free(ai);
}
}
}
return xCurrent;
}
XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el)
{
return xml_element_to_SOAP_REQUEST_worker(NULL, NULL, NULL, NULL, el, 0);
}
XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el)
{
if (request) {
return XMLRPC_RequestSetData(request, xml_element_to_SOAP_REQUEST_worker(request, NULL, NULL, NULL, el, 0));
}
return NULL;
}
xml_element* SOAP_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) {
#define BUF_SIZE 128
xml_element* elem_val = NULL;
if (node) {
int bFreeNode = 0;
char buf[BUF_SIZE];
XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(node);
char* pName = NULL, *pAttrType = NULL;
elem_val = xml_elem_new();
switch (type) {
case xmlrpc_type_struct:
case xmlrpc_type_mixed:
case xmlrpc_type_array:
if (type == xmlrpc_type_array) {
const char* type = get_array_soap_type(node);
xml_element_attr* attr_array_type = NULL;
snprintf(buf, sizeof(buf), "%s[%i]", type, XMLRPC_VectorSize(node));
attr_array_type = new_attr(TOKEN_ARRAY_TYPE, buf);
Q_PushTail(&elem_val->attrs, attr_array_type);
pAttrType = TOKEN_ARRAY;
}
else if (type == xmlrpc_type_struct) {
int fault_type = get_fault_type(node);
if (fault_type) {
if (fault_type == 1) {
node = gen_fault_xmlrpc(node, elem_val);
bFreeNode = 1;
}
pName = TOKEN_FAULT;
}
}
{
XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
while ( xIter ) {
xml_element* next_el = SOAP_to_xml_element_worker(request, xIter);
if (next_el) {
Q_PushTail(&elem_val->children, next_el);
}
xIter = XMLRPC_VectorNext(node);
}
}
break;
case xmlrpc_type_empty:
pAttrType = TOKEN_NULL;
break;
case xmlrpc_type_string:
pAttrType = TOKEN_STRING;
simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
break;
case xmlrpc_type_int:
pAttrType = TOKEN_INT;
snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
simplestring_add(&elem_val->text, buf);
break;
case xmlrpc_type_boolean:
pAttrType = TOKEN_BOOLEAN;
snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
simplestring_add(&elem_val->text, buf);
break;
case xmlrpc_type_double:
pAttrType = TOKEN_DOUBLE;
snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
simplestring_add(&elem_val->text, buf);
break;
case xmlrpc_type_datetime:
{
time_t tt = XMLRPC_GetValueDateTime(node);
struct tm *tm = localtime (&tt);
pAttrType = TOKEN_DATETIME;
if(strftime (buf, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", tm)) {
simplestring_add(&elem_val->text, buf);
}
}
break;
case xmlrpc_type_base64:
{
struct buffer_st buf;
pAttrType = TOKEN_BASE64;
base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
simplestring_addn(&elem_val->text, buf.data, buf.offset );
buffer_delete(&buf);
}
break;
break;
default:
break;
}
if (!pName) {
if (pAttrType) {
pName = (char*)XMLRPC_GetValueID(node);
if (pName) {
Q_PushTail(&elem_val->attrs, new_attr(TOKEN_TYPE, pAttrType));
}
else {
pName = pAttrType;
}
}
else {
pName = (char*)XMLRPC_GetValueID(node);
if (!pName) {
pName = "item";
}
}
}
elem_val->name = strdup(pName);
if (bFreeNode) {
XMLRPC_CleanupValue(node);
}
}
return elem_val;
}
xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node) {
return SOAP_to_xml_element_worker(NULL, node);
}
xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
xml_element* root = xml_elem_new();
if (root) {
xml_element* body = xml_elem_new();
root->name = strdup("SOAP-ENV:Envelope");
Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"));
Q_PushTail(&root->attrs, new_attr("xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance"));
Q_PushTail(&root->attrs, new_attr("xmlns:xsd", "http://www.w3.org/1999/XMLSchema"));
Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"));
Q_PushTail(&root->attrs, new_attr("xmlns:si", "http://soapinterop.org/xsd"));
Q_PushTail(&root->attrs, new_attr("xmlns:ns6", "http://testuri.org"));
Q_PushTail(&root->attrs, new_attr("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/"));
if (body) {
xml_element* el_serialized =
SOAP_to_xml_element_worker(request,
XMLRPC_RequestGetData(request));
if (el_serialized && !strcmp(el_serialized->name, TOKEN_FAULT)) {
Q_PushTail(&body->children, el_serialized);
}
else {
xml_element* rpc = xml_elem_new();
if (rpc) {
const char* methodname = XMLRPC_RequestGetMethodName(request);
XMLRPC_REQUEST_TYPE rtype = XMLRPC_RequestGetRequestType(request);
if (rtype == xmlrpc_request_call) {
if (methodname) {
rpc->name = strdup(methodname);
}
}
else {
char buf[128];
snprintf(buf, sizeof(buf), "%s%s",
methodname ? methodname : "",
"Response");
rpc->name = strdup(buf);
}
if (rpc->name) {
if(el_serialized) {
if(Q_Size(&el_serialized->children) && rtype == xmlrpc_request_call) {
xml_element* iter = (xml_element*)Q_Head(&el_serialized->children);
while(iter) {
Q_PushTail(&rpc->children, iter);
iter = (xml_element*)Q_Next(&el_serialized->children);
}
xml_elem_free_non_recurse(el_serialized);
}
else {
Q_PushTail(&rpc->children, el_serialized);
}
}
Q_PushTail(&body->children, rpc);
}
else {
}
}
}
body->name = strdup("SOAP-ENV:Body");
Q_PushTail(&root->children, body);
}
}
return root;
}