//     ____   ______ __
//    / __ \ / ____// /
//   / /_/ // /    / /
//  / ____// /___ / /___   PixInsight Class Library
// /_/     \____//_____/   PCL 2.4.23
// ----------------------------------------------------------------------------
// pcl/NetworkTransfer.h - Released 2022-03-12T18:59:29Z
// ----------------------------------------------------------------------------
// This file is part of the PixInsight Class Library (PCL).
// PCL is a multiplatform C++ framework for development of PixInsight modules.
//
// Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved.
//
// Redistribution and use in both source and binary forms, with or without
// modification, is permitted provided that the following conditions are met:
//
// 1. All redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. All redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
//    of their contributors, may be used to endorse or promote products derived
//    from this software without specific prior written permission. For written
//    permission, please contact info@pixinsight.com.
//
// 4. All products derived from this software, in any form whatsoever, must
//    reproduce the following acknowledgment in the end-user documentation
//    and/or other materials provided with the product:
//
//    "This product is based on software from the PixInsight project, developed
//    by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
//
//    Alternatively, if that is where third-party acknowledgments normally
//    appear, this acknowledgment must be reproduced in the product itself.
//
// THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
// INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
// DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// ----------------------------------------------------------------------------

#ifndef __PCL_NetworkTransfer_h
#define __PCL_NetworkTransfer_h

/// \file pcl/NetworkTransfer.h

#ifndef __PCL_BUILDING_PIXINSIGHT_APPLICATION

#include <pcl/Defs.h>

#include <pcl/Control.h>
#include <pcl/UIObject.h>

namespace pcl
{

// ----------------------------------------------------------------------------

/*!
 * \class NetworkTransfer
 * \brief Synchronous network transfers.
 *
 * %NetworkTransfer is a simple network utility class with the following
 * capabilities:
 *
 * \li Synchronous download and upload network data transfers.
 *
 * \li HTTP, FTP, SMTP, HTTPS, FTPS and SMTPS protocols supported.
 *
 * \li Supports user authentication.
 *
 * \li Event-driven operation with separate download, upload and progress event
 * handlers.
 *
 * \li Transparent reusable connections for maximum performance.
 *
 * \li Thread-safe class.
 */
class PCL_CLASS NetworkTransfer : public UIObject
{
public:

   /*!
    * Constructs a default %NetworkTransfer object.
    */
   NetworkTransfer();

   /*!
    * Destroys this client-side instance and dereferences the server-side
    * object. If the server-side object becomes unreferenced, it will be
    * garbage-collected and eventually destroyed by the core application.
    *
    * When the server-side object is destroyed, all active network connections
    * are also closed automatically. See the CloseConnection() member function
    * form more information.
    */
   virtual ~NetworkTransfer()
   {
   }

   /*!
    * Copy constructor. This constructor is disabled because %NetworkTransfer
    * represents unique server-side objects.
    */
   NetworkTransfer( const NetworkTransfer& ) = delete;

   /*!
    * Copy assignment. This operator is disabled because %NetworkTransfer
    * represents unique server-side objects.
    */
   NetworkTransfer& operator =( const NetworkTransfer& ) = delete;

   /*!
    * Ensures that the server-side object managed by this instance is uniquely
    * referenced.
    *
    * Since network transfers are unique objects by definition, calling this
    * member function has no effect.
    */
   void EnsureUnique() override
   {
      // Unique by definition
   }

   /*!
    * Returns a reference to a null %NetworkTransfer instance. A null
    * %NetworkTransfer does not correspond to an existing network transfer in
    * the PixInsight core application.
    */
   static NetworkTransfer& Null();

   /*!
    * Sets the URL of this %NetworkTransfer object.
    *
    * \param url           A valid URL identifying the network resource of this
    *                      network transfer operation.
    *
    * \param userName      The user name for authentication. The default value
    *                      is an empty string.
    *
    * \param userPassword  The user password for authentication. The default
    *                      value is an empty string.
    *
    * URLs must be valid and conform to the URI specification from RFC 3986
    * (Uniform Resource Identifier: Generic Syntax). The HTTP, FTP, HTTPS,
    * FTPS, SSH and SMTP protocols are supported.
    *
    * The \a url may include user authentication data in the 'user:password@'
    * format. It may also include a port specification. For example:
    *
    * <tt>ftp://somebody:secretpwd@ftp.somewhere.com/mydir/myfile.txt:21</tt>
    *
    * However, if explicit user name and password are specified, they will
    * override any user authentication data included in the URL. In all cases,
    * user authentication data will never be shown on the console.
    *
    * If the specified URL is not syntactically valid, this function throws an
    * Error exception. Existence or responsiveness of the server are not
    * verified.
    */
   void SetURL( const String& url, const String& userName = String(), const String& userPassword = String() );

   /*!
    * Sets the proxy URL of this %NetworkTransfer object.
    *
    * \param url           A valid URL identifying the proxy server used by
    *                      this network transfer operation.
    *
    * \param userName      The user name for proxy authentication. The default
    *                      value is an empty string.
    *
    * \param userPassword  The user password for proxy authentication. The
    *                      default value is an empty string.
    *
    * The parameters used by this member function are identical to those of the
    * SetURL() function.
    *
    * If the specified URL is not syntactically valid, this function throws an
    * Error exception. Existence or responsiveness of the proxy server are not
    * verified.
    */
   void SetProxyURL( const String& url, const String& userName = String(), const String& userPassword = String() );

   /*!
    * Set SSL/TLS options.
    *
    * \param useSSL     Enable SSL/TLS for the FTP, SMTP, POP3 and IMAP
    *                   protocols. These are protocols that start plain text by
    *                   default but can be restarted automatically using secure
    *                   connections. The default value is true.
    *
    * \param forceSSL   If true, SSL will be required for communication during
    *                   the whole transfer operation; in this case, if SSL is
    *                   not available the connection will fail. If this
    *                   parameter is false, SSL/TLS will be used if available,
    *                   but the operation will continue (insecurely) otherwise.
    *                   The default value is false.
    *
    * \param verifyPeer Verify the authenticity of the peer's certificate, and
    *                   fail if it is not authentic. The peer's certificate is
    *                   checked through a chain of certification authority (CA)
    *                   cerfificates supplied by the calling machine. If this
    *                   parameter is false, the connection won't fail if the
    *                   authenticity of the server's certificate cannot be
    *                   verified. This parameter works for all TLS based
    *                   protocols: HTTPS, FTPS, SMTPS, etc. The default value
    *                   is true.
    *
    * \param verifyHost Verify the identity of the host. The connection will
    *                   fail if the Common Name or Subject Alternate Name
    *                   fields of the server's certificate don't match the host
    *                   name in the URL set via the SetURL() member function.
    *                   If this parameter is false, the connection won't fail
    *                   if the identity of the server cannot be verified. This
    *                   parameter works for all TLS based protocols: HTTPS,
    *                   FTPS, SMTPS, etc. If the \a verifyPeer parameter is
    *                   false, this parameter is ignored. The default value is
    *                   true.
    *
    * By default, all %NetworkTransfer objects are initialized to use SSL/TLS
    * if possible for plain text protocols. Authenticity of peer and host
    * certificates are always verified by default. In other words, the default
    * parameter values of this member function reflect the default state of the
    * object upon construction.
    *
    * If the specified SSL settings are not valid for some reason, or if an
    * internal error occurs, this function throws an Error exception.
    */
   void SetSSL( bool useSSL = true, bool forceSSL = false, bool verifyPeer = true, bool verifyHost = true );

   /*!
    * Define a set of custom HTTP headers.
    *
    * \param nlsHeaders A newline-separated list of custom HTTP headers. To
    *                   remove all custom headers and return to the default set
    *                   of HTTP headers, specify an empty string.
    *
    * The specified list of custom headers will replace any previously defined
    * list, if one was set by calling this function.
    *
    * To disable an HTTP header, specify an empty header value (no characters
    * after the ':'). For example:
    *
    * "User-Agent:"
    *
    * To define a custom header without a value, include no ':' separator and
    * end the header with a semicolon:
    *
    * "CustomHeader;"
    *
    * Example:
    *
    * \code
    * NetworkTransfer N;
    * N.SetURL( "http://foo-bar.com/" );
    * N.SetCustomHTTPHeaders( "Content-Type: text/plain\nMyCustomHeader: 1" );
    * N.OnUploadDataRequested( MyUploadDataFunc, receiver );
    * N.Upload();
    * \endcode
    */
   void SetCustomHTTPHeaders( const String& nlsHeaders );

   /*!
    * Sets the connection timeout for this %NetworkTransfer object.
    *
    * \param seconds    Connection timeout in seconds. Can legally be zero to
    *                   reset the connection timeout to its default value of 30
    *                   seconds.
    *
    * If the specified timeout is not valid for some reason, or if an internal
    * error occurs, this function throws an Error exception.
    */
   void SetConnectionTimeout( int seconds );

   /*!
    * Performs a data download operation.
    *
    * Returns true upon successful completion of the download operation.
    * Returns false in the event of error, or if the operation was aborted.
    *
    * \note Before invoking this function, a download event handler must be set
    * by calling OnDownloadDataAvailable().
    */
   bool Download();

   /*!
    * Performs a data upload operation.
    *
    * \param uploadSize The total size in bytes of the data that will be
    *                   transferred. For example, if you are going to upload a
    *                   disk file, you should specify the file size here. If
    *                   the size cannot be known in advance, this argument can
    *                   legally be zero. However, specifying the total upload
    *                   size can improve uploading performance significantly.
    *                   The default value is zero.
    *
    * Returns true upon successful completion of the upload operation. Returns
    * false in the event of error, or if the operation was aborted.
    *
    * \note Before invoking this function, an upload event handler must be set
    * by calling OnUploadDataRequested().
    */
   bool Upload( fsize_type uploadSize = 0 );

   /*!
    * Performs an HTTP POST operation.
    *
    * Example:
    *
    * \code
    * NetworkTransfer transfer;
    * transfer.SetURL( "http://example-cars-info-site.com/" );
    * transfer.OnDownloadDataAvailable( GetCarInfo, receiver );
    * if ( !transfer.POST( "brand=Toyota&model=RAV4" ) )
    *    throw Error( "POST operation failed: " + transfer.ErrorInformation() );
    * \endcode
    *
    * Returns true upon successful completion of the POST operation. Returns
    * false in the event of error, or if the operation has been aborted.
    *
    * This member function performs a download operation. The downloaded data
    * will be the server response to the POSTed \a fields.
    *
    * \note Before invoking this function, a download event handler must be set
    * by calling OnDownloadDataAvailable().
    */
   bool POST( const String& fields );

   /*!
    * Performs an SMTP operation.
    *
    * \param mailFrom   The 'return-to' email address.
    *
    * \param mailRecipients   The list of recipients for this SMTP operation,
    *                   usually a list of email addresses that will receive the
    *                   transferred data as an email message.
    *
    * Returns true upon successful completion of the SMTP operation. Returns
    * false in the event of error, or if the operation has been aborted.
    *
    * This member function performs an upload operation. The uploaded data will
    * be the header and body of an email message. See RFC 5322 for detailed
    * information on the data format applicable.
    *
    * \note Before invoking this function, an upload event handler must be set
    * by calling OnUploadDataRequested().
    */
   bool SMTP( const String& mailFrom, const StringList& mailRecipients );

   /*!
    * Closes the internal network connection associated with this object.
    *
    * Normally, when you call one of the Download(), Upload(), POST() or SMTP()
    * member functions for the first time with a %NetworkTransfer object, the
    * PixInsight core application does not close the network connection
    * automatically after completing the operation. The connection remains
    * active for some time, ready to be reused if the same %NetworkTransfer
    * object is used again to perform additional (and compatible) data
    * transfers. This greatly improves performance of network transactions.
    *
    * By calling this function you can force immediate close of the active
    * connection. If there is no active connection associated with this object,
    * this function is simply ignored. Note that the destructor of
    * %NetworkTransfer also closes any active connection automatically.
    */
   void CloseConnection();

   /*!
    * Returns the URL of the last network transfer operation performed with
    * this object, or an empty string if no transfer operation has been done.
    *
    * \note User authentication data (user name and password) will never be
    * included in the URL returned by this function.
    */
   String URL() const;

   /*!
    * Returns the newline-separated list of custom HTTP headers defined for
    * this object, or an empty string if no custom headers have been defined.
    * Custom HTTP headers are defined by calling SetCustomHTTPHeaders().
    */
   String CustomHTTPHeaders() const;

   /*!
    * Returns the URL of the proxy server used in the last network transfer
    * operation performed with this object, or an empty string if no proxy
    * server has been specified.
    *
    * \note Proxy user authentication data (user name and password) will never
    * be included in the URL returned by this function.
    */
   String ProxyURL() const;

   /*!
    * Returns true if the last network transfer operation was successful; false
    * in the event of error, if no transfer operation has been performed with
    * this object, or if the last operation was aborted.
    */
   bool PerformedOK() const;

   /*!
    * A synonym for PerformedOK()
    */
   operator bool() const
   {
      return PerformedOK();
   }

   /*!
    * Returns true iff the last network transfer operation was aborted by an
    * event handler. Returns false if the operation was not aborted (whether
    * successful or not), or if no transfer operation has been performed with
    * this object.
    */
   bool WasAborted() const;

   /*!
    * Returns the server response code after the last call to Perform().
    * Returns zero if the response code could not be retrieved, or if no
    * transfer operation has been done.
    */
   int ResponseCode() const;

   /*!
    * The content type (or MIME type) reported by the remote server after a
    * successful download operation, or an empty string if no valid content
    * type could be retrieved, if the last operation was an upload, or if no
    * transfer operation has been performed with this object.
    */
   String ContentType() const;

   /*!
    * Returns the total number of bytes transferred in the last network
    * operation performed with this object (whether successful or not).
    */
   fsize_type BytesTransferred() const;

   /*!
    * Returns the transfer speed in KiB per second, measured for the last
    * network transfer operation performed with this object.
    */
   double TotalSpeed() const;

   /*!
    * Returns the total elapsed time in seconds, measured for the last
    * network transfer operation performed with this object.
    */
   double TotalTime() const;

   /*!
    * Returns the error information generated in the last network transfer
    * operation. Returns an empty string if no error occurred, or if no
    * transfer operation has been performed with this object.
    *
    * Note that this function may return some useful diagnostics information,
    * even if the last operation was successful.
    */
   String ErrorInformation() const;

   /*!
    * \defgroup network_transfer_event_handlers NetworkTransfer Event Handlers
    */

   /*!
    * Defines the prototype of a <em>download event handler</em>.
    *
    * A download event is generated when a %NetworkTransfer instance receives
    * data from a remote location, which is ready to be processed by the
    * calling process.
    *
    * \param sender  The object that sends a download event.
    *
    * \param buffer  The starting address of a contiguous block of data that
    *                has been downloaded. The handler function must not modify
    *                these data in any way.
    *
    * \param size    The length in bytes of the \a buffer data block.
    *
    * The handler returns true to continue the download operation. If it
    * returns false, the operation will be aborted.
    *
    * \ingroup network_transfer_event_handlers
    */
   typedef bool (Control::*download_event_handler)( NetworkTransfer& sender, const void* buffer, fsize_type size );

   /*!
    * Defines the prototype of an <em>upload event handler</em>.
    *
    * An upload event is generated when a %NetworkTransfer instance is ready to
    * send new data to a remote location, which must be provided by the calling
    * process.
    *
    * \param sender  The object that sends an upload event.
    *
    * \param buffer  The starting address of a contiguous memory block where
    *                the data to be uploaded must be copied by the handler
    *                function.
    *
    * \param maxSize The maximum length in bytes of the \a buffer data block.
    *                The handler function must \e not store more bytes than the
    *                value passed in this argument.
    *
    * The handler returns an integer value, which will be interpreted as
    * follows:
    *
    * \li An integer greater than zero is the actual size in bytes of the
    * contiguous block that has been copied back to the passed \a buffer. In
    * this case the returned value must be less than or equal to \a maxSize.
    *
    * \li Zero to finalize the upload operation.
    *
    * \li A negative integer to abort the upload operation.
    *
    * \ingroup network_transfer_event_handlers
    */
   typedef fsize_type (Control::*upload_event_handler)( NetworkTransfer& sender, void* buffer, fsize_type maxSize );

   /*!
    * Defines the prototype of a <em>progress event handler</em>.
    *
    * A %NetworkTransfer instance generates progress events at regular
    * intervals during an active data transfer operation. Handling these events
    * can be useful to provide feedback to the user during long download or
    * upload procedures.
    *
    * \param sender           The object that sends a progress event.
    *
    * \param downloadTotal    Total download size in bytes.
    *
    * \param downloadCurrent  Size of currently downloaded data in bytes.
    *
    * \param uploadTotal      Total upload size in bytes.
    *
    * \param uploadCurrent    Size of currently uploaded data in bytes.
    *
    * The handler returns true to continue the ongoing operation. If it returns
    * false, the operation will be aborted.
    *
    * \ingroup network_transfer_event_handlers
    */
   typedef bool (Control::*progress_event_handler)( NetworkTransfer& sender,
                                    fsize_type downloadTotal, fsize_type downloadCurrent,
                                    fsize_type uploadTotal, fsize_type uploadCurrent );

   /*!
    * Sets the handler for <em>download data available</em> events generated by
    * this %NetworkTransfer object. These events are generated when the object
    * receives data from a remote location, which is ready to be processed.
    *
    * \param handler    The event handler. Must be a member function of the
    *                   \a receiver object's class.
    *
    * \param receiver   The control that will receive events from this object.
    *
    * \ingroup network_transfer_event_handlers
    */
   void OnDownloadDataAvailable( download_event_handler handler, Control& receiver );

   /*!
    * Sets the handler for <em>upload data requested</em> events generated by
    * this %NetworkTransfer object. These events are generated when the object
    * is ready to send new data to a remote location.
    *
    * \param handler    The event handler. Must be a member function of the
    *                   \a receiver object's class.
    *
    * \param receiver   The control that will receive events from this object.
    *
    * \ingroup network_transfer_event_handlers
    */
   void OnUploadDataRequested( upload_event_handler handler, Control& receiver );

   /*!
    * Sets the handler for <em>transfer progress</em> events generated by this
    * %NetworkTransfer object. These events are generated at regular intervals
    * during an active data transfer operation.
    *
    * \param handler    The event handler. Must be a member function of the
    *                   \a receiver object's class.
    *
    * \param receiver   The control that will receive events from this object.
    *
    * \ingroup network_transfer_event_handlers
    */
   void OnTransferProgress( progress_event_handler handler, Control& receiver );

private:

   download_event_handler onDownloadDataAvailable = nullptr;
   upload_event_handler   onUploadDataRequested   = nullptr;
   progress_event_handler onTransferProgress      = nullptr;

   NetworkTransfer( void* h ) : UIObject( h )
   {
   }

   void* CloneHandle() const override;

   friend class NetworkTransferEventDispatcher;
};

// ----------------------------------------------------------------------------

} // pcl

#endif   // __PCL_BUILDING_PIXINSIGHT_APPLICATION

#endif   // __PCL_NetworkTransfer_h

// ----------------------------------------------------------------------------
// EOF pcl/NetworkTransfer.h - Released 2022-03-12T18:59:29Z
