/* expat start of element handler */ static void _xmlrpc_startElement(void *userData, const char *name, const char **attrs) { xml_element *c; xml_elem_data* mydata = (xml_elem_data*)userData; const char** p = attrs; if(mydata) { c = mydata->current; mydata->current = xml_elem_new(); mydata->current->name = (char*)strdup(name); mydata->current->parent = c; /* init attrs */ while(p && *p) { xml_element_attr* attr = malloc(sizeof(xml_element_attr)); if(attr) { attr->key = strdup(*p); attr->val = strdup(*(p+1)); Q_PushTail(&mydata->current->attrs, attr); p += 2; } } } }
xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { xml_element* wrapper = NULL; if (request) { const char* pStr = NULL; XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request); wrapper = xml_elem_new(); if (request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; } else if (request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } if (pStr) { wrapper->name = strdup(pStr); } if(request_type == xmlrpc_request_call) { pStr = XMLRPC_RequestGetMethodName(request); if (pStr) { xml_element* method = xml_elem_new(); method->name = strdup(ELEM_METHODNAME); simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } } if (xParams) { Q_PushTail(&wrapper->children, XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); } else { /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ xml_element* params = xml_elem_new(); params->name = strdup(ELEM_PARAMS); Q_PushTail(&wrapper->children, params); } } return wrapper; }
xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { xml_element* wrapper = NULL; xml_element* root = NULL; if(request) { XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); const char* pStr = NULL; xml_element_attr* version = emalloc(sizeof(xml_element_attr)); version->key = estrdup(ATTR_VERSION); version->val = estrdup(VAL_VERSION_0_9); wrapper = xml_elem_new(); if(request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } else if(request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; } if(pStr) { wrapper->name = estrdup(pStr); } root = xml_elem_new(); root->name = estrdup(ELEM_ROOT); Q_PushTail(&root->attrs, version); Q_PushTail(&root->children, wrapper); pStr = XMLRPC_RequestGetMethodName(request); if(pStr) { xml_element* method = xml_elem_new(); method->name = estrdup(ELEM_METHODNAME); simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } Q_PushTail(&wrapper->children, DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request))); } return root; }
xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, XMLRPC_REQUEST_TYPE request_type, int depth) { #define BUF_SIZE 512 xml_element* root = NULL; if (node) { char buf[BUF_SIZE]; XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node); xml_element* elem_val = xml_elem_new(); /* special case for when root element is not an array */ if (depth == 0 && !(type == xmlrpc_vector && vtype == xmlrpc_vector_array && request_type == xmlrpc_request_call) ) { int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)); xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1); if (next_el) { Q_PushTail(&elem_val->children, next_el); } elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); } else { switch (type) { case xmlrpc_empty: /* treat null value as empty string in xmlrpc. */ case xmlrpc_string: elem_val->name = strdup(ELEM_STRING); simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); break; case xmlrpc_int: elem_val->name = strdup(ELEM_INT); snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_boolean: elem_val->name = strdup(ELEM_BOOLEAN); snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_double: elem_val->name = strdup(ELEM_DOUBLE); snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_datetime: elem_val->name = strdup(ELEM_DATETIME); simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); break; case xmlrpc_base64: { struct buffer_st buf; elem_val->name = strdup(ELEM_BASE64); base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); simplestring_addn(&elem_val->text, buf.data, buf.offset ); buffer_delete(&buf); } break; case xmlrpc_vector: { XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); xml_element* root_vector_elem = elem_val; switch (my_type) { case xmlrpc_vector_array: { if(depth == 0) { elem_val->name = strdup(ELEM_PARAMS); } else { /* Hi my name is Dave and I like to make things as confusing * as possible, thus I will throw in this 'data' element * where it absolutely does not belong just so that people * cannot code arrays and structs in a similar and straight * forward manner. Have a good day. * * GRRRRRRRRR! */ xml_element* data = xml_elem_new(); data->name = strdup(ELEM_DATA); elem_val->name = strdup(ELEM_ARRAY); Q_PushTail(&elem_val->children, data); root_vector_elem = data; } } break; case xmlrpc_vector_mixed: /* not officially supported */ case xmlrpc_vector_struct: elem_val->name = strdup(ELEM_STRUCT); break; default: break; } /* recurse through sub-elements */ while ( xIter ) { xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1); if (next_el) { Q_PushTail(&root_vector_elem->children, next_el); } xIter = XMLRPC_VectorNext(node); } } break; default: break; } } { XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector); if (depth == 1) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); /* yet another hack for the "fault" crap */ if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { root = value; } else { xml_element* param = xml_elem_new(); param->name = strdup(ELEM_PARAM); Q_PushTail(¶m->children, value); root = param; } Q_PushTail(&value->children, elem_val); } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { xml_element* member = xml_elem_new(); xml_element* name = xml_elem_new(); xml_element* value = xml_elem_new(); member->name = strdup(ELEM_MEMBER); name->name = strdup(ELEM_NAME); value->name = strdup(ELEM_VALUE); simplestring_add(&name->text, XMLRPC_GetValueID(node)); Q_PushTail(&member->children, name); Q_PushTail(&member->children, value); Q_PushTail(&value->children, elem_val); root = member; } else if (vtype == xmlrpc_vector_array) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); Q_PushTail(&value->children, elem_val); root = value; } else if (vtype == xmlrpc_vector_none) { /* no parent. non-op */ root = elem_val; } else { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); Q_PushTail(&value->children, elem_val); root = value; } } } return root; }
/****f* xml_element/xml_elem_parse_buf * NAME * xml_elem_parse_buf * SYNOPSIS * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) * FUNCTION * parse a buffer containing XML into an xml_element in-memory tree * INPUTS * in_buf - buffer containing XML document * len - length of buffer * options - input options. optional * error - error result data. optional. check if result is null. * RESULT * void * NOTES * The returned data must be free'd by caller * SEE ALSO * xml_elem_serialize_to_string () * xml_elem_free () * SOURCE */ xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) { xml_element* xReturn = NULL; char buf[100] = ""; static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8}; if(!options) { options = &default_opts; } if(in_buf) { XML_Parser parser; xml_elem_data mydata = {0}; parser = XML_ParserCreate(NULL); mydata.root = xml_elem_new(); mydata.current = mydata.root; mydata.input_options = options; mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8); XML_SetElementHandler(parser, (XML_StartElementHandler)_xmlrpc_startElement, (XML_EndElementHandler)_xmlrpc_endElement); XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler)_xmlrpc_charHandler); /* pass the xml_elem_data struct along */ XML_SetUserData(parser, (void*)&mydata); if(!len) { len = strlen(in_buf); } /* parse the XML */ if(XML_Parse(parser, in_buf, len, 1) == 0) { enum XML_Error err_code = XML_GetErrorCode(parser); int line_num = XML_GetCurrentLineNumber(parser); int col_num = XML_GetCurrentColumnNumber(parser); long byte_idx = XML_GetCurrentByteIndex(parser); /* int byte_total = XML_GetCurrentByteCount(parser); */ const char * error_str = XML_ErrorString(err_code); if(byte_idx >= 0) { snprintf(buf, sizeof(buf), "\n\tdata beginning %ld before byte index: %s\n", byte_idx > 10 ? 10 : byte_idx, in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx)); } /* fprintf(stderr, "expat reports error code %i\n" "\tdescription: %s\n" "\tline: %i\n" "\tcolumn: %i\n" "\tbyte index: %ld\n" "\ttotal bytes: %i\n%s ", err_code, error_str, line_num, col_num, byte_idx, byte_total, buf); */ /* error condition */ if(error) { error->parser_code = (long)err_code; error->line = line_num; error->column = col_num; error->byte_index = byte_idx; error->parser_error = error_str; } } else { xReturn = (xml_element*)Q_Head(&mydata.root->children); xReturn->parent = NULL; } XML_ParserFree(parser); xml_elem_free_non_recurse(mydata.root); } return xReturn; }
xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { #define BUF_SIZE 512 xml_element* root = NULL; if(node) { char buf[BUF_SIZE]; const char* id = XMLRPC_GetValueID(node); XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request); int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space); xml_element* elem_val = xml_elem_new(); const char* pAttrType = NULL; xml_element_attr* attr_type = bNoAddType ? NULL : emalloc(sizeof(xml_element_attr)); if(attr_type) { attr_type->key = estrdup(ATTR_TYPE); attr_type->val = 0; Q_PushTail(&elem_val->attrs, attr_type); } elem_val->name = (type == xmlrpc_vector) ? estrdup(ATTR_VECTOR) : estrdup(ATTR_SCALAR); if(id && *id) { xml_element_attr* attr_id = emalloc(sizeof(xml_element_attr)); if(attr_id) { attr_id->key = estrdup(ATTR_ID); attr_id->val = estrdup(id); Q_PushTail(&elem_val->attrs, attr_id); } } switch(type) { case xmlrpc_string: pAttrType = ATTR_STRING; simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); break; case xmlrpc_int: pAttrType = ATTR_INT; snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_boolean: pAttrType = ATTR_BOOLEAN; snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_double: pAttrType = ATTR_DOUBLE; snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_datetime: pAttrType = ATTR_DATETIME; simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); break; case xmlrpc_base64: { struct buffer_st buf; pAttrType = ATTR_BASE64; base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); simplestring_addn(&elem_val->text, buf.data, buf.offset ); buffer_delete(&buf); } break; case xmlrpc_vector: { XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); switch(my_type) { case xmlrpc_vector_array: pAttrType = ATTR_ARRAY; break; case xmlrpc_vector_mixed: pAttrType = ATTR_MIXED; break; case xmlrpc_vector_struct: pAttrType = ATTR_STRUCT; break; default: break; } /* recurse through sub-elements */ while( xIter ) { xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter); if(next_el) { Q_PushTail(&elem_val->children, next_el); } xIter = XMLRPC_VectorNext(node); } } break; default: break; } if(pAttrType && attr_type && !bNoAddType) { attr_type->val = estrdup(pAttrType); } root = elem_val; } return root; }
/* 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; }
/* 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; }