sdlPtr load_wsdl()

in hphp/runtime/ext/soap/sdl.cpp [573:1005]


sdlPtr load_wsdl(char *struri, HttpClient *http) {
  sdlCtx ctx;
  ctx.sdl = std::make_shared<sdl>();
  ctx.sdl->source = struri;

  load_wsdl_ex(struri, &ctx, false, http);
  schema_pass2(&ctx);

  int i = 0;
  for (auto servicesIter = ctx.services.begin();
       servicesIter != ctx.services.end(); ++servicesIter, ++i) {
    xmlNodePtr service = servicesIter->second;
    xmlNodePtr port;
    bool has_soap_port = false;
    xmlNodePtr trav = service->children;
    while (trav) {
      if (!is_wsdl_element(trav) || node_is_equal(trav,"documentation")) {
        trav = trav->next;
        continue;
      }
      if (!node_is_equal(trav,"port")) {
        throw SoapException("Parsing WSDL: Unexpected WSDL element <%s>",
                            trav->name);
      }

      port = trav;

      auto tmpbinding = std::make_shared<sdlBinding>();
      xmlAttrPtr bindingAttr = get_attribute(port->properties, "binding");
      if (bindingAttr == nullptr) {
        throw SoapException("Parsing WSDL: No binding associated with <port>");
      }

      char *wsdl_soap_namespace = nullptr;

        /* find address and figure out binding type */
      xmlNodePtr address = nullptr;
      xmlNodePtr trav2 = port->children;
      while (trav2) {
        if (node_is_equal(trav2,"address") && trav2->ns) {
          if (!strncmp((char*)trav2->ns->href, WSDL_SOAP11_NAMESPACE,
                       sizeof(WSDL_SOAP11_NAMESPACE))) {
            address = trav2;
            wsdl_soap_namespace = WSDL_SOAP11_NAMESPACE;
            tmpbinding->bindingType = BINDING_SOAP;
          } else if (!strncmp((char*)trav2->ns->href, WSDL_SOAP12_NAMESPACE,
                              sizeof(WSDL_SOAP12_NAMESPACE))) {
            address = trav2;
            wsdl_soap_namespace = WSDL_SOAP12_NAMESPACE;
            tmpbinding->bindingType = BINDING_SOAP;
          } else if (!strncmp((char*)trav2->ns->href, RPC_SOAP12_NAMESPACE,
                              sizeof(RPC_SOAP12_NAMESPACE))) {
            address = trav2;
            wsdl_soap_namespace = RPC_SOAP12_NAMESPACE;
            tmpbinding->bindingType = BINDING_SOAP;
          } else if (!strncmp((char*)trav2->ns->href, WSDL_HTTP11_NAMESPACE,
                              sizeof(WSDL_HTTP11_NAMESPACE))) {
            address = trav2;
            tmpbinding->bindingType = BINDING_HTTP;
          } else if (!strncmp((char*)trav2->ns->href, WSDL_HTTP12_NAMESPACE,
                              sizeof(WSDL_HTTP12_NAMESPACE))) {
            address = trav2;
            tmpbinding->bindingType = BINDING_HTTP;
          }
        }
        if (trav2 != address && is_wsdl_element(trav2) &&
            !node_is_equal(trav2,"documentation")) {
          throw SoapException("Parsing WSDL: Unexpected WSDL element <%s>",
                              trav2->name);
        }
        trav2 = trav2->next;
      }
      if (!address || tmpbinding->bindingType == BINDING_HTTP) {
        if (has_soap_port || trav->next || i < (int)ctx.services.size() - 1) {
          trav = trav->next;
          continue;
        } else if (!address) {
          throw SoapException("Parsing WSDL: No address associated "
                              "with <port>");
        }
      }
      has_soap_port = true;

      xmlAttrPtr location = get_attribute(address->properties, "location");
      if (!location) {
        throw SoapException("Parsing WSDL: No location associated "
                            "with <port>");
      }
      tmpbinding->location = (char*)location->children->content;

      char *ctype = strrchr((char*)bindingAttr->children->content,':');
      if (ctype == nullptr) {
        ctype = (char*)bindingAttr->children->content;
      } else {
        ++ctype;
      }
      xmlNodeMap::iterator iterBinding = ctx.bindings.find(ctype);
      if (iterBinding == ctx.bindings.end()) {
        throw SoapException("Parsing WSDL: No <binding> element "
                            "with name '%s'", ctype);
      }
      xmlNodePtr binding = iterBinding->second;

      if (tmpbinding->bindingType == BINDING_SOAP) {
        xmlAttrPtr tmp;

        auto soapBinding = std::make_shared<sdlSoapBinding>();
        soapBinding->style = SOAP_DOCUMENT;
        xmlNodePtr soapBindingNode = get_node_ex(binding->children, "binding",
                                                 wsdl_soap_namespace);
        if (soapBindingNode) {
          tmp = get_attribute(soapBindingNode->properties, "style");
          if (tmp &&
              !strncmp((char*)tmp->children->content, "rpc", sizeof("rpc"))) {
            soapBinding->style = SOAP_RPC;
          }

          tmp = get_attribute(soapBindingNode->properties, "transport");
          if (tmp) {
            if (strncmp((char*)tmp->children->content, WSDL_HTTP_TRANSPORT,
                        sizeof(WSDL_HTTP_TRANSPORT)) == 0) {
              soapBinding->transport = SOAP_TRANSPORT_HTTP;
            } else {
              throw SoapException("Parsing WSDL: PHP-SOAP doesn't support "
                                  "transport '%s'", tmp->children->content);
            }
          }
        }
        tmpbinding->bindingAttributes = soapBinding;
      }

      xmlAttrPtr name = get_attribute(binding->properties, "name");
      if (name == nullptr) {
        throw SoapException("Parsing WSDL: Missing 'name' attribute "
                            "for <binding>");
      }
      tmpbinding->name = (char*)name->children->content;

      xmlAttrPtr type = get_attribute(binding->properties, "type");
      if (type == nullptr) {
        throw SoapException("Parsing WSDL: Missing 'type' attribute "
                            "for <binding>");
      }

      xmlNodePtr portType, operation;
      ctype = strrchr((char*)type->children->content,':');
      if (ctype == nullptr) {
        ctype = (char*)type->children->content;
      } else {
        ++ctype;
      }
      xmlNodeMap::iterator iter = ctx.portTypes.find(ctype);
      if (iter == ctx.portTypes.end()) {
        throw SoapException("Parsing WSDL: Missing <portType> with name '%s'",
                            name->children->content);
      }
      portType = iter->second;

      trav2 = binding->children;
      while (trav2) {
        if ((tmpbinding->bindingType == BINDING_SOAP &&
             node_is_equal_ex(trav2, "binding", wsdl_soap_namespace)) ||
            !is_wsdl_element(trav2) ||
            node_is_equal(trav2,"documentation")) {
          trav2 = trav2->next;
          continue;
        }
        if (!node_is_equal(trav2,"operation")) {
          throw SoapException("Parsing WSDL: Unexpected WSDL element <%s>",
                              trav2->name);
        }

        operation = trav2;
        xmlAttrPtr op_name = get_attribute(operation->properties, "name");
        if (op_name == nullptr) {
          throw SoapException("Parsing WSDL: Missing 'name' attribute "
                              "for <operation>");
        }

        xmlNodePtr trav3 = operation->children;
        while  (trav3) {
          if (tmpbinding->bindingType == BINDING_SOAP &&
              node_is_equal_ex(trav3, "operation", wsdl_soap_namespace)) {
          } else if (is_wsdl_element(trav3) &&
                     !node_is_equal(trav3,"input") &&
                     !node_is_equal(trav3,"output") &&
                     !node_is_equal(trav3,"fault") &&
                     !node_is_equal(trav3,"documentation")) {
            throw SoapException("Parsing WSDL: Unexpected WSDL element <%s>",
                                trav3->name);
          }
          trav3 = trav3->next;
        }

        xmlNodePtr portTypeOperation =
          get_node_with_attribute_ex(portType->children, "operation",
                                     WSDL_NAMESPACE, "name",
                                   (char*)op_name->children->content, nullptr);
        if (portTypeOperation == nullptr) {
          throw SoapException("Parsing WSDL: Missing <portType>/<operation> "
                              "with name '%s'", op_name->children->content);
        }

        xmlNodePtr input, output, fault;
        xmlAttrPtr paramOrder;

        auto function = std::make_shared<sdlFunction>();
        function->functionName = (char*)op_name->children->content;

        if (tmpbinding->bindingType == BINDING_SOAP) {
          auto soapFunctionBinding = std::make_shared<sdlSoapBindingFunction>();
          sdlSoapBindingPtr soapBinding = tmpbinding->bindingAttributes;
          soapFunctionBinding->style = soapBinding->style;

          xmlNodePtr soapOperation = get_node_ex
            (operation->children, "operation", wsdl_soap_namespace);
          xmlAttrPtr tmp;
          if (soapOperation) {
            tmp = get_attribute(soapOperation->properties, "soapAction");
            if (tmp) {
              soapFunctionBinding->soapAction = (char*)tmp->children->content;
            }

            tmp = get_attribute(soapOperation->properties, "style");
            if (tmp) {
              if (!strncmp((char*)tmp->children->content, "rpc",
                           sizeof("rpc"))) {
                soapFunctionBinding->style = SOAP_RPC;
              } else {
                soapFunctionBinding->style = SOAP_DOCUMENT;
              }
            } else {
              soapFunctionBinding->style = soapBinding->style;
            }
          }

          function->bindingAttributes = soapFunctionBinding;
        }

        input = get_node_ex(portTypeOperation->children, "input",
                            WSDL_NAMESPACE);
        if (input) {
          xmlAttrPtr message = get_attribute(input->properties, "message");
          if (message == nullptr) {
            throw SoapException("Parsing WSDL: Missing name for <input> "
                                "of '%s'", op_name->children->content);
          }
          wsdl_message(&ctx, function->requestParameters,
                       message->children->content);
/* FIXME
          xmlAttrPtr name = get_attribute(input->properties, "name");
            if (name) {
              function->requestName = estrdup(name->children->content);
            } else {
*/
          {
            function->requestName = function->functionName;
          }

          if (tmpbinding->bindingType == BINDING_SOAP) {
            input = get_node_ex(operation->children, "input", WSDL_NAMESPACE);
            if (input) {
              sdlSoapBindingFunctionPtr soapFunctionBinding =
                function->bindingAttributes;
              wsdl_soap_binding_body(&ctx, input, wsdl_soap_namespace,
                                     &soapFunctionBinding->input,
                                     function->requestParameters);
            }
          }
        }

        output = get_node_ex(portTypeOperation->children, "output",
                             WSDL_NAMESPACE);
        if (output) {
          xmlAttrPtr message = get_attribute(output->properties, "message");
          if (message == nullptr) {
            throw SoapException("Parsing WSDL: Missing name for <output> "
                                "of '%s'", op_name->children->content);
          }
          wsdl_message(&ctx, function->responseParameters,
                       message->children->content);

/* FIXME
            xmlAttrPtr name = get_attribute(output->properties, "name");
            if (name) {
              function->responseName = estrdup(name->children->content);
            } else if (input == nullptr) {
              function->responseName = estrdup(function->functionName);
            } else {
*/
          {
            function->responseName = function->functionName + "Response";
          }

          if (tmpbinding->bindingType == BINDING_SOAP) {
            output = get_node_ex(operation->children, "output",
                                 WSDL_NAMESPACE);
            if (output) {
              sdlSoapBindingFunctionPtr soapFunctionBinding =
                function->bindingAttributes;
              wsdl_soap_binding_body(&ctx, output, wsdl_soap_namespace,
                                     &soapFunctionBinding->output,
                                     function->responseParameters);
            }
          }
        }

        paramOrder = get_attribute(portTypeOperation->properties,
                                   "parameterOrder");
        if (paramOrder) {
          /* FIXME: */
        }

        fault = portTypeOperation->children;
        while (fault) {
          if (node_is_equal_ex(fault, "fault", WSDL_NAMESPACE)) {
            xmlAttrPtr propName = get_attribute(fault->properties, "name");
            if (propName == nullptr) {
              throw SoapException("Parsing WSDL: Missing name for <fault> "
                                  "of '%s'", op_name->children->content);
            }
            xmlAttrPtr message = get_attribute(fault->properties, "message");
            if (message == nullptr) {
              throw SoapException("Parsing WSDL: Missing name for <output> "
                                  "of '%s'", op_name->children->content);
            }

            auto f = std::make_shared<sdlFault>();
            f->name = (char*)propName->children->content;
            wsdl_message(&ctx, f->details, message->children->content);
            if (f->details.size() != 1) {
              throw SoapException("Parsing WSDL: The fault message '%s' must "
                                  "have a single part",
                                  message->children->content);
            }

            if (tmpbinding->bindingType == BINDING_SOAP) {
              xmlNodePtr soap_fault =
                get_node_with_attribute_ex(operation->children, "fault",
                                           WSDL_NAMESPACE, "name",
                                           (char*)f->name.c_str(), nullptr);
              if (soap_fault) {
                xmlNodePtr childTrav = soap_fault->children;
                while (childTrav) {
                  if (node_is_equal_ex(
                          childTrav, "fault", wsdl_soap_namespace)) {
                    auto binding = f->bindingAttributes =
                         std::make_shared<sdlSoapBindingFunctionFault>();
                    xmlAttrPtr tmp = get_attribute(childTrav->properties, "use");
                    if (tmp && !strncmp((char*)tmp->children->content,
                                        "encoded", sizeof("encoded"))) {
                      binding->use = SOAP_ENCODED;
                    } else {
                      binding->use = SOAP_LITERAL;
                    }

                    tmp = get_attribute(childTrav->properties, "namespace");
                    if (tmp) {
                      binding->ns = (char*)tmp->children->content;
                    }

                    if (binding->use == SOAP_ENCODED) {
                      tmp = get_attribute(childTrav->properties,
                          "encodingStyle");
                      if (tmp) {
                        if (strncmp((char*)tmp->children->content,
                                    SOAP_1_1_ENC_NAMESPACE,
                                    sizeof(SOAP_1_1_ENC_NAMESPACE)) == 0) {
                          binding->encodingStyle = SOAP_ENCODING_1_1;
                        } else if (strncmp((char*)tmp->children->content,
                                           SOAP_1_2_ENC_NAMESPACE,
                                           sizeof(SOAP_1_2_ENC_NAMESPACE))
                                   == 0) {
                          binding->encodingStyle = SOAP_ENCODING_1_2;
                        } else {
                          throw SoapException("Parsing WSDL: Unknown "
                                              "encodingStyle '%s'",
                                              tmp->children->content);
                        }
                      } else {
                        throw SoapException("Parsing WSDL: Unspecified "
                                            "encodingStyle");
                      }
                    }
                  } else if (is_wsdl_element(childTrav) &&
                             !node_is_equal(childTrav,"documentation")) {
                    throw SoapException("Parsing WSDL: Unexpected WSDL "
                                        "element <%s>", childTrav->name);
                  }
                  childTrav = childTrav->next;
                }
              }
            }
            sdlFaultMap::iterator iter = function->faults.find(f->name);
            if (iter != function->faults.end()) {
              throw SoapException("Parsing WSDL: <fault> with name '%s' "
                                  "already defined in '%s'", f->name.c_str(),
                                  op_name->children->content);
            }
            function->faults[f->name] = f;
          }
          fault = fault->next;
        }

        function->binding = tmpbinding;

        {
          std::string tmp = toLower(function->functionName);
          sdlFunctionMap::iterator iter = ctx.sdl->functions.find(tmp);
          if (iter != ctx.sdl->functions.end()) {
            ctx.sdl->functions[folly::to<std::string>
                               (ctx.sdl->functions.size())] = function;
          } else {
            ctx.sdl->functions[tmp] = function;
          }
          ctx.sdl->functionsOrder.push_back(tmp);
          if (function->requestName != function->functionName) {
            ctx.sdl->requests[toLower(function->requestName)] = function;
          }
        }
        trav2 = trav2->next;
      }

      ctx.sdl->bindings[tmpbinding->name] = tmpbinding;
      trav= trav->next;
    }
  }

  if (i == 0) {
    throw SoapException("Parsing WSDL: Couldn't bind to service");
  }
  return ctx.sdl;
}