Back to index

php5  5.3.10
xml_to_soap.c
Go to the documentation of this file.
00001 /*
00002   This file is part of libXMLRPC - a C library for xml-encoded function calls.
00003 
00004   Author: Dan Libby (dan@libby.com)
00005 */
00006 
00007 
00008 /*-**********************************************************************
00009 * TODO:                                                                 *
00010 *  - [SOAP-ENC:position] read sparse arrays (and write?)                *
00011 *  - [SOAP-ENC:offset] read partially transmitted arrays  (and write?)  *
00012 *  - read "flattened" multi-dimensional arrays. (don't bother writing)  *
00013 *                                                                       *
00014 * BUGS:                                                                 *
00015 *  - does not read schema. thus only knows soap pre-defined types.      *
00016 *  - references (probably) do not work. untested.                       *
00017 *  - does not expose SOAP-ENV:Header to application at all.             *
00018 *  - does not use namespaces correctly, thus:                           *
00019 *    - namespaces are hard-coded in comparison tokens                   *
00020 *    - if a sender uses another namespace identifer, it will break      *
00021 ************************************************************************/
00022 
00023 
00024 static const char rcsid[] = "#(@) $Id:";
00025 
00026 #ifdef _WIN32
00027 #include "xmlrpc_win32.h"
00028 #endif
00029 #include <string.h>
00030 #include <stdlib.h>
00031 #include "xml_to_soap.h"
00032 #include "base64.h"
00033 
00034 /* list of tokens used in vocab */
00035 #define TOKEN_ANY                          "xsd:ur-type"
00036 #define TOKEN_ARRAY          "SOAP-ENC:Array"
00037 #define TOKEN_ARRAY_TYPE     "SOAP-ENC:arrayType"
00038 #define TOKEN_BASE64         "SOAP-ENC:base64"
00039 #define TOKEN_BOOLEAN        "xsd:boolean"
00040 #define TOKEN_DATETIME       "xsd:timeInstant"
00041 #define TOKEN_DOUBLE         "xsd:double"
00042 #define TOKEN_FLOAT          "xsd:float"
00043 #define TOKEN_ID             "id"
00044 #define TOKEN_INT            "xsd:int"
00045 #define TOKEN_NULL           "xsi:null"
00046 #define TOKEN_STRING         "xsd:string"
00047 #define TOKEN_STRUCT                "xsd:struct"
00048 #define TOKEN_TYPE           "xsi:type"
00049 #define TOKEN_FAULT                 "SOAP-ENV:Fault"
00050 #define TOKEN_MUSTUNDERSTAND "SOAP-ENV:mustUnderstand"
00051 #define TOKEN_ACTOR                 "SOAP-ENV:actor"
00052 #define TOKEN_ACTOR_NEXT            "http://schemas.xmlsoap.org/soap/actor/next"
00053 
00054 #define TOKEN_XMLRPC_FAULTCODE   "faultCode"
00055 #define TOKEN_XMLRPC_FAULTSTRING "faultString"
00056 #define TOKEN_SOAP_FAULTCODE     "faultcode"
00057 #define TOKEN_SOAP_FAULTSTRING   "faultstring"
00058 #define TOKEN_SOAP_FAULTDETAILS  "details"
00059 #define TOKEN_SOAP_FAULTACTOR    "actor"
00060 
00061 
00062 /* determine if a string represents a soap type, as used in element names */
00063 static inline int is_soap_type(const char* soap_type) {
00064        return(strstr(soap_type, "SOAP-ENC:") || strstr(soap_type, "xsd:")) ? 1 : 0;
00065 }
00066 
00067 /* utility func to generate a new attribute. possibly should be in xml_element.c?? */
00068 static xml_element_attr* new_attr(const char* key, const char* val) {
00069        xml_element_attr* attr = malloc(sizeof(xml_element_attr));
00070        if (attr) {
00071               attr->key = key ? strdup(key) : NULL;
00072               attr->val = val ? strdup(val) : NULL;
00073        }
00074        return attr;
00075 }
00076 
00077 struct array_info {
00078        char          kids_type[128];
00079        unsigned long size;
00080        /* ... ? */
00081 };
00082 
00083 
00084 /* parses soap arrayType attribute to generate an array_info structure.
00085  * TODO: should deal with sparse, flattened, & multi-dimensional arrays
00086  */
00087 static struct array_info* parse_array_type_info(const char* array_type) {
00088        struct array_info* ai = NULL;
00089        if (array_type) {
00090               ai = (struct array_info*)calloc(1, sizeof(struct array_info));
00091               if (ai) {
00092                      char buf[128], *p;
00093                      snprintf(buf, sizeof(buf), "%s", array_type);
00094                      p = strchr(buf, '[');
00095                      if (p) {
00096                             *p = 0;
00097                      }
00098                      strcpy(ai->kids_type, buf);
00099               }
00100        }
00101        return ai;
00102 }
00103 
00104 /* performs heuristics on an xmlrpc_vector_array to determine
00105  * appropriate soap arrayType string.
00106  */
00107 static const char* get_array_soap_type(XMLRPC_VALUE node) {
00108        XMLRPC_VALUE_TYPE_EASY type = xmlrpc_type_none;
00109 
00110        XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
00111        int loopCount = 0;
00112        const char* soapType = TOKEN_ANY;
00113 
00114        type = XMLRPC_GetValueTypeEasy(xIter);
00115        xIter = XMLRPC_VectorNext(node);
00116 
00117        while (xIter) {
00118               /* 50 seems like a decent # of loops.  That will likely
00119                * cover most cases.  Any more and we start to sacrifice
00120                * performance.
00121                */
00122               if ( (XMLRPC_GetValueTypeEasy(xIter) != type) || loopCount >= 50) {
00123                      type = xmlrpc_type_none;
00124                      break;
00125               }
00126               loopCount ++;
00127 
00128               xIter = XMLRPC_VectorNext(node);
00129        }
00130        switch (type) {
00131        case xmlrpc_type_none:
00132               soapType = TOKEN_ANY;
00133               break;
00134        case xmlrpc_type_empty:
00135               soapType = TOKEN_NULL;
00136               break;
00137        case xmlrpc_type_int:
00138               soapType = TOKEN_INT;
00139               break;
00140        case xmlrpc_type_double:
00141               soapType = TOKEN_DOUBLE;
00142               break;
00143        case xmlrpc_type_boolean:
00144               soapType = TOKEN_BOOLEAN;
00145               break;
00146        case xmlrpc_type_string:
00147               soapType = TOKEN_STRING;
00148               break;
00149        case xmlrpc_type_base64:
00150               soapType = TOKEN_BASE64;
00151               break;
00152        case xmlrpc_type_datetime:
00153               soapType = TOKEN_DATETIME;
00154               break;
00155        case xmlrpc_type_struct:
00156               soapType = TOKEN_STRUCT;
00157               break;
00158        case xmlrpc_type_array:
00159               soapType = TOKEN_ARRAY;
00160               break;
00161        case xmlrpc_type_mixed:
00162               soapType = TOKEN_STRUCT;
00163               break;
00164        }
00165        return soapType;
00166 }
00167 
00168 /* determines whether a node is a fault or not, and of which type:
00169  * 0 = not a fault,
00170  * 1 = xmlrpc style fault
00171  * 2 = soap style fault.
00172  */
00173 static inline int get_fault_type(XMLRPC_VALUE node) {
00174        if (XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTCODE) &&
00175                XMLRPC_VectorGetValueWithID(node, TOKEN_XMLRPC_FAULTSTRING)) {
00176               return 1;
00177        }
00178        else if (XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTCODE) &&
00179                             XMLRPC_VectorGetValueWithID(node, TOKEN_SOAP_FAULTSTRING)) {
00180               return 2;
00181        }
00182        return 0;
00183 }
00184 
00185 /* input: an XMLRPC_VALUE representing a fault struct in xml-rpc style.
00186  * output: an XMLRPC_VALUE representing a fault struct in soap style,
00187  *  with xmlrpc codes mapped to soap codes, and all other values preserved.
00188  *  note that the returned value is a completely new value, and must be freed.
00189  *  the input value is untouched.
00190  */
00191 static XMLRPC_VALUE gen_fault_xmlrpc(XMLRPC_VALUE node, xml_element* el_target) {
00192        XMLRPC_VALUE xDup = XMLRPC_DupValueNew(node);
00193        XMLRPC_VALUE xCode = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTCODE);
00194        XMLRPC_VALUE xStr = XMLRPC_VectorGetValueWithID(xDup, TOKEN_XMLRPC_FAULTSTRING);
00195 
00196        XMLRPC_SetValueID(xCode, TOKEN_SOAP_FAULTCODE, 0);
00197        XMLRPC_SetValueID(xStr, TOKEN_SOAP_FAULTSTRING, 0);
00198 
00199        /* rough mapping of xmlrpc fault codes to soap codes */
00200        switch (XMLRPC_GetValueInt(xCode)) {
00201        case -32700:           /* "parse error. not well formed", */
00202        case -32701:           /* "parse error. unsupported encoding" */
00203        case -32702:           /* "parse error. invalid character for encoding" */
00204        case -32600:           /* "server error. invalid xml-rpc.  not conforming to spec." */
00205        case -32601:           /* "server error. requested method not found" */
00206        case -32602:           /* "server error. invalid method parameters" */
00207               XMLRPC_SetValueString(xCode, "SOAP-ENV:Client", 0);
00208               break;
00209        case -32603:           /* "server error. internal xml-rpc error" */
00210        case -32500:           /* "application error" */
00211        case -32400:           /* "system error" */
00212        case -32300:           /* "transport error */
00213               XMLRPC_SetValueString(xCode, "SOAP-ENV:Server", 0);
00214               break;
00215        }
00216        return xDup;
00217 }
00218 
00219 /* returns a new XMLRPC_VALUE representing a soap fault, comprised of a struct with four keys. */
00220 static XMLRPC_VALUE gen_soap_fault(const char* fault_code, const char* fault_string, 
00221                                                                                const char* actor, const char* details) {
00222        XMLRPC_VALUE xReturn = XMLRPC_CreateVector(TOKEN_FAULT, xmlrpc_vector_struct);
00223        XMLRPC_AddValuesToVector(xReturn,
00224                                                                 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTCODE, fault_code, 0),
00225                                                                 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTSTRING, fault_string, 0),
00226                                                                 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTACTOR, actor, 0),
00227                                                                 XMLRPC_CreateValueString(TOKEN_SOAP_FAULTDETAILS, details, 0),
00228                                                                 NULL);
00229        return xReturn;
00230 }
00231 
00232 /* translates xml soap dom to native data structures. recursive. */
00233 XMLRPC_VALUE xml_element_to_SOAP_REQUEST_worker(XMLRPC_REQUEST request, 
00234                                                                                                                 XMLRPC_VALUE xParent,
00235                                                                                                                 struct array_info* parent_array,
00236                                                                                                                 XMLRPC_VALUE xCurrent, 
00237                                                                                                                 xml_element* el, 
00238                                                                                                                 int depth) {
00239        XMLRPC_REQUEST_TYPE rtype = xmlrpc_request_none;
00240 
00241        /* no current element on first call */
00242        if (!xCurrent) {
00243               xCurrent = XMLRPC_CreateValueEmpty();
00244        }
00245 
00246        /* increment recursion depth guage */
00247        depth ++;
00248 
00249        /* safety first. must have a valid element */
00250        if (el && el->name) {
00251               const char* id = NULL;
00252               const char* type = NULL, *arrayType=NULL, *actor = NULL;
00253               xml_element_attr* attr_iter = Q_Head(&el->attrs);
00254               int b_must_understand = 0;
00255               
00256               /* in soap, types may be specified in either element name -or- with xsi:type attribute. */
00257               if (is_soap_type(el->name)) {
00258                      type = el->name;
00259               }
00260               /* if our parent node, by definition a vector, is not an array, then
00261                  our element name must be our key identifier. */
00262               else if (XMLRPC_GetVectorType(xParent) != xmlrpc_vector_array) {
00263                      id = el->name;
00264                      if(!strcmp(id, "item")) {
00265                      }
00266               }
00267 
00268               /* iterate through element attributes, pick out useful stuff. */
00269               while (attr_iter) {
00270                      /* element's type */
00271                      if (!strcmp(attr_iter->key, TOKEN_TYPE)) {
00272                             type = attr_iter->val;
00273                      }
00274                      /* array type */
00275                      else if (!strcmp(attr_iter->key, TOKEN_ARRAY_TYPE)) {
00276                             arrayType = attr_iter->val;
00277                      }
00278                      /* must understand, sometimes present in headers. */
00279                      else if (!strcmp(attr_iter->key, TOKEN_MUSTUNDERSTAND)) {
00280                             b_must_understand = strchr(attr_iter->val, '1') ? 1 : 0;
00281                      }
00282                      /* actor, used in conjuction with must understand. */
00283                      else if (!strcmp(attr_iter->key, TOKEN_ACTOR)) {
00284                             actor = attr_iter->val;
00285                      }
00286                      attr_iter = Q_Next(&el->attrs);
00287               }
00288 
00289               /* check if caller says we must understand something in a header. */
00290               if (b_must_understand) {
00291                      /* is must understand actually indended for us?
00292                         BUG: spec says we should also determine if actor is our URL, but
00293                              we do not have that information. */
00294                      if (!actor || !strcmp(actor, TOKEN_ACTOR_NEXT)) {
00295                             /* TODO: implement callbacks or other mechanism for applications
00296                                to "understand" these headers. For now, we just bail if we
00297                                get a mustUnderstand header intended for us. */
00298                             XMLRPC_RequestSetError(request, 
00299                                                                                gen_soap_fault("SOAP-ENV:MustUnderstand",
00300                                                                                                                   "SOAP Must Understand Error",
00301                                                                                                                   "", ""));
00302                             return xCurrent;
00303                      }
00304               }
00305 
00306               /* set id (key) if one was found. */
00307               if (id) {
00308                      XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact);
00309               }
00310 
00311               /* according to soap spec, 
00312                  depth 1 = Envelope, 2 = Header, Body & Fault, 3 = methodcall or response. */
00313               if (depth == 3) {
00314                      const char* methodname = el->name;
00315                      char* p = NULL;
00316 
00317                      /* BUG: we determine request or response type using presence of "Response" in element name.
00318                         According to spec, this is only recommended, not required. Apparently, implementations
00319                         are supposed to know the type of action based on state, which strikes me as a bit lame.
00320                         Anyway, we don't have that state info, thus we use Response as a heuristic. */
00321                      rtype =
00322 #ifdef strcasestr
00323                      strcasestr(el->name, "response") ? xmlrpc_request_response : xmlrpc_request_call;
00324 #else
00325                      strstr(el->name, "esponse") ? xmlrpc_request_response : xmlrpc_request_call;
00326 #endif
00327                      XMLRPC_RequestSetRequestType(request, rtype);
00328 
00329                      /* Get methodname.  strip xml namespace crap. */
00330                      p = strchr(el->name, ':');
00331                      if (p) {
00332                             methodname = p + 1;
00333                      }
00334                      if (rtype == xmlrpc_request_call) {
00335                             XMLRPC_RequestSetMethodName(request, methodname);
00336                      }
00337               }
00338 
00339 
00340               /* Next, we begin to convert actual values. if no children, then must be a scalar value. */
00341               if (!Q_Size(&el->children)) {
00342                      if (!type && parent_array && parent_array->kids_type[0]) {
00343                             type = parent_array->kids_type;
00344                      }
00345                      if (!type || !strcmp(type, TOKEN_STRING)) {
00346                             XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len);
00347                      }
00348                      else if (!strcmp(type, TOKEN_INT)) {
00349                             XMLRPC_SetValueInt(xCurrent, atoi(el->text.str));
00350                      }
00351                      else if (!strcmp(type, TOKEN_BOOLEAN)) {
00352                             XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str));
00353                      }
00354                      else if (!strcmp(type, TOKEN_DOUBLE) ||
00355                                           !strcmp(type, TOKEN_FLOAT)) {
00356                             XMLRPC_SetValueDouble(xCurrent, atof(el->text.str));
00357                      }
00358                      else if (!strcmp(type, TOKEN_NULL)) {
00359                             /* already an empty val. do nothing. */
00360                      }
00361                      else if (!strcmp(type, TOKEN_DATETIME)) {
00362                             XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str);
00363                      }
00364                      else if (!strcmp(type, TOKEN_BASE64)) {
00365                             struct buffer_st buf;
00366                             base64_decode_xmlrpc(&buf, el->text.str, el->text.len);
00367                             XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset);
00368                             buffer_delete(&buf);
00369                      }
00370               }
00371               /* Element has children, thus a vector, or "compound type" in soap-speak. */
00372               else {
00373                      struct array_info* ai = NULL;
00374                      xml_element* iter = (xml_element*)Q_Head(&el->children);
00375 
00376                      if (!type || !strcmp(type, TOKEN_STRUCT)) {
00377                             XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct);
00378                      }
00379                      else if (!strcmp(type, TOKEN_ARRAY) || arrayType != NULL) {
00380                             /* determine magic associated with soap array type.
00381                                this is passed down as we recurse, so our children have access to the info. */
00382                             ai = parse_array_type_info(arrayType);    /* alloc'ed ai free'd below. */
00383                             XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_array);
00384                      }
00385                      else {
00386                             /* mixed is probably closest thing we have to compound type. */
00387                             XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed);
00388                      }
00389                      /* Recurse, adding values as we go.  Check for error during recursion
00390                         and if found, bail.  this short-circuits us out of the recursion. */
00391                      while ( iter && !XMLRPC_RequestGetError(request) ) {
00392                             XMLRPC_VALUE xNext = NULL;
00393                             /* top level elements don't actually represent values, so we just pass the
00394                                current value along until we are deep enough. */
00395                             if ( depth <= 2 ||
00396                                      (rtype == xmlrpc_request_response && depth <= 3) ) {
00397                                    xml_element_to_SOAP_REQUEST_worker(request, NULL, ai, xCurrent, iter, depth);
00398                             }
00399                             /* ready to do some actual de-serialization. create a new empty value and
00400                                pass that along to be init'd, then add it to our current vector. */
00401                             else {
00402                                    xNext = XMLRPC_CreateValueEmpty();
00403                                    xml_element_to_SOAP_REQUEST_worker(request, xCurrent, ai, xNext, iter, depth);
00404                                    XMLRPC_AddValueToVector(xCurrent, xNext);
00405                             }
00406                             iter = (xml_element*)Q_Next(&el->children);
00407                      }
00408                      /* cleanup */
00409                      if (ai) {
00410                             free(ai);
00411                      }
00412               }
00413        }
00414        return xCurrent;
00415 }
00416 
00417 /* Convert soap xml dom to XMLRPC_VALUE, sans request info.  untested. */
00418 XMLRPC_VALUE xml_element_to_SOAP_VALUE(xml_element* el)
00419 {
00420        return xml_element_to_SOAP_REQUEST_worker(NULL, NULL, NULL, NULL, el, 0);
00421 }
00422 
00423 /* Convert soap xml dom to XMLRPC_REQUEST */
00424 XMLRPC_VALUE xml_element_to_SOAP_REQUEST(XMLRPC_REQUEST request, xml_element* el)
00425 {
00426        if (request) {
00427               return XMLRPC_RequestSetData(request, xml_element_to_SOAP_REQUEST_worker(request, NULL, NULL, NULL, el, 0));
00428        }
00429        return NULL;
00430 }
00431 
00432 
00433 /* translates data structures to soap/xml. recursive */
00434 xml_element* SOAP_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) {
00435 #define BUF_SIZE 128
00436        xml_element* elem_val = NULL;
00437        if (node) {
00438               int bFreeNode = 0;  /* sometimes we may need to free 'node' variable */
00439               char buf[BUF_SIZE];
00440               XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(node);
00441               char* pName = NULL, *pAttrType = NULL;
00442 
00443               /* create our return value element */
00444               elem_val = xml_elem_new();
00445 
00446               switch (type) {
00447               case xmlrpc_type_struct:
00448               case xmlrpc_type_mixed:
00449               case xmlrpc_type_array:
00450                      if (type == xmlrpc_type_array) {
00451                             /* array's are _very_ special in soap.
00452                                TODO: Should handle sparse/partial arrays here. */
00453 
00454                             /* determine soap array type. */
00455                             const char* type = get_array_soap_type(node);
00456                             xml_element_attr* attr_array_type = NULL;
00457 
00458                             /* specify array kids type and array size. */
00459                             snprintf(buf, sizeof(buf), "%s[%i]", type, XMLRPC_VectorSize(node));
00460                             attr_array_type = new_attr(TOKEN_ARRAY_TYPE, buf);
00461 
00462                             Q_PushTail(&elem_val->attrs, attr_array_type);
00463 
00464                             pAttrType = TOKEN_ARRAY;
00465                      }
00466                      /* check for fault, which is a rather special case. 
00467                         (can't these people design anything consistent/simple/elegant?) */
00468                      else if (type == xmlrpc_type_struct) {
00469                             int fault_type = get_fault_type(node);
00470                             if (fault_type) {
00471                                    if (fault_type == 1) {
00472                                           /* gen fault from xmlrpc style fault codes              
00473                                               notice that we get a new node, which must be freed herein. */
00474                                           node = gen_fault_xmlrpc(node, elem_val);
00475                                           bFreeNode = 1;
00476                                    }
00477                                    pName = TOKEN_FAULT;
00478                             }
00479                      }
00480 
00481                      {
00482                             /* recurse through sub-elements */
00483                             XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
00484                             while ( xIter ) {
00485                                    xml_element* next_el = SOAP_to_xml_element_worker(request, xIter);
00486                                    if (next_el) {
00487                                           Q_PushTail(&elem_val->children, next_el);
00488                                    }
00489                                    xIter = XMLRPC_VectorNext(node);
00490                             }
00491                      }
00492 
00493                      break;
00494 
00495                      /* handle scalar types */
00496               case xmlrpc_type_empty:
00497                      pAttrType = TOKEN_NULL;
00498                      break;
00499               case xmlrpc_type_string:
00500                      pAttrType = TOKEN_STRING;
00501                      simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
00502                      break;
00503               case xmlrpc_type_int:
00504                      pAttrType = TOKEN_INT;
00505                      snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
00506                      simplestring_add(&elem_val->text, buf);
00507                      break;
00508               case xmlrpc_type_boolean:
00509                      pAttrType = TOKEN_BOOLEAN;
00510                      snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
00511                      simplestring_add(&elem_val->text, buf);
00512                      break;
00513               case xmlrpc_type_double:
00514                      pAttrType = TOKEN_DOUBLE;
00515                      snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
00516                      simplestring_add(&elem_val->text, buf);
00517                      break;
00518               case xmlrpc_type_datetime:
00519                      {
00520                             time_t tt = XMLRPC_GetValueDateTime(node);
00521                             struct tm *tm = localtime (&tt);
00522                             pAttrType = TOKEN_DATETIME;
00523                             if(strftime (buf, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", tm)) {
00524                                    simplestring_add(&elem_val->text, buf);
00525                             }
00526                      }
00527                      break;
00528               case xmlrpc_type_base64:
00529                      {
00530                             struct buffer_st buf;
00531                             pAttrType = TOKEN_BASE64;
00532                             base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
00533                             simplestring_addn(&elem_val->text, buf.data, buf.offset );
00534                             buffer_delete(&buf);
00535                      }
00536                      break;
00537                      break;
00538               default:
00539                      break;
00540               }
00541 
00542               /* determining element's name is a bit tricky, due to soap semantics. */
00543               if (!pName) {
00544                      /* if the value's type is known... */
00545                      if (pAttrType) {
00546                             /* see if it has an id (key). If so, use that as name, and type as an attribute. */
00547                             pName = (char*)XMLRPC_GetValueID(node);
00548                             if (pName) {
00549                                    Q_PushTail(&elem_val->attrs, new_attr(TOKEN_TYPE, pAttrType));
00550                             }
00551 
00552                             /* otherwise, use the type as the name. */
00553                             else {
00554                                    pName = pAttrType;
00555                             }
00556                      }
00557                      /* if the value's type is not known... (a rare case?) */
00558                      else {
00559                             /* see if it has an id (key). otherwise, default to generic "item" */
00560                             pName = (char*)XMLRPC_GetValueID(node);
00561                             if (!pName) {
00562                                    pName = "item";
00563                             }
00564                      }
00565               }
00566               elem_val->name = strdup(pName);
00567 
00568               /* cleanup */
00569               if (bFreeNode) {
00570                      XMLRPC_CleanupValue(node);
00571               }
00572        }
00573        return elem_val;
00574 }
00575 
00576 /* convert XMLRPC_VALUE to soap xml dom.  untested. */
00577 xml_element* SOAP_VALUE_to_xml_element(XMLRPC_VALUE node) {
00578        return SOAP_to_xml_element_worker(NULL, node);
00579 }
00580 
00581 /* convert XMLRPC_REQUEST to soap xml dom. */
00582 xml_element* SOAP_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
00583        xml_element* root = xml_elem_new();
00584 
00585        /* safety first. */
00586        if (root) {
00587               xml_element* body = xml_elem_new();
00588               root->name = strdup("SOAP-ENV:Envelope");
00589 
00590               /* silly namespace stuff */
00591               Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"));
00592               Q_PushTail(&root->attrs, new_attr("xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance"));
00593               Q_PushTail(&root->attrs, new_attr("xmlns:xsd", "http://www.w3.org/1999/XMLSchema"));
00594               Q_PushTail(&root->attrs, new_attr("xmlns:SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"));
00595               Q_PushTail(&root->attrs, new_attr("xmlns:si", "http://soapinterop.org/xsd"));
00596               Q_PushTail(&root->attrs, new_attr("xmlns:ns6", "http://testuri.org"));
00597               Q_PushTail(&root->attrs, new_attr("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/"));
00598 
00599               /* Q_PushHead(&root->attrs, new_attr("xmlns:ks", "http://kitchen.sink.org/soap/everything/under/sun"));
00600                      JUST KIDDING!! :-)  ---->                ------------------------------------------------- */
00601 
00602               if (body) {
00603                      /* go ahead and serialize first... */
00604                      xml_element* el_serialized =  
00605                      SOAP_to_xml_element_worker(request, 
00606                                                                                     XMLRPC_RequestGetData(request));
00607 
00608                      /* check for fault, in which case, there is no intermediate element */
00609                      if (el_serialized && !strcmp(el_serialized->name, TOKEN_FAULT)) {
00610                             Q_PushTail(&body->children, el_serialized);
00611                      }
00612                      /* usual case: not a fault. Add Response element in between. */
00613                      else {
00614                             xml_element* rpc = xml_elem_new();
00615 
00616                             if (rpc) {
00617                                    const char* methodname = XMLRPC_RequestGetMethodName(request);
00618                                    XMLRPC_REQUEST_TYPE rtype = XMLRPC_RequestGetRequestType(request);
00619 
00620                                    /* if we are making a request, we want to use the methodname as is. */
00621                                    if (rtype == xmlrpc_request_call) {
00622                                           if (methodname) {
00623                                                  rpc->name = strdup(methodname);
00624                                           }
00625                                    }
00626                                    /* if it's a response, we append "Response". Also, given xmlrpc-epi
00627                                       API/architecture, it's likely that we don't have a methodname for
00628                                       the response, so we have to check that. */
00629                                    else {
00630                                           char buf[128];
00631                                           snprintf(buf, sizeof(buf), "%s%s", 
00632                                                                methodname ? methodname : "",
00633                                                                "Response");
00634 
00635                                           rpc->name = strdup(buf);
00636                                    }
00637 
00638                                    /* add serialized data to method call/response.
00639                                       add method call/response to body element */
00640                                    if (rpc->name) {
00641                                           if(el_serialized) {
00642                                                  if(Q_Size(&el_serialized->children) && rtype == xmlrpc_request_call) {
00643                                                         xml_element* iter = (xml_element*)Q_Head(&el_serialized->children);
00644                                                         while(iter) {
00645                                                                Q_PushTail(&rpc->children, iter);
00646                                                                iter = (xml_element*)Q_Next(&el_serialized->children);
00647                                                         }
00648                                                         xml_elem_free_non_recurse(el_serialized);
00649                                                  }
00650                                                  else {
00651                                                         Q_PushTail(&rpc->children, el_serialized);
00652                                                  }
00653                                           }
00654 
00655                                           Q_PushTail(&body->children, rpc);
00656                                    }
00657                                    else {
00658                                           /* no method name?!
00659                                              TODO: fault here...? */
00660                                    }
00661                             }
00662                      }
00663                      body->name = strdup("SOAP-ENV:Body");
00664                      Q_PushTail(&root->children, body);
00665               }
00666        }
00667 
00668        return root;
00669 }
00670