/* SPDX-FileCopyrightText: 2005 Tobias Koenig SPDX-License-Identifier: MIT */ #include #include #include #include "soapbinding.h" #include using namespace KWSDL; // TODO: set namespace correctly static const char soapBindingTransportHTTP[] = "http://schemas.xmlsoap.org/soap/http"; static const char soapStandardNamespace[] = "http://schemas.xmlsoap.org/wsdl/soap/"; static QString soapPrefix(ParserContext *context) { QString prefix = context->namespaceManager()->prefix(soapStandardNamespace); if (!prefix.isEmpty()) { return prefix + QLatin1Char(':'); } else { return QString(); } } SoapBinding::Binding::Binding() : mTransport(HTTPTransport) , mStyle(DocumentStyle) { } SoapBinding::Binding::~Binding() { } void SoapBinding::Binding::setTransport(Transport transport) { mTransport = transport; } SoapBinding::Transport SoapBinding::Binding::transport() const { return mTransport; } void SoapBinding::Binding::setStyle(Style style) { mStyle = style; } SoapBinding::Style SoapBinding::Binding::style() const { return mStyle; } void SoapBinding::Binding::loadXML(ParserContext *context, const QDomElement &element) { if (element.hasAttribute(QLatin1String("transport"))) { if (element.attribute(QLatin1String("transport")) == soapBindingTransportHTTP) { mTransport = HTTPTransport; } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Binding: unknown 'transport' value")); } } if (element.hasAttribute(QLatin1String("style"))) { if (element.attribute(QLatin1String("style")) == QLatin1String("rpc")) { mStyle = RPCStyle; } else if (element.attribute(QLatin1String("style")) == QLatin1String("document")) { mStyle = DocumentStyle; } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Binding: unknown 'style' value")); } } } void SoapBinding::Binding::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("binding")); parent.appendChild(element); if (mTransport == HTTPTransport) { element.setAttribute(QLatin1String("transport"), soapBindingTransportHTTP); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Binding: unknown 'transport' value")); } if (mStyle == RPCStyle) { element.setAttribute(QLatin1String("style"), QLatin1String("rpc")); } else { element.setAttribute(QLatin1String("style"), QLatin1String("document")); } } SoapBinding::Operation::Operation() : mStyle(DocumentStyle) { } SoapBinding::Operation::Operation(const QString &name) : mName(name) , mStyle(DocumentStyle) { } SoapBinding::Operation::~Operation() { } void SoapBinding::Operation::loadXML(ParserContext *context, const QDomElement &element) { // read soapAction, discarding leading/trailing space (https://github.com/KDAB/KDSoap/issues/71) mSoapAction = element.attribute(QLatin1String("soapAction")).trimmed(); // qDebug() << "style=" << element.attribute("style"); if (element.hasAttribute(QLatin1String("style"))) { if (element.attribute(QLatin1String("style")) == QLatin1String("rpc")) { mStyle = RPCStyle; } else if (element.attribute(QLatin1String("style")) == QLatin1String("document")) { mStyle = DocumentStyle; } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Operation: unknown 'style' value")); } } // TODO: fallback is style value of soap:binding } void SoapBinding::Operation::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("operation")); parent.appendChild(element); if (!mSoapAction.isEmpty()) { element.setAttribute(QLatin1String("soapAction"), mSoapAction); } if (mStyle == RPCStyle) { element.setAttribute(QLatin1String("style"), QLatin1String("rpc")); } else { element.setAttribute(QLatin1String("style"), QLatin1String("document")); } } void SoapBinding::Operation::setName(const QString &name) { mName = name; } QString SoapBinding::Operation::name() const { return mName; } void SoapBinding::Operation::setAction(const QString &action) { mSoapAction = action; } QString SoapBinding::Operation::action() const { return mSoapAction; } void SoapBinding::Operation::setStyle(Style style) { mStyle = style; } SoapBinding::Style SoapBinding::Operation::style() const { return mStyle; } void SoapBinding::Operation::setInput(const Body &input) { mInputBody = input; } SoapBinding::Body SoapBinding::Operation::input() const { return mInputBody; } void SoapBinding::Operation::setOutput(const Body &output) { mOutputBody = output; } SoapBinding::Body SoapBinding::Operation::output() const { return mOutputBody; } void SoapBinding::Operation::addInputHeader(const Header &inputHeader) { mInputHeaders << inputHeader; } SoapBinding::Headers SoapBinding::Operation::inputHeaders() const { return mInputHeaders; } void SoapBinding::Operation::addOutputHeader(const Header &outputHeader) { mOutputHeaders << outputHeader; } SoapBinding::Headers SoapBinding::Operation::outputHeaders() const { return mOutputHeaders; } void SoapBinding::Operation::setFault(const Fault &fault) { mFault = fault; } SoapBinding::Fault SoapBinding::Operation::fault() const { return mFault; } SoapBinding::Body::Body() : mUse(LiteralUse) { } SoapBinding::Body::~Body() { } #if 0 void SoapBinding::Body::setEncodingStyle(const QString &encodingStyle) { mEncodingStyle = encodingStyle; } QString SoapBinding::Body::encodingStyle() const { return mEncodingStyle; } void SoapBinding::Body::setPart(const QString &part) { mPart = part; } void SoapBinding::Body::setUse(Use use) { mUse = use; } void SoapBinding::Body::setNameSpace(const QString &nameSpace) { mNameSpace = nameSpace; } #endif QString SoapBinding::Body::part() const { return mPart; } SoapBinding::Use SoapBinding::Body::use() const { return mUse; } QString SoapBinding::Body::nameSpace() const { return mNameSpace; } void SoapBinding::Body::loadXML(ParserContext *context, const QDomElement &element) { #if 0 // ## Seems to be set to a namespace, always https://schemas.xmlsoap.org/soap/encoding/ ... unused here. if (element.hasAttribute("encodingStyle")) { mEncodingStyle = element.attribute("encodingStyle"); } else { mEncodingStyle = QString(); } #endif mPart = element.attribute(QLatin1String("parts")); mNameSpace = element.attribute(QLatin1String("namespace")); if (element.hasAttribute(QLatin1String("use"))) { if (element.attribute(QLatin1String("use")) == QLatin1String("literal")) { mUse = LiteralUse; } else if (element.attribute(QLatin1String("use")) == QLatin1String("encoded")) { mUse = EncodedUse; } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Body: unknown 'use' value")); } } } void SoapBinding::Body::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("body")); parent.appendChild(element); #if 0 if (!mEncodingStyle.isEmpty()) { element.setAttribute("encodingStyle", mEncodingStyle); } #endif if (!mPart.isEmpty()) { element.setAttribute(QLatin1String("part"), mPart); } if (!mNameSpace.isEmpty()) { element.setAttribute(QLatin1String("namespace"), mNameSpace); } if (mUse == LiteralUse) { element.setAttribute(QLatin1String("use"), QLatin1String("literal")); } else { element.setAttribute(QLatin1String("use"), QLatin1String("encoded")); } } SoapBinding::Fault::Fault() : mUse(LiteralUse) { } SoapBinding::Fault::~Fault() { } #if 0 void SoapBinding::Fault::setEncodingStyle(const QString &encodingStyle) { mEncodingStyle = encodingStyle; } QString SoapBinding::Fault::encodingStyle() const { return mEncodingStyle; } #endif void SoapBinding::Fault::setUse(Use use) { mUse = use; } SoapBinding::Use SoapBinding::Fault::use() const { return mUse; } void SoapBinding::Fault::setNameSpace(const QString &nameSpace) { mNameSpace = nameSpace; } QString SoapBinding::Fault::nameSpace() const { return mNameSpace; } void SoapBinding::Fault::loadXML(ParserContext *context, const QDomElement &element) { #if 0 if (element.hasAttribute("encodingStyle")) { mEncodingStyle = element.attribute("encodingStyle"); } else { mEncodingStyle = QString(); } #endif mNameSpace = element.attribute(QLatin1String("namespace")); if (element.hasAttribute(QLatin1String("use"))) { if (element.attribute(QLatin1String("use")) == QLatin1String("literal")) { mUse = LiteralUse; } else if (element.attribute(QLatin1String("use")) == QLatin1String("encoded")) { mUse = EncodedUse; } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Fault: unknown 'use' value")); } } } void SoapBinding::Fault::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("fault")); parent.appendChild(element); #if 0 if (!mEncodingStyle.isEmpty()) { element.setAttribute("encodingStyle", mEncodingStyle); } #endif if (!mNameSpace.isEmpty()) { element.setAttribute(QLatin1String("namespace"), mNameSpace); } if (mUse == LiteralUse) { element.setAttribute(QLatin1String("use"), QLatin1String("literal")); } else { element.setAttribute(QLatin1String("use"), QLatin1String("encoded")); } } SoapBinding::Header::Header() : mUse(LiteralUse) { } SoapBinding::Header::~Header() { } void SoapBinding::Header::setHeaderFault(const HeaderFault &headerFault) { mHeaderFault = headerFault; } SoapBinding::HeaderFault SoapBinding::Header::headerFault() const { return mHeaderFault; } void SoapBinding::Header::setMessage(const QName &message) { mMessage = message; } QName SoapBinding::Header::message() const { return mMessage; } void SoapBinding::Header::setPart(const QString &part) { mPart = part; } QString SoapBinding::Header::part() const { return mPart; } void SoapBinding::Header::setUse(Use use) { mUse = use; } SoapBinding::Use SoapBinding::Header::use() const { return mUse; } #if 0 void SoapBinding::Header::setEncodingStyle(const QString encodingStyle) { mEncodingStyle = encodingStyle; } QString SoapBinding::Header::encodingStyle() const { return mEncodingStyle; } #endif void SoapBinding::Header::setNameSpace(const QString &nameSpace) { mNameSpace = nameSpace; } QString SoapBinding::Header::nameSpace() const { return mNameSpace; } void SoapBinding::Header::loadXML(ParserContext *context, const QDomElement &element) { mMessage = element.attribute(QLatin1String("message")); if (mMessage.isEmpty()) { context->messageHandler()->warning(QLatin1String("SoapBinding::Header: 'message' required")); } else { if (!mMessage.prefix().isEmpty()) { mMessage.setNameSpace(context->namespaceManager()->uri(mMessage.prefix())); } } mPart = element.attribute(QLatin1String("part")); if (mPart.isEmpty()) { context->messageHandler()->warning(QLatin1String("SoapBinding::Header: 'part' required")); } if (element.attribute(QLatin1String("use")) == QLatin1String("literal")) { mUse = LiteralUse; } else if (element.attribute(QLatin1String("use")) == QLatin1String("encoded")) { mUse = EncodedUse; } else if (element.attribute(QLatin1String("use")).isEmpty()) { context->messageHandler()->warning(QLatin1String("SoapBinding::Header: 'use' required")); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Header: unknown 'use' value")); } #if 0 if (element.hasAttribute("encodingStyle")) { mEncodingStyle = element.attribute("encodingStyle"); } else { mEncodingStyle = QString(); } #endif mNameSpace = element.attribute(QLatin1String("namespace")); QDomElement child = element.firstChildElement(); while (!child.isNull()) { NSManager namespaceManager(context, child); if (child.tagName() == soapPrefix(context) + QLatin1String("headerfault")) { mHeaderFault.loadXML(context, child); } else { context->messageHandler()->warning(QString::fromLatin1("SoapBinding::Header: unknown tag %1").arg(child.tagName())); } child = child.nextSiblingElement(); } } void SoapBinding::Header::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("header")); parent.appendChild(element); if (!mMessage.isEmpty()) { element.setAttribute(QLatin1String("message"), mMessage.qname()); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Header: 'message' required")); } if (!mPart.isEmpty()) { element.setAttribute(QLatin1String("part"), mPart); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Header: 'part' required")); } #if 0 if (!mEncodingStyle.isEmpty()) { element.setAttribute("encodingStyle", mEncodingStyle); } #endif if (!mNameSpace.isEmpty()) { element.setAttribute(QLatin1String("namespace"), mNameSpace); } if (mUse == LiteralUse) { element.setAttribute(QLatin1String("use"), QLatin1String("literal")); } else { element.setAttribute(QLatin1String("use"), QLatin1String("encoded")); } mHeaderFault.saveXML(context, document, element); } SoapBinding::HeaderFault::HeaderFault() : mUse(LiteralUse) { } SoapBinding::HeaderFault::~HeaderFault() { } void SoapBinding::HeaderFault::setMessage(const QName &message) { mMessage = message; } QName SoapBinding::HeaderFault::message() const { return mMessage; } #if 0 void SoapBinding::HeaderFault::setEncodingStyle(const QString &encodingStyle) { mEncodingStyle = encodingStyle; } QString SoapBinding::HeaderFault::encodingStyle() const { return mEncodingStyle; } #endif void SoapBinding::HeaderFault::setPart(const QString &part) { mPart = part; } QString SoapBinding::HeaderFault::part() const { return mPart; } void SoapBinding::HeaderFault::setUse(Use use) { mUse = use; } SoapBinding::Use SoapBinding::HeaderFault::use() const { return mUse; } void SoapBinding::HeaderFault::setNameSpace(const QString &nameSpace) { mNameSpace = nameSpace; } QString SoapBinding::HeaderFault::nameSpace() const { return mNameSpace; } void SoapBinding::HeaderFault::loadXML(ParserContext *context, const QDomElement &element) { mMessage = element.attribute(QLatin1String("message")); if (mMessage.isEmpty()) { context->messageHandler()->warning(QLatin1String("SoapBinding::HeaderFault: 'message' required")); } mPart = element.attribute(QLatin1String("part")); if (mPart.isEmpty()) { context->messageHandler()->warning(QLatin1String("SoapBinding::HeaderFault: 'part' required")); } if (element.attribute(QLatin1String("use")) == QLatin1String("literal")) { mUse = LiteralUse; } else if (element.attribute(QLatin1String("use")) == QLatin1String("encoded")) { mUse = EncodedUse; } else if (element.attribute(QLatin1String("use")).isEmpty()) { context->messageHandler()->warning(QLatin1String("SoapBinding::HeaderFault: 'use' required")); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::HeaderFault: unknown 'use' value")); } #if 0 if (element.hasAttribute("encodingStyle")) { mEncodingStyle = element.attribute("encodingStyle"); } else { mEncodingStyle = QString(); } #endif mNameSpace = element.attribute(QLatin1String("namespace")); } void SoapBinding::HeaderFault::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("headerfault")); parent.appendChild(element); if (!mMessage.isEmpty()) { element.setAttribute(QLatin1String("message"), mMessage.qname()); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::HeaderFault: 'message' required")); } if (!mPart.isEmpty()) { element.setAttribute(QLatin1String("part"), mPart); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::HeaderFault: 'part' required")); } #if 0 if (!mEncodingStyle.isEmpty()) { element.setAttribute("encodingStyle", mEncodingStyle); } #endif if (!mNameSpace.isEmpty()) { element.setAttribute(QLatin1String("namespace"), mNameSpace); } if (mUse == LiteralUse) { element.setAttribute(QLatin1String("use"), QLatin1String("literal")); } else { element.setAttribute(QLatin1String("use"), QLatin1String("encoded")); } } SoapBinding::Address::Address() { } SoapBinding::Address::~Address() { } void SoapBinding::Address::setLocation(const QUrl &location) { mLocation = location; } QUrl SoapBinding::Address::location() const { return mLocation; } void SoapBinding::setOperations(const Operation::Map &operations) { mOperations = operations; } SoapBinding::Operation::Map SoapBinding::operations() const { return mOperations; } void SoapBinding::Address::loadXML(ParserContext *context, const QDomElement &element) { mLocation = QUrl(element.attribute(QLatin1String("location"))); if (!mLocation.isValid()) { context->messageHandler()->warning(QLatin1String("SoapBinding::Address: 'location' required")); } } void SoapBinding::Address::saveXML(ParserContext *context, QDomDocument &document, QDomElement &parent) const { QDomElement element = document.createElementNS(soapStandardNamespace, soapPrefix(context) + QLatin1String("address")); parent.appendChild(element); if (mLocation.isValid()) { element.setAttribute(QLatin1String("location"), mLocation.toString()); } else { context->messageHandler()->warning(QLatin1String("SoapBinding::Address: 'location' required")); } } SoapBinding::SoapBinding() { } SoapBinding::~SoapBinding() { } SoapBinding::SoapBinding(const SoapBinding &other) : mBinding(other.mBinding) , mOperations(other.mOperations) , mAddress(other.mAddress) { } SoapBinding &SoapBinding::operator=(const SoapBinding &other) { SoapBinding copy(other); swap(copy); return *this; } void SoapBinding::swap(SoapBinding &other) noexcept { using std::swap; swap(mBinding, other.mBinding); swap(mOperations, other.mOperations); swap(mAddress, other.mAddress); } void SoapBinding::setAddress(const Address &address) { mAddress = address; } SoapBinding::Address SoapBinding::address() const { return mAddress; } void SoapBinding::setBinding(const Binding &binding) { mBinding = binding; } SoapBinding::Binding SoapBinding::binding() const { return mBinding; } void SoapBinding::parseBinding(ParserContext *context, const QDomElement &parent) { mBinding.loadXML(context, parent); } void SoapBinding::parseOperation(ParserContext *context, const QString &name, const QDomElement &parent) { QDomElement child = parent.firstChildElement(); while (!child.isNull()) { NSManager namespaceManager(context, child); if (NSManager::soapNamespaces().contains(namespaceManager.nameSpace(child))) { if (namespaceManager.localName(child) == QLatin1String("operation")) { Operation op(name); op.loadXML(context, child); mOperations.insert(name, op); } } child = child.nextSiblingElement(); } } // Parse void SoapBinding::parseOperationInput(ParserContext *context, const QString &name, const QDomElement &parent) { QDomElement child = parent.firstChildElement(); while (!child.isNull()) { NSManager namespaceManager(context, child); // qDebug() << Q_FUNC_INFO << namespaceManager.localName(child) << namespaceManager.nameSpace(child); if (NSManager::soapNamespaces().contains(namespaceManager.nameSpace(child))) { if (namespaceManager.localName(child) == QLatin1String("body")) { Operation &op = mOperations[name]; Body inputBody; inputBody.loadXML(context, child); op.setInput(inputBody); } else if (namespaceManager.localName(child) == QLatin1String("header")) { Operation &op = mOperations[name]; Header inputHeader; inputHeader.loadXML(context, child); op.addInputHeader(inputHeader); } } child = child.nextSiblingElement(); } } // Parse void SoapBinding::parseOperationOutput(ParserContext *context, const QString &name, const QDomElement &parent) { QDomElement child = parent.firstChildElement(); while (!child.isNull()) { NSManager namespaceManager(context, child); if (NSManager::soapNamespaces().contains(namespaceManager.nameSpace(child))) { if (namespaceManager.localName(child) == QLatin1String("body")) { Operation &op = mOperations[name]; Body outputBody; outputBody.loadXML(context, child); op.setOutput(outputBody); } else if (namespaceManager.localName(child) == QLatin1String("header")) { Operation &op = mOperations[name]; Header outputHeader; outputHeader.loadXML(context, child); op.addOutputHeader(outputHeader); } } child = child.nextSiblingElement(); } } void SoapBinding::parseOperationFault(ParserContext *context, const QString &name, const QDomElement &parent) { QDomElement child = parent.firstChildElement(); while (!child.isNull()) { NSManager namespaceManager(context, child); if (NSManager::soapNamespaces().contains(namespaceManager.nameSpace(child))) { if (namespaceManager.localName(child) == QLatin1String("fault")) { Operation &op = mOperations[name]; Fault fault; fault.loadXML(context, child); op.setFault(fault); } } child = child.nextSiblingElement(); } } void SoapBinding::parsePort(ParserContext *context, const QDomElement &parent) { QDomElement child = parent.firstChildElement(); while (!child.isNull()) { NSManager namespaceManager(context, child); if (NSManager::soapNamespaces().contains(namespaceManager.nameSpace(child))) { if (namespaceManager.localName(child) == QLatin1String("address")) { mAddress.loadXML(context, child); } } child = child.nextSiblingElement(); } } void SoapBinding::synthesizeBinding(ParserContext *context, QDomDocument &document, QDomElement &parent) const { mBinding.saveXML(context, document, parent); } void SoapBinding::synthesizeOperation(ParserContext *context, const QString &name, QDomDocument &document, QDomElement &parent) const { const Operation &op = mOperations[name]; op.saveXML(context, document, parent); } void SoapBinding::synthesizeOperationInput(ParserContext *context, const QString &name, QDomDocument &document, QDomElement &parent) const { const Operation &op = mOperations[name]; op.input().saveXML(context, document, parent); const Headers inputHeaders = op.inputHeaders(); for (const Header &header : inputHeaders) { header.saveXML(context, document, parent); } } void SoapBinding::synthesizeOperationOutput(ParserContext *context, const QString &name, QDomDocument &document, QDomElement &parent) const { const Operation &op = mOperations[name]; op.output().saveXML(context, document, parent); const Headers outputHeaders = op.outputHeaders(); for (const Header &header : outputHeaders) { header.saveXML(context, document, parent); } } void SoapBinding::synthesizeOperationFault(ParserContext *context, const QString &name, QDomDocument &document, QDomElement &parent) const { const Operation &op = mOperations[name]; op.fault().saveXML(context, document, parent); } void SoapBinding::synthesizePort(ParserContext *context, QDomDocument &document, QDomElement &parent) const { mAddress.saveXML(context, document, parent); } bool SoapBinding::Headers::contains(const Header &other) const { for (const Header &header : std::as_const(*this)) { if (header.mMessage == other.mMessage && header.mPart == other.mPart && header.mUse == other.mUse && #if 0 header.mEncodingStyle == other.mEncodingStyle && #endif header.mNameSpace == other.mNameSpace) { return true; } } return false; }