XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) { if(!xCurrent) { xCurrent = XMLRPC_CreateValueEmpty(); } if(el->name) { const char* id = NULL; const char* type = NULL; xml_element_attr* attr_iter = Q_Head(&el->attrs); while(attr_iter) { if(!strcmp(attr_iter->key, ATTR_ID)) { id = attr_iter->val; } if(!strcmp(attr_iter->key, ATTR_TYPE)) { type = attr_iter->val; } attr_iter = Q_Next(&el->attrs); } if(id) { XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); } if(!strcmp(el->name, ATTR_SCALAR)) { if(!type || !strcmp(type, ATTR_STRING)) { XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); } else if(!strcmp(type, ATTR_INT)) { XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); } else if(!strcmp(type, ATTR_BOOLEAN)) { XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); } else if(!strcmp(type, ATTR_DOUBLE)) { XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); } else if(!strcmp(type, ATTR_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); } else if(!strcmp(type, ATTR_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 if(!strcmp(el->name, ATTR_VECTOR)) { xml_element* iter = (xml_element*)Q_Head(&el->children); if(!type || !strcmp(type, ATTR_MIXED)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_ARRAY)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array); } else if(!strcmp(type, ATTR_STRUCT)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); } while( iter ) { XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty(); xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter); XMLRPC_AddValueToVector(xCurrent, xNext); iter = (xml_element*)Q_Next(&el->children); } } else { xml_element* iter = (xml_element*)Q_Head(&el->children); while( iter ) { xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter); iter = (xml_element*)Q_Next(&el->children); } if(!strcmp(el->name, ELEM_METHODCALL)) { if(request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } } else if(!strcmp(el->name, ELEM_METHODRESPONSE)) { if(request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } } else if(!strcmp(el->name, ELEM_METHODNAME)) { if(request) { XMLRPC_RequestSetMethodName(request, el->text.str); } } } } return xCurrent; }
XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) { if (!current_val) { /* This should only be the case for the first element */ current_val = XMLRPC_CreateValueEmpty(); } if (el->name) { /* first, deal with the crazy/stupid fault format */ if (!strcmp(el->name, ELEM_FAULT)) { xml_element* fault_value = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); if(fault_value) { xml_element* fault_struct = (xml_element*)Q_Head(&fault_value->children); if(fault_struct) { xml_element* iter = (xml_element*)Q_Head(&fault_struct->children); while (iter) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&fault_struct->children); } } } } else if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */ || (!strcmp(el->name, ELEM_PARAMS) && (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) ) { /* this "PARAMS" concept is silly. dave?! */ xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_array); while (iter) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } } else if (!strcmp(el->name, ELEM_STRUCT)) { xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); while ( iter ) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } } else if (!strcmp(el->name, ELEM_STRING) || (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { XMLRPC_SetValueString(current_val, el->text.str, el->text.len); } else if (!strcmp(el->name, ELEM_NAME)) { XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { XMLRPC_SetValueInt(current_val, atoi(el->text.str)); } else if (!strcmp(el->name, ELEM_BOOLEAN)) { XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); } else if (!strcmp(el->name, ELEM_DOUBLE)) { XMLRPC_SetValueDouble(current_val, atof(el->text.str)); } else if (!strcmp(el->name, ELEM_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); } else if (!strcmp(el->name, ELEM_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); buffer_delete(&buf); } else { xml_element* iter; if (!strcmp(el->name, ELEM_METHODCALL)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } } else if (!strcmp(el->name, ELEM_METHODNAME)) { if (request) { XMLRPC_RequestSetMethodName(request, el->text.str); } } iter = (xml_element*)Q_Head(&el->children); while ( iter ) { xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, current_val, iter); iter = (xml_element*)Q_Next(&el->children); } } } return current_val; }
/* 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; }