/****************************************************************************
**
** This file is part of the KD Soap project.
**
** SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
**
** SPDX-License-Identifier: MIT
**
****************************************************************************/
#ifndef KDSOAPCLIENTINTERFACE_H
#define KDSOAPCLIENTINTERFACE_H

#include "KDSoapMessage.h"
#include "KDSoapPendingCall.h"
#include <QtCore/QString>
#include <QtCore/QtGlobal>

class KDSoapAuthentication;
class KDSoapSslHandler;
class KDSoapClientInterfacePrivate;
QT_BEGIN_NAMESPACE
class QSslError;
class QSslConfiguration;
class QNetworkCookieJar;
class QNetworkProxy;
QT_END_NAMESPACE

/**
 * KDSoapClientInterface is a generic accessor class that is used to place
 * calls to remote SOAP objects.
 * This class is useful for dynamic access to remote objects: that is, when
 * you do not have a generated code that represents the remote interface.
 * \code
 *  const int year = 2009;
 *
 *  const QString endPoint = QLatin1String("http://www.27seconds.com/Holidays/US/Dates/USHolidayDates.asmx");
 *  const QString messageNamespace = QLatin1String("http://www.27seconds.com/Holidays/US/Dates/");
 *  KDSoapClientInterface client(endPoint, messageNamespace);
 *
 *  KDSoapMessage message;
 *  message.addArgument(QLatin1String("year"), year);
 *
 *  qDebug("Looking up the date of Valentine's Day in %i...", year);
 *
 *  KDSoapMessage response = client.call(QLatin1String("GetValentinesDay"), message);
 *
 *  qDebug("%s", qPrintable(response.arguments()[0].value().toString()));
 * \endcode
 */
class KDSOAP_EXPORT KDSoapClientInterface
{
public:
    /**
     * Version of the SOAP protocol to use when sending requests.
     * \see setSoapVersion()
     */
    enum SoapVersion
    {
        /** Use format version 1.1 of the SOAP specification */
        SOAP1_1 = 1,
        /** Use format version 1.2 of the SOAP specification */
        SOAP1_2 = 2
    };

    /**
     * Creates a KDSoapClientInterface object associated with the end point \p endPoint.
     * \note No connection is done yet at this point, the parameters are simply stored for later use.
     * \param endPoint the URL of the SOAP service, including http or https scheme, port number
     *                 if needed, and path. Example: http://server/path/soap.php
     * \param messageNamespace the namespace URI used for the message and its arguments.
     *                 Example: http://server/path, but could be any URI, it doesn't have to exist
     *                 or even to be http, this is really just a namespace, which is part of the
     *                 specification of the SOAP service.
     */
    explicit KDSoapClientInterface(const QString &endPoint, const QString &messageNamespace);
    /**
     * Destroys the object interface and frees up any resource used.
     * \warning Any running asynchronous calls will be canceled.
     */
    ~KDSoapClientInterface();

    KDSoapClientInterface(const KDSoapClientInterface &) = delete;
    KDSoapClientInterface &operator=(const KDSoapClientInterface &) = delete;

    /**
     * Calls the method \p method on this interface and passes the arguments specified in \p message
     * to the method.
     * \param method the method name, without arguments. For instance \c "addContact". Only used in RPC style.
     * \param message arguments for the method call
     * \param soapAction optional \c "SoapAction" header, see the specification of the SOAP service.
     *        You are strongly encouraged to pass a value here, even if it's just "", given that by default
     *        (for a null string) KDSoap will generate a SOAPAction for backwards compatibility reasons.
     * \param headers optional arguments which will be passed as \c <soap:Header>.
     *
     * This is an asynchronous call, so this function returns immediately.
     * The returned KDSoapPendingCall object can be used to find out information about the reply.
     * You should create a KDSoapPendingCallWatcher to connect to the finished() signal.
     *
     * \warning The returned KDSoapPendingCall object (or a copy of it) must stay alive
     * for the whole duration of the call. If you do not want to wait for a response,
     * use callNoReply instead.
     *
     * \code
     *  const int year = 2009;
     *
     *  const QString endPoint = QLatin1String("http://www.27seconds.com/Holidays/US/Dates/USHolidayDates.asmx");
     *  const QString messageNamespace = QLatin1String("http://www.27seconds.com/Holidays/US/Dates/");
     *  KDSoapClientInterface* client= new KDSoapClientInterface(endPoint, messageNamespace);
     *
     *  KDSoapMessage message;
     *  message.addArgument(QLatin1String("year"), year);
     *
     *  qDebug("Looking up the date of Valentine's Day in %i...", year);
     *
     *  KDSoapPendingCall pendingCall = client->asyncCall(QLatin1String("GetValentinesDay"), message);
     *
     *  // create a watcher object that will signal the call's completion
     *  KDSoapPendingCallWatcher* watcher = new KDSoapPendingCallWatcher(pendingCall, this);
     *  connect(watcher, &KDSoapPendingCallWatcher::finished,
     *          this, &MyClass::pendingCallFinished);
     *
     *  void MyClass::pendingCallFinished(KDSoapPendingCallWatcher* pendingCall)
     *  {
     *      KDSoapMessage response = pendingCall->returnMessage();
     *      qDebug("%s", qPrintable(response.arguments()[0].value().toString()));
     *  }
     * \endcode
     */
    KDSoapPendingCall asyncCall(const QString &method, const KDSoapMessage &message, const QString &soapAction = QString(),
                                const KDSoapHeaders &headers = KDSoapHeaders());

    /**
     * Calls the method \p method on this interface and passes the parameters specified in \p message
     * to the method.
     * \param method the method name, without arguments. For instance \c "addContact". Only used in RPC style.
     * \param message arguments for the method call
     * \param soapAction optional \c "SoapAction" header, see the specification of the SOAP service.
     *        You are strongly encouraged to pass a value here, even if it's just "", given that by default
     *        (for a null string) KDSoap will generate a SOAPAction for backwards compatibility reasons.
     * \param headers optional arguments which will be passed as \c <soap:Header>.
     *
     * \warning This is a blocking call. It is NOT recommended to use this in the main thread of
     * graphical applications, since it will block the event loop for the duration of the call.
     * Use this only in threads, or in non-GUI programs.
     */
    KDSoapMessage call(const QString &method, const KDSoapMessage &message, const QString &soapAction = QString(),
                       const KDSoapHeaders &headers = KDSoapHeaders());

    /**
     * Calls the method \p method on this interface and passes the parameters specified in \p message
     * to the method.
     * \param method the method name, without arguments. For instance \c "addContact". Only used in RPC style.
     * \param message arguments for the method call
     * \param soapAction optional \c "SoapAction" header, see the specification of the SOAP service.
     *        You are strongly encouraged to pass a value here, even if it's just "", given that by default
     *        (for a null string) KDSoap will generate a SOAPAction for backwards compatibility reasons.
     * \param headers optional arguments which will be passed as \c <soap:Header>.
     *
     * This is an asynchronous call, where the caller does not want to wait for a response.
     * The method returns immediately, the call is performed later. No error handling is possible.
     */
    void callNoReply(const QString &method, const KDSoapMessage &message, const QString &soapAction = QString(),
                     const KDSoapHeaders &headers = KDSoapHeaders());

    /**
     * Provide the necessary authentication for this service.
     * \param authentication the authentication data
     */
    void setAuthentication(const KDSoapAuthentication &authentication);

    /**
     * Sets a persistent header, which will be sent with any subsequent SOAP call.
     * \param name internal name, used to replace any existing header previously set with this name
     * \param header the actual message to be sent
     */
    void setHeader(const QString &name, const KDSoapMessage &header);

    /**
     * Sets the SOAP version to be used for any subsequent SOAP call.
     * \param version #SOAP1_1 or #SOAP1_2
     * The default version is SOAP 1.1.
     */
    void setSoapVersion(KDSoapClientInterface::SoapVersion version);

    /**
     * Returns the version of SOAP being used in this instance.
     */
    KDSoapClientInterface::SoapVersion soapVersion() const;

    /**
     * Returns the end point of the SOAP service.
     * \since 1.2
     */
    QString endPoint() const;

    /**
     * Sets the end point of the SOAP service.
     * \param endPoint the URL of the SOAP service, including http or https scheme, port number
     *                 if needed, and path. Example: http://server/path/soap.php
     * \since 1.2
     */
    void setEndPoint(const QString &endPoint);

    /**
     * Returns the cookie jar to use for the HTTP requests.
     * If no cookie jar was set by setCookieJar previously, a default
     * one will be returned, which belongs to the client interface (no need to delete it).
     * \since 1.2
     */
    QNetworkCookieJar *cookieJar() const;

    /**
     * Sets the cookie jar to use for the HTTP requests.
     * The ownership of the cookie jar is NOT transferred, so that it is possible
     * to share the same cookie jar between multiple client interfaces.
     * \since 1.2
     */
    void setCookieJar(QNetworkCookieJar *jar);

    /**
     * Returns the network proxy used for the HTTP requests.
     * \since 1.2
     * \sa QNetworkAccessManager::proxy()
     */
    QNetworkProxy proxy() const;

    /**
     * Sets the network proxy used for the HTTP requests.
     * \since 1.2
     * \sa QNetworkAccessManager::setProxy()
     */
    void setProxy(const QNetworkProxy &proxy);

    /**
     * Sets additional HTTP headers in the requests
     * \since 1.4
     * \sa QNetworkAccessManager::setRawHeader()
     */
    void setRawHTTPHeaders(const QMap<QByteArray, QByteArray> &headers);

    /**
     * WSDL style. See the "style" attribute for soap:binding, in the WSDL file.
     * See https://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/ for a discussion
     * on the pros and cons of both styles.
     *
     * In RPC style, the method name passed to call() or asyncCall() is sent as an xml element
     * wrapping the message parameters.
     *
     * In Document style, the KDSoapMessage represents the entire "document" to be sent, so the
     * the method name passed to call() or asyncCall() is ignored, and the name of the KDSoapMessage
     * is used as the main xml element name. This difference is mostly useful in the case of
     * generated code, so that it can serialize existing complex types, and send them as messages.
     */
    enum Style
    {
        RPCStyle, ///< the method name is sent as an xml element wrapping the message parameters
        DocumentStyle ///< the message is sent as is, the method name is usually the name of the message
    };

    /**
     * Sets the WSDL style used by this service.
     * \since 1.1
     */
    void setStyle(Style style);

    /**
     * Returns the WSDL style used by this service.
     * \since 1.1
     */
    Style style() const;

    /**
     * Returns the headers returned by the last synchronous call().
     * For asyncCall(), use KDSoapPendingCall::returnHeaders().
     * \since 1.1
     */
    KDSoapHeaders lastResponseHeaders() const;

    /**
     * Asks Qt to ignore ssl errors in https requests. Use this for testing
     * only!
     */
    void ignoreSslErrors();

#ifndef QT_NO_SSL
    /**
     * \brief ignoreSslErrors
     * If this function is called, the SSL errors given in \p errors will be ignored.
     * Note that you can set the expected certificate in the SSL error.
     * See QNetworkReply::ignoreSslErrors() for more information.
     *
     * \param errors list of errors to ignore
     * \since 1.4
     */
    void ignoreSslErrors(const QList<QSslError> &errors);
#endif

    /**
     * Returns the ssl handler object, which can be used for notification
     * and handling of SSL errors.
     *
     * Note that the notifications from synchronous calls will come in delayed, after the
     * synchronous call failed. For this reason, it is not possible to ignore specific
     * ssl errors during runtime when using synchronous calls. Use asynchronous calls instead.
     *
     * \since 1.3
     */
    KDSoapSslHandler *sslHandler() const;

#ifndef QT_NO_SSL
    /**
     * Returns the ssl configuration used for outgoing connections
     * \since 1.3
     */
    QSslConfiguration sslConfiguration() const;

    /**
     * Sets the ssl configuration used for outgoing connections
     * \since 1.3
     */
    void setSslConfiguration(const QSslConfiguration &config);
#endif

    /**
     * Returns the timeout used for requests, in milliseconds.
     * The default timeout is 30 minutes.
     * \since 1.8
     */
    int timeout() const;

    /**
     * Sets the timeout used for future requests, in milliseconds.
     * A negative value disables the timeout feature.
     * \since 1.8
     */
    void setTimeout(int msecs);

    /**
     * Sets the message addressing properties that will be written
     * in the header for all future messages.
     * This method is useful for client code generated from WSDL.
     * Note that this takes precedence over KDSoapMessage::setMessageAddressingProperties.
     * \since 2.2
     */
    void setMessageAddressingProperties(const KDSoapMessageAddressingProperties &map);

    /**
     * \brief setSendSoapActionInHttpHeader
     * \param sendInHttpHeader
     * This option can be used to enable/disable the sending of the SOAP action parameter
     * in the Content-Type HTTP header (in the case of SOAP 1.2 requests).
     * It might be necessary to disable it for certain  SOAP server implementations
     * in the case if the action is passed in the SOAP envelope's header with WS-Addressing.
     * \since 1.11
     */
    void setSendSoapActionInHttpHeader(bool sendInHttpHeader);

    /**
     * \brief sendActionInHTTP_Header
     * \return true if the SOAP action is passed in the HTTP Content-Type of the request.
     * This parameter is used only in the case of SOAP 1.2 requests.
     * This option is enabled by default.
     * \since 1.11
     */
    bool sendSoapActionInHttpHeader() const;

    /**
     * \brief setSendSoapActionInWsAddressingHeader
     * \param sendInWsAddressingHeader
     * This function can be used to enable/disable the sending of the SOAP action embedded to
     * the request's SOAP envelope header. See the headers of the WS-Addressing:
     * https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/messaging-protocols#message-addressing-headers
     * \since 1.11
     */
    void setSendSoapActionInWsAddressingHeader(bool sendInWsAddressingHeader);

    /**
     * \brief sendSoapActionInWsAddressingHeader
     * \return true if the SOAP action is embedded to the request's SOAP envelope
     * header. See the headers of the WS-Addressing:
     * https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/messaging-protocols#message-addressing-headers
     * This option is disabled by default, the SOAP action is sent in the Conten-Type HTTP header.
     * See setSendHTTPActionParameter for more details.
     * \since 1.11
     */
    bool sendSoapActionInWsAddressingHeader() const;

private:
    friend class KDSoapThreadTask;
    KDSoapClientInterfacePrivate *const d;
};

#endif // KDSOAPCLIENTINTERFACE_H
