mirror of
https://github.com/php/php-src.git
synced 2024-10-20 16:02:44 +00:00
671 lines
22 KiB
C
671 lines
22 KiB
C
/*
|
|
This file is part of libXMLRPC - a C library for xml-encoded function calls.
|
|
|
|
Author: Dan Libby (dan@libby.com)
|
|
*/
|
|
|
|
|
|
/*-**********************************************************************
|
|
* TODO: *
|
|
* - [SOAP-ENC:position] read sparse arrays (and write?) *
|
|
* - [SOAP-ENC:offset] read partially transmitted arrays (and write?) *
|
|
* - read "flattened" multi-dimensional arrays. (don't bother writing) *
|
|
* *
|
|
* BUGS: *
|
|
* - does not read schema. thus only knows soap pre-defined types. *
|
|
* - references (probably) do not work. untested. *
|
|
* - does not expose SOAP-ENV:Header to application at all. *
|
|
* - does not use namespaces correctly, thus: *
|
|
* - namespaces are hard-coded in comparison tokens *
|
|
* - if a sender uses another namespace identifer, it will break *
|
|
************************************************************************/
|
|
|
|
|
|
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"
|
|
|
|
/* list of tokens used in vocab */
|
|
#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"
|
|
|
|
|
|
/* determine if a string represents a soap type, as used in element names */
|
|
static inline int is_soap_type(const char* soap_type) {
|
|
return(strstr(soap_type, "SOAP-ENC:") || strstr(soap_type, "xsd:")) ? 1 : 0;
|
|
}
|
|
|
|
/* utility func to generate a new attribute. possibly should be in xml_element.c?? */
|
|
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;
|
|
/* ... ? */
|
|
};
|
|
|
|
|
|
/* parses soap arrayType attribute to generate an array_info structure.
|
|
* TODO: should deal with sparse, flattened, & multi-dimensional arrays
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* performs heuristics on an xmlrpc_vector_array to determine
|
|
* appropriate soap arrayType string.
|
|
*/
|
|
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) {
|
|
/* 50 seems like a decent # of loops. That will likely
|
|
* cover most cases. Any more and we start to sacrifice
|
|
* performance.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* determines whether a node is a fault or not, and of which type:
|
|
* 0 = not a fault,
|
|
* 1 = xmlrpc style fault
|
|
* 2 = soap style fault.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* input: an XMLRPC_VALUE representing a fault struct in xml-rpc style.
|
|
* output: an XMLRPC_VALUE representing a fault struct in soap style,
|
|
* with xmlrpc codes mapped to soap codes, and all other values preserved.
|
|
* note that the returned value is a completely new value, and must be freed.
|
|
* the input value is untouched.
|
|
*/
|
|
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);
|
|
|
|
/* rough mapping of xmlrpc fault codes to soap codes */
|
|
switch (XMLRPC_GetValueInt(xCode)) {
|
|
case -32700: /* "parse error. not well formed", */
|
|
case -32701: /* "parse error. unsupported encoding" */
|
|
case -32702: /* "parse error. invalid character for encoding" */
|
|
case -32600: /* "server error. invalid xml-rpc. not conforming to spec." */
|
|
case -32601: /* "server error. requested method not found" */
|
|
case -32602: /* "server error. invalid method parameters" */
|
|
XMLRPC_SetValueString(xCode, "SOAP-ENV:Client", 0);
|
|
break;
|
|
case -32603: /* "server error. internal xml-rpc error" */
|
|
case -32500: /* "application error" */
|
|
case -32400: /* "system error" */
|
|
case -32300: /* "transport error */
|
|
XMLRPC_SetValueString(xCode, "SOAP-ENV:Server", 0);
|
|
break;
|
|
}
|
|
return xDup;
|
|
}
|
|
|
|
/* returns a new XMLRPC_VALUE representing a soap fault, comprised of a struct with four keys. */
|
|
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;
|
|
}
|
|
|
|
/* translates xml soap dom to native data structures. recursive. */
|
|
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;
|
|
|
|
/* no current element on first call */
|
|
if (!xCurrent) {
|
|
xCurrent = XMLRPC_CreateValueEmpty();
|
|
}
|
|
|
|
/* increment recursion depth guage */
|
|
depth ++;
|
|
|
|
/* safety first. must have a valid element */
|
|
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;
|
|
|
|
/* in soap, types may be specified in either element name -or- with xsi:type attribute. */
|
|
if (is_soap_type(el->name)) {
|
|
type = el->name;
|
|
}
|
|
/* if our parent node, by definition a vector, is not an array, then
|
|
our element name must be our key identifier. */
|
|
else if (XMLRPC_GetVectorType(xParent) != xmlrpc_vector_array) {
|
|
id = el->name;
|
|
if(!strcmp(id, "item")) {
|
|
}
|
|
}
|
|
|
|
/* iterate through element attributes, pick out useful stuff. */
|
|
while (attr_iter) {
|
|
/* element's type */
|
|
if (!strcmp(attr_iter->key, TOKEN_TYPE)) {
|
|
type = attr_iter->val;
|
|
}
|
|
/* array type */
|
|
else if (!strcmp(attr_iter->key, TOKEN_ARRAY_TYPE)) {
|
|
arrayType = attr_iter->val;
|
|
}
|
|
/* must understand, sometimes present in headers. */
|
|
else if (!strcmp(attr_iter->key, TOKEN_MUSTUNDERSTAND)) {
|
|
b_must_understand = strchr(attr_iter->val, '1') ? 1 : 0;
|
|
}
|
|
/* actor, used in conjuction with must understand. */
|
|
else if (!strcmp(attr_iter->key, TOKEN_ACTOR)) {
|
|
actor = attr_iter->val;
|
|
}
|
|
attr_iter = Q_Next(&el->attrs);
|
|
}
|
|
|
|
/* check if caller says we must understand something in a header. */
|
|
if (b_must_understand) {
|
|
/* is must understand actually indended for us?
|
|
BUG: spec says we should also determine if actor is our URL, but
|
|
we do not have that information. */
|
|
if (!actor || !strcmp(actor, TOKEN_ACTOR_NEXT)) {
|
|
/* TODO: implement callbacks or other mechanism for applications
|
|
to "understand" these headers. For now, we just bail if we
|
|
get a mustUnderstand header intended for us. */
|
|
XMLRPC_RequestSetError(request,
|
|
gen_soap_fault("SOAP-ENV:MustUnderstand",
|
|
"SOAP Must Understand Error",
|
|
"", ""));
|
|
return xCurrent;
|
|
}
|
|
}
|
|
|
|
/* set id (key) if one was found. */
|
|
if (id) {
|
|
XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact);
|
|
}
|
|
|
|
/* according to soap spec,
|
|
depth 1 = Envelope, 2 = Header, Body & Fault, 3 = methodcall or response. */
|
|
if (depth == 3) {
|
|
const char* methodname = el->name;
|
|
char* p = NULL;
|
|
|
|
/* BUG: we determine request or response type using presence of "Response" in element name.
|
|
According to spec, this is only recommended, not required. Apparently, implementations
|
|
are supposed to know the type of action based on state, which strikes me as a bit lame.
|
|
Anyway, we don't have that state info, thus we use Response as a heuristic. */
|
|
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);
|
|
|
|
/* Get methodname. strip xml namespace crap. */
|
|
p = strchr(el->name, ':');
|
|
if (p) {
|
|
methodname = p + 1;
|
|
}
|
|
if (rtype == xmlrpc_request_call) {
|
|
XMLRPC_RequestSetMethodName(request, methodname);
|
|
}
|
|
}
|
|
|
|
|
|
/* Next, we begin to convert actual values. if no children, then must be a scalar value. */
|
|
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)) {
|
|
/* already an empty val. do nothing. */
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
/* Element has children, thus a vector, or "compound type" in soap-speak. */
|
|
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) {
|
|
/* determine magic associated with soap array type.
|
|
this is passed down as we recurse, so our children have access to the info. */
|
|
ai = parse_array_type_info(arrayType); /* alloc'ed ai free'd below. */
|
|
XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array);
|
|
}
|
|
else {
|
|
/* mixed is probably closest thing we have to compound type. */
|
|
XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
|
|
}
|
|
/* Recurse, adding values as we go. Check for error during recursion
|
|
and if found, bail. this short-circuits us out of the recursion. */
|
|
while ( iter && !XMLRPC_RequestGetError(request) ) {
|
|
XMLRPC_VALUE xNext = NULL;
|
|
/* top level elements don't actually represent values, so we just pass the
|
|
current value along until we are deep enough. */
|
|
if ( depth <= 2 ||
|
|
(rtype == xmlrpc_request_response && depth <= 3) ) {
|
|
xml_element_to_SOAP_REQUEST_worker(request, NULL, ai, xCurrent, iter, depth);
|
|
}
|
|
/* ready to do some actual de-serialization. create a new empty value and
|
|
pass that along to be init'd, then add it to our current vector. */
|
|
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);
|
|
}
|
|
/* cleanup */
|
|
if (ai) {
|
|
free(ai);
|
|
}
|
|
}
|
|
}
|
|
return xCurrent;
|
|
}
|
|
|
|
/* Convert soap xml dom to XMLRPC_VALUE, sans request info. untested. */
|
|
XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el)
|
|
{
|
|
return xml_element_to_SOAP_REQUEST_worker(NULL, NULL, NULL, NULL, el, 0);
|
|
}
|
|
|
|
/* Convert soap xml dom to XMLRPC_REQUEST */
|
|
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;
|
|
}
|
|
|
|
|
|
/* translates data structures to soap/xml. recursive */
|
|
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; /* sometimes we may need to free 'node' variable */
|
|
char buf[BUF_SIZE];
|
|
XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(node);
|
|
char* pName = NULL, *pAttrType = NULL;
|
|
|
|
/* create our return value element */
|
|
elem_val = xml_elem_new();
|
|
|
|
switch (type) {
|
|
case xmlrpc_type_struct:
|
|
case xmlrpc_type_mixed:
|
|
case xmlrpc_type_array:
|
|
if (type == xmlrpc_type_array) {
|
|
/* array's are _very_ special in soap.
|
|
TODO: Should handle sparse/partial arrays here. */
|
|
|
|
/* determine soap array type. */
|
|
const char* type = get_array_soap_type(node);
|
|
xml_element_attr* attr_array_type = NULL;
|
|
|
|
/* specify array kids type and array size. */
|
|
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;
|
|
}
|
|
/* check for fault, which is a rather special case.
|
|
(can't these people design anything consistent/simple/elegant?) */
|
|
else if (type == xmlrpc_type_struct) {
|
|
int fault_type = get_fault_type(node);
|
|
if (fault_type) {
|
|
if (fault_type == 1) {
|
|
/* gen fault from xmlrpc style fault codes
|
|
notice that we get a new node, which must be freed herein. */
|
|
node = gen_fault_xmlrpc(node, elem_val);
|
|
bFreeNode = 1;
|
|
}
|
|
pName = TOKEN_FAULT;
|
|
}
|
|
}
|
|
|
|
{
|
|
/* recurse through sub-elements */
|
|
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;
|
|
|
|
/* handle scalar types */
|
|
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;
|
|
}
|
|
|
|
/* determining element's name is a bit tricky, due to soap semantics. */
|
|
if (!pName) {
|
|
/* if the value's type is known... */
|
|
if (pAttrType) {
|
|
/* see if it has an id (key). If so, use that as name, and type as an attribute. */
|
|
pName = (char*)XMLRPC_GetValueID(node);
|
|
if (pName) {
|
|
Q_PushTail(&elem_val->attrs, new_attr(TOKEN_TYPE, pAttrType));
|
|
}
|
|
|
|
/* otherwise, use the type as the name. */
|
|
else {
|
|
pName = pAttrType;
|
|
}
|
|
}
|
|
/* if the value's type is not known... (a rare case?) */
|
|
else {
|
|
/* see if it has an id (key). otherwise, default to generic "item" */
|
|
pName = (char*)XMLRPC_GetValueID(node);
|
|
if (!pName) {
|
|
pName = "item";
|
|
}
|
|
}
|
|
}
|
|
elem_val->name = strdup(pName);
|
|
|
|
/* cleanup */
|
|
if (bFreeNode) {
|
|
XMLRPC_CleanupValue(node);
|
|
}
|
|
}
|
|
return elem_val;
|
|
}
|
|
|
|
/* convert XMLRPC_VALUE to soap xml dom. untested. */
|
|
xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node) {
|
|
return SOAP_to_xml_element_worker(NULL, node);
|
|
}
|
|
|
|
/* convert XMLRPC_REQUEST to soap xml dom. */
|
|
xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
|
|
xml_element* root = xml_elem_new();
|
|
|
|
/* safety first. */
|
|
if (root) {
|
|
xml_element* body = xml_elem_new();
|
|
root->name = strdup("SOAP-ENV:Envelope");
|
|
|
|
/* silly namespace stuff */
|
|
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/"));
|
|
|
|
/* Q_PushHead(&root->attrs, new_attr("xmlns:ks", "http://kitchen.sink.org/soap/everything/under/sun"));
|
|
JUST KIDDING!! :-) ----> ------------------------------------------------- */
|
|
|
|
if (body) {
|
|
/* go ahead and serialize first... */
|
|
xml_element* el_serialized =
|
|
SOAP_to_xml_element_worker(request,
|
|
XMLRPC_RequestGetData(request));
|
|
|
|
/* check for fault, in which case, there is no intermediate element */
|
|
if (el_serialized && !strcmp(el_serialized->name, TOKEN_FAULT)) {
|
|
Q_PushTail(&body->children, el_serialized);
|
|
}
|
|
/* usual case: not a fault. Add Response element in between. */
|
|
else {
|
|
xml_element* rpc = xml_elem_new();
|
|
|
|
if (rpc) {
|
|
const char* methodname = XMLRPC_RequestGetMethodName(request);
|
|
XMLRPC_REQUEST_TYPE rtype = XMLRPC_RequestGetRequestType(request);
|
|
|
|
/* if we are making a request, we want to use the methodname as is. */
|
|
if (rtype == xmlrpc_request_call) {
|
|
if (methodname) {
|
|
rpc->name = strdup(methodname);
|
|
}
|
|
}
|
|
/* if it's a response, we append "Response". Also, given xmlrpc-epi
|
|
API/architecture, it's likely that we don't have a methodname for
|
|
the response, so we have to check that. */
|
|
else {
|
|
char buf[128];
|
|
snprintf(buf, sizeof(buf), "%s%s",
|
|
methodname ? methodname : "",
|
|
"Response");
|
|
|
|
rpc->name = strdup(buf);
|
|
}
|
|
|
|
/* add serialized data to method call/response.
|
|
add method call/response to body element */
|
|
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 {
|
|
/* no method name?!
|
|
TODO: fault here...? */
|
|
}
|
|
}
|
|
}
|
|
body->name = strdup("SOAP-ENV:Body");
|
|
Q_PushTail(&root->children, body);
|
|
}
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|