/*---------------------------------------------------------------------\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\---------------------------------------------------------------------*/
/** \file	zypp/Fetcher.h
 *
*/
#ifndef ZYPP_FETCHER_H
#define ZYPP_FETCHER_H

#include <iosfwd>
#include <list>

#include <zypp-core/base/Flags.h>
#include <zypp-core/base/PtrTypes.h>
#include <zypp-core/Pathname.h>
#include <zypp-core/Url.h>
#include <zypp-core/OnMediaLocation>
#include <zypp/Digest.h>
#include <zypp/MediaSetAccess.h>
#include <zypp/FileChecker.h>
#include <zypp-core/ui/ProgressData>

///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////

  /**
  * This class allows to retrieve a group of files in a confortable
  * way, providing some smartness that does not belong to the
  * media layer like:
  *
  * \li Configurable local caches to retrieve already
  *     donwloaded files.
  * \li File checkers that can check for right checksums
  *     digital signatures, etc.
  *
  * \code
  * MediaSetAccess access(url, path);
  * Fetcher fetcher;
  * fetcher.enqueue( OnMediaLocation().setLocation("/somefile") );
  * fetcher.addCachePath("/tmp/cache")
  * fetcher.start( "/download-dir, access );
  * fetcher.reset();
  * \endcode
  *
  * To use the checkers. just create a functor implementing
  * bool operator()(const Pathname &file) \see FileChecker.
  * Pass the necessary validation data in the constructor
  * of the functor, and pass the object to the \ref enqueue
  * method.
  *
  * \code
  * ChecksumFileChecker checker(CheckSum("sha1", "....");
  * fetcher.enqueue( location, checker);
  * \endcode
  *
  * If you need to use more than one checker
  * \see CompositeFileChecker
  *
  * Additionally, you can automatically enqueue a job
  * with a checksum checker by using \ref enqueueDigested
  * which will use the \ref OnMediaLocation checksum
  * automatically.
  *
  * \code
  * location.setChecksum(CheckSum("sha1", "...."));
  * fetcher.enqueueDigested(location);
  * \endcode
  *
  * \note If the checksum of the location is empty, but
  * \ref enqueueDigested is used, then the user will get a
  * warning that the file has no checksum.
  *
  * Additionally, Fetcher supports checking the downloaded
  * content by using signed indexes on the remote side.
  *
  * \code
  * MediaSetAccess access(url, path);
  * Fetcher fetcher;
  * fetcher.addIndex(OnMediaLocation("/content"));
  * fetcher.enqueue( OnMediaLocation().setLocation("/somefile") );
  * fetcher.start( "/download-dir, access );
  * fetcher.reset();
  * \endcode
  *
  * Indexes are supported in CHECKSUMS format (simple text file)
  * with checksum and file name, or content file, whith
  * HASH SHA1 line entries.
  *
  * \note The indexes file names are relative to the directory
  * where the index is.
  *
  * \note libzypp-11.x: Introduction of sha256 lead to the insight
  * that using SHA1SUMS as filename was a bad choice. New media store
  * the checksums in a CHECKSUMS file. If a CHECKSUMS file is not
  * present, we fall back looking for a SHA1SUMS file. The checksum
  * type (md5,sha1,sha256) is auto detected by looking at the cheksums
  * length. No need to somehow encode it in the filename.
  *
  * \note bsc#1253740: Replicating the file tree below the destination
  * directory fails if filenames stored in an OnMediaLocation are relative
  * and referring to "../". They will point to a location outside the cache.
  * To fix the Fetcher writing to a location outside the destination directory
  * all remote paths will be mapped to filenames below the destination directory.
  * \ref mapToCachePath will return the location below the destination directory,
  * where a remote resource will be located.
  */
  class ZYPP_API Fetcher
  {
    friend std::ostream & operator<<( std::ostream & str,
                                      const Fetcher & obj );
  public:
    /** Implementation  */
    class Impl;
  public:

    /**
     * Various option flags to change behavior
     */
    enum Option
    {
      /**
       * If a content file is found, it is
       * downloaded and read.
       */
      AutoAddContentFileIndexes = 0x0001,
      /**
       * If a CHECKSUMS file is found, it is
       * downloaded and read.
       */
      AutoAddChecksumsIndexes = 0x0002,
      /**
       * If a content or CHECKSUMS file is found,
       * it is downloaded and read.
       */
      AutoAddIndexes = AutoAddContentFileIndexes | AutoAddChecksumsIndexes,
    };
    ZYPP_DECLARE_FLAGS(Options, Option);

    /** Default ctor */
    Fetcher();
    /** Dtor */
    virtual ~Fetcher();

  public:

   /**
    * Set the Fetcher options
    * \see Fetcher::Options
    */
    void setOptions( Options options );

   /**
    * Get current options
    * \see Fetcher::Options
    */
    Options options() const;

   /**
    * Adds an index containing metadata (for example
    * checksums ) that will be retrieved and read
    * before the job processing starts.
    *
    * Nothing will be transferred or checked
    * until \ref start() is called.
    *
    * The index is relative to the media path, and
    * the listed files too.
    *
    * Indexes in the SHA1SUM format, and YaST
    * content file
    *
    * The file has to be signed or the user will be
    * warned that the file is unsigned. You can
    * place the signature next to the file adding the
    * .asc extension.
    *
    * If you expect the key to not to be in the target
    * system, then you can place it next to the index
    * using adding the .key extension.
    *
    */
    void addIndex( const OnMediaLocation &resource );

   /**
    * Enqueue a object for transferal, they will not
    * be transferred until \ref start() is called
    *
    */
    void enqueue( const OnMediaLocation &resource,
                  const FileChecker &checker = FileChecker() );

    /**
    * Enqueue a object for transferal, they will not
    * be transferred until \ref start() is called
    *
    * \note As \ref OnMediaLocation contains the digest information,
    * a \ref ChecksumFileChecker is automatically added to the
    * transfer job, so make sure you don't add another one or
    * the user could be asked twice.
    *
    * \todo FIXME implement checker == operator to avoid this.
    */
     void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker = FileChecker() );


    /**
      * \deprecated
      * the deltafile argument is part of OnMediaLocation now, please use \ref enqueueDigested without the
      * deltafile argument
      */
    ZYPP_DEPRECATED void enqueueDigested( const OnMediaLocation &resource,
                          const FileChecker &checker, const Pathname &deltafile );


    /**
     * Enqueue a directory
     *
     * As the files to be enqueued are not known
     * in advance, all files whose checksum can
     * be found in some index are enqueued with
     * checksum checking. Otherwise they are not.
     *
     * Some index may provide
     * the checksums, either by \ref addIndex or
     * using \ref AutoAddIndexes flag.
     *
     * Files are checked by providing a
     * CHECKSUMS or content file listing
     * <checksum> filename
     * and a respective CHECKSUMS.asc/content.asc which has
     * the signature for the checksums.
     *
     * If you expect the user to not have the key of
     * the signature either in the trusted or untrusted
     * keyring, you can offer it as CHECKSUMS.key (or content.key)
     *
     * \param recursive True if the complete tree should
     * be enqueued.
     *
     * \note As \ref checksums are read from the index,
     * a \ref ChecksumFileChecker is automatically added to
     * transfer jobs having a checksum available,
     * so make sure you don't add another one or
     * the user could be asked twice.
     *
     * \note The format of the file CHECKSUMS is the output of:
     * ls | grep -v CHECKSUMS | xargs sha256sum > CHECKSUMS
     * in each subdirectory.
     *
     * \note Every file CHECKSUMS.* except of CHECKSUMS.(asc|key|(void)) will
     * not be transferred and will be ignored.
     *
     */
    void enqueueDir( const OnMediaLocation &resource,
                     bool recursive = false,
                     const FileChecker &checker = FileChecker() );

    /**
     * Enqueue a directory and always check for
     * checksums.
     *
     * As the files to be enqueued are not known
     * in advance, all files are enqueued with
     * checksum checking. If the checksum of some file is
     * not in some index, then there will be a verification
     * warning ( \ref DigestReport ).
     *
     * Therefore some index will need to provide
     * the checksums, either by \ref addIndex or
     * using \ref AutoAddIndexes flag.
     *
     * Files are checked by providing a
     * CHECKSUMS or content file listing
     * <checksum> filename
     * and a respective CHECKSUMS.asc/content.asc which has
     * the signature for the checksums.
     *
     * If you expect the user to not have the key of
     * the signature either in the trusted or untrusted
     * keyring, you can offer it as CHECKSUMS.key (or content.key)
     *
     * \param recursive True if the complete tree should
     * be enqueued.
     *
     * \note As \ref checksums are read from the index,
     * a \ref ChecksumFileChecker is automatically added to every
     * transfer job, so make sure you don't add another one or
     * the user could be asked twice.
     *
     * \note The format of the file CHECKSUMS is the output of:
     * ls | grep -v CHECKSUMS | xargs sha256sum > CHECKSUMS
     * in each subdirectory.
     *
     * \note Every file CHECKSUMS.* except of CHECKSUMS.(asc|key|(void)) will
     * not be transferred and will be ignored.
     *
     */
    void enqueueDigestedDir( const OnMediaLocation &resource,
                             bool recursive = false,
                             const FileChecker &checker = FileChecker() );

    /**
    * adds a directory to the list of directories
    * where to look for cached files
    */
    void addCachePath( const Pathname &cache_dir );


    enum CacheOptions {
      Default     = 0,
      CleanFiles  = 1
    };
    /**
     * adds a directory to the list of directories
     * where to look for cached files and options on how to handle the cache
     */
    void addCachePath( const Pathname &cache_dir, CacheOptions options );

    /**
     * Reset the transfer (jobs) list
     * \note It does not reset the cache directory list
     */
    void reset();

    /**
     * Sets the media set access that will be used to precache and
     * to download the files when \ref start is called. Calling \ref start
     * will reset the media access.
     */
    void setMediaSetAccess ( MediaSetAccess &media );

    /**
    * start the transfer to a destination directory
    * \a dest_dir
    * The media has to be provides with \ref setMediaSetAccess before
    * calling this version of start.
    * The file tree will be replicated inside this
    * directory
    *
    */
    void start( const Pathname &dest_dir,
      const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );

    /**
    * start the transfer to a destination directory
    * \a dest_dir
    * You have to provde a media set access
    * \a media to get the files from
    * The file tree will be replicated inside this
    * directory
    *
    */
    void start( const Pathname &dest_dir,
                MediaSetAccess &media,
                const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );

  public:
    /** Map a resource filename to a local path below a destDir.
     * bsc#1253740: To fix the Fetcher writing to a location outside
     * the destination directory if remote paths have leading "../"s,
     * this function maps a resource filename to it's location below
     * the destination directory.
     * \note It's an implementation detail, that in many cases the
     * "../"s are encoded as "%2E%2E/"s. This may change in future
     * version if necessary.
     */
    static Pathname mapToCachePath( Pathname remotePath_r );
    static Pathname mapToCachePath( const OnMediaLocation & resource_r )
    { return mapToCachePath( resource_r.filename() ); }

  private:
    /** Pointer to implementation */
    RWCOW_pointer<Impl> _pimpl;
  };
  ///////////////////////////////////////////////////////////////////
  ZYPP_DECLARE_OPERATORS_FOR_FLAGS(Fetcher::Options);

  /** relates: Fetcher Stream output */
  std::ostream & operator<<( std::ostream & str, const Fetcher & obj );

  /////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////
#endif // ZYPP_FETCHER_H
