static xmlDocPtr serialize_response_call()

in hphp/runtime/ext/soap/ext_soap.cpp [1213:1557]


static xmlDocPtr serialize_response_call(
    std::shared_ptr<sdlFunction> function,
    const char *function_name,
    const char *uri, Variant &ret,
    const Array& headers, int version) {
  xmlNodePtr envelope = nullptr, body, param;
  xmlNsPtr ns = nullptr;
  int use = SOAP_LITERAL;
  xmlNodePtr head = nullptr;

  encode_reset_ns();

  xmlDocPtr doc = xmlNewDoc(BAD_CAST("1.0"));
  doc->charset = XML_CHAR_ENCODING_UTF8;
  doc->encoding = xmlCharStrdup("UTF-8");

  if (version == SOAP_1_1) {
    envelope = xmlNewDocNode(doc, nullptr, BAD_CAST("Envelope"), nullptr);
    ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_1_ENV_NAMESPACE),
                  BAD_CAST(SOAP_1_1_ENV_NS_PREFIX));
    xmlSetNs(envelope,ns);
  } else if (version == SOAP_1_2) {
    envelope = xmlNewDocNode(doc, nullptr, BAD_CAST("Envelope"), nullptr);
    ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENV_NAMESPACE),
                  BAD_CAST(SOAP_1_2_ENV_NS_PREFIX));
    xmlSetNs(envelope,ns);
  } else {
    throw_soap_server_fault("Server", "Unknown SOAP version");
  }
  xmlDocSetRootElement(doc, envelope);

  if (ret.isObject() &&
      ret.toObject()->instanceof(SystemLib::s_SoapFaultClass)) {
    ObjectData* obj = ret.getObjectData();

    char *detail_name;
    std::shared_ptr<sdlFault> fault;
    string fault_ns;

    if (!headers.empty()) {
      encodePtr hdr_enc;
      int hdr_use = SOAP_LITERAL;
      Variant hdr_ret = obj->o_get("headerfault");
      auto h = cast<soapHeader>(headers[0]);
      const char *hdr_ns   = h->hdr ? h->hdr->ns.c_str() : nullptr;
      const char *hdr_name = h->function_name.data();

      head = xmlNewChild(envelope, ns, BAD_CAST("Header"), nullptr);
      if (hdr_ret.isObject() &&
          hdr_ret.toObject().instanceof(SoapHeader::getClass())) {
        const SoapHeader *ht = Native::data<SoapHeader>(hdr_ret.toObject());

        string key;
        if (!ht->m_namespace.empty()) {
          key += ht->m_namespace.data();
          key += ':';
          hdr_ns = ht->m_namespace.data();
        }
        if (!ht->m_name.empty()) {
          key += ht->m_name.data();
          hdr_name = ht->m_name.data();
        }
        if (h->hdr) {
          sdlSoapBindingFunctionHeaderMap::iterator iter =
            h->hdr->headerfaults.find(key);
          if (iter != h->hdr->headerfaults.end()) {
            auto hdr = iter->second;
            hdr_enc = hdr->encode;
            hdr_use = hdr->use;
          }
        }
        hdr_ret = ht->m_data;
        obj->setProp(nullptr, s_headerfault.get(), *hdr_ret.asTypedValue());
      }

      if (h->function) {
        if (serialize_response_call2(head, h->function,
                                     h->function_name.data(), uri,
                                     hdr_ret, version, 0) == SOAP_ENCODED) {
          obj->setProp(nullptr, s_headerfault.get(), *hdr_ret.asTypedValue());
          use = SOAP_ENCODED;
        }
      } else {
        xmlNodePtr xmlHdr = master_to_xml(hdr_enc, hdr_ret, hdr_use, head);
        if (hdr_name) {
          xmlNodeSetName(xmlHdr, BAD_CAST(hdr_name));
        }
        if (hdr_ns) {
          xmlNsPtr nsptr = encode_add_ns(xmlHdr, hdr_ns);
          xmlSetNs(xmlHdr, nsptr);
        }
      }
    }

    body = xmlNewChild(envelope, ns, BAD_CAST("Body"), nullptr);
    param = xmlNewChild(body, ns, BAD_CAST("Fault"), nullptr);

    fault_ns = obj->o_get("faultcodens").toString().data();
    use = SOAP_LITERAL;
    if (!obj->o_get("_name").toString().empty()) {
      if (function) {
        sdlFaultMap::iterator iter =
          function->faults.find(obj->o_get("_name").toString().data());
        if (iter != function->faults.end()) {
          fault = iter->second;
          if (function->binding &&
              function->binding->bindingType == BINDING_SOAP &&
              fault->bindingAttributes) {
            sdlSoapBindingFunctionFaultPtr fb = fault->bindingAttributes;
            use = fb->use;
            if (fault_ns.empty()) {
              fault_ns = fb->ns;
            }
          }
        }
      }
    } else if (function && function->faults.size() == 1) {
      fault = function->faults[0];
      if (function->binding &&
          function->binding->bindingType == BINDING_SOAP &&
          fault->bindingAttributes) {
        sdlSoapBindingFunctionFaultPtr fb = fault->bindingAttributes;
        use = fb->use;
        if (fault_ns.empty()) {
          fault_ns = fb->ns;
        }
      }
    }

    if (fault_ns.empty() && fault && fault->details.size() == 1) {
      sdlParamPtr sparam = fault->details[0];
      if (sparam->element) {
        fault_ns = sparam->element->namens;
      }
    }

    if (version == SOAP_1_1) {
      if (!obj->o_get("faultcode").toString().empty()) {
        xmlNodePtr node = xmlNewNode(nullptr, BAD_CAST("faultcode"));
        String str = StringUtil::HtmlEncode(obj->o_get("faultcode").toString(),
                                            StringUtil::QuoteStyle::Double,
                                            "UTF-8", true, true);
        xmlAddChild(param, node);
        if (!fault_ns.empty()) {
          xmlNsPtr nsptr = encode_add_ns(node, fault_ns.c_str());
          xmlChar *code = xmlBuildQName(BAD_CAST(str.data()), nsptr->prefix,
                                        nullptr, 0);
          xmlNodeSetContent(node, code);
          xmlFree(code);
        } else {
          xmlNodeSetContentLen(node, BAD_CAST(str.data()), str.size());
        }
      }
      if (!obj->o_get("faultstring").toString().empty()) {
        xmlNodePtr node = master_to_xml(
          get_conversion(dataTypeToSoap(KindOfString)),
          obj->o_get("faultstring"), SOAP_LITERAL,
          param
        );
        xmlNodeSetName(node, BAD_CAST("faultstring"));
      }
      if (!obj->o_get("faultactor").toString().empty()) {
        xmlNodePtr node = master_to_xml(
          get_conversion(dataTypeToSoap(KindOfString)),
          obj->o_get("faultactor"), SOAP_LITERAL,
          param
        );
        xmlNodeSetName(node, BAD_CAST("faultactor"));
      }
      detail_name = "detail";
    } else {
      if (!obj->o_get("faultcode").toString().empty()) {
        xmlNodePtr node = xmlNewChild(param, ns, BAD_CAST("Code"), nullptr);
        String str = StringUtil::HtmlEncode(obj->o_get("faultcode").toString(),
                                            StringUtil::QuoteStyle::Double,
                                            "UTF-8", true, true);
        node = xmlNewChild(node, ns, BAD_CAST("Value"), nullptr);
        if (!fault_ns.empty()) {
          xmlNsPtr nsptr = encode_add_ns(node, fault_ns.c_str());
          xmlChar *code = xmlBuildQName(BAD_CAST(str.data()), nsptr->prefix,
                                        nullptr, 0);
          xmlNodeSetContent(node, code);
          xmlFree(code);
        } else {
          xmlNodeSetContentLen(node, BAD_CAST(str.data()), str.size());
        }
      }
      if (!obj->o_get("faultstring").toString().empty()) {
        xmlNodePtr node = xmlNewChild(param, ns, BAD_CAST("Reason"), nullptr);
        node = master_to_xml(
          get_conversion(dataTypeToSoap(KindOfString)),
          obj->o_get("faultstring"),
          SOAP_LITERAL, node
        );
        xmlNodeSetName(node, BAD_CAST("Text"));
        xmlSetNs(node, ns);
      }
      detail_name = SOAP_1_2_ENV_NS_PREFIX":Detail";
    }
    if (fault && fault->details.size() == 1) {
      xmlNodePtr node;
      Variant detail;
      sdlParamPtr sparam;
      xmlNodePtr x;

      if (!obj->o_get("detail").isNull()) {
        detail = obj->o_get("detail");
      }
      node = xmlNewNode(nullptr, BAD_CAST(detail_name));
      xmlAddChild(param, node);

      sparam = fault->details[0];

      if (detail.isObject() && sparam->element) {
        Variant prop = detail.toObject()->o_get(sparam->element->name.c_str());
        if (!prop.isNull()) {
          detail = prop;
        }
      }

      x = serialize_parameter(sparam, detail, 1, nullptr, use, node);

      if (function &&
          function->binding &&
          function->binding->bindingType == BINDING_SOAP &&
          function->bindingAttributes) {
        sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
        if (fnb->style == SOAP_RPC && !sparam->element) {
          if (fault->bindingAttributes) {
            sdlSoapBindingFunctionFaultPtr fb = fault->bindingAttributes;
            if (!fb->ns.empty()) {
              xmlNsPtr ns2 = encode_add_ns(x, fb->ns.c_str());
              xmlSetNs(x, ns2);
            }
          }
        } else {
          if (sparam->element) {
            xmlNsPtr ns2 = encode_add_ns(x, sparam->element->namens.c_str());
            xmlNodeSetName(x, BAD_CAST(sparam->element->name.c_str()));
            xmlSetNs(x, ns2);
          }
        }
      }
      if (use == SOAP_ENCODED && version == SOAP_1_2) {
        xmlSetNsProp(x, envelope->ns, BAD_CAST("encodingStyle"),
                     BAD_CAST(SOAP_1_2_ENC_NAMESPACE));
      }
    } else if (!obj->o_get("detail").isNull()) {
      serialize_zval(obj->o_get("detail"), sdlParamPtr(), detail_name, use, param);
    }
  } else {

    if (!headers.empty()) {
      head = xmlNewChild(envelope, ns, BAD_CAST("Header"), nullptr);
      for (ArrayIter iter(headers); iter; ++iter) {
        auto h = cast<soapHeader>(iter.second());
        if (!h->retval.isNull()) {
          encodePtr hdr_enc;
          int hdr_use = SOAP_LITERAL;
          Variant &hdr_ret = h->retval;
          const char *hdr_ns   = h->hdr ? h->hdr->ns.c_str() : nullptr;
          const char *hdr_name = h->function_name.data();

          if (h->retval.isObject() &&
              h->retval.toObject().instanceof(SoapHeader::getClass())) {
            const SoapHeader *ht = Native::data<SoapHeader>(
              h->retval.toObject());
            string key;
            if (!ht->m_namespace.empty()) {
              key += ht->m_namespace.data();
              key += ':';
              hdr_ns = ht->m_namespace.data();
            }
            if (!ht->m_name.empty()) {
              key += ht->m_name.data();
              hdr_name = ht->m_name.data();
            }
            if (function && function->binding &&
                function->binding->bindingType == BINDING_SOAP) {
              sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
              sdlSoapBindingFunctionHeaderMap::iterator iter =
                fnb->output.headers.find(key);
              if (iter != fnb->output.headers.end()) {
                hdr_enc = iter->second->encode;
                hdr_use = iter->second->use;
              }
            }
            hdr_ret = ht->m_data;
          }

          if (h->function) {
            if (serialize_response_call2(head, h->function,
                                         h->function_name.data(), uri, hdr_ret,
                                         version, 0) == SOAP_ENCODED) {
              use = SOAP_ENCODED;
            }
          } else {
            xmlNodePtr xmlHdr = master_to_xml(hdr_enc, hdr_ret, hdr_use, head);
            if (hdr_name) {
              xmlNodeSetName(xmlHdr, BAD_CAST(hdr_name));
            }
            if (hdr_ns) {
              xmlNsPtr nsptr = encode_add_ns(xmlHdr,hdr_ns);
              xmlSetNs(xmlHdr, nsptr);
            }
          }
        }
      }

      if (head->children == nullptr) {
        xmlUnlinkNode(head);
        xmlFreeNode(head);
      }
    }

    body = xmlNewChild(envelope, ns, BAD_CAST("Body"), nullptr);

    if (serialize_response_call2(body, function.get(), function_name, uri, ret,
                                 version, 1) == SOAP_ENCODED) {
      use = SOAP_ENCODED;
    }
  }

  if (use == SOAP_ENCODED) {
    xmlNewNs(envelope, BAD_CAST(XSD_NAMESPACE), BAD_CAST(XSD_NS_PREFIX));
    if (version == SOAP_1_1) {
      xmlNewNs(envelope, BAD_CAST(SOAP_1_1_ENC_NAMESPACE),
               BAD_CAST(SOAP_1_1_ENC_NS_PREFIX));
      xmlSetNsProp(envelope, envelope->ns, BAD_CAST("encodingStyle"),
                   BAD_CAST(SOAP_1_1_ENC_NAMESPACE));
    } else if (version == SOAP_1_2) {
      xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENC_NAMESPACE),
               BAD_CAST(SOAP_1_2_ENC_NS_PREFIX));
    }
  }

  encode_finish();

  if (function && function->responseName.empty() &&
      body->children == nullptr && head == nullptr) {
    xmlFreeDoc(doc);
    return nullptr;
  }
  return doc;
}