added first skeleton of media backend
authorJiri Srain <jsrain@suse.cz>
Thu, 8 Dec 2005 15:51:15 +0000 (15:51 +0000)
committerJiri Srain <jsrain@suse.cz>
Thu, 8 Dec 2005 15:51:15 +0000 (15:51 +0000)
configure.ac
zypp/Makefile.am
zypp/media/Makefile.am
zypp/media/MediaAccess.cc [new file with mode: 0644]
zypp/media/MediaAccess.h [new file with mode: 0644]
zypp/media/MediaCurl.cc [new file with mode: 0644]
zypp/media/MediaCurl.h [new file with mode: 0644]
zypp/media/MediaException.h [new file with mode: 0644]
zypp/media/MediaHandler.cc [new file with mode: 0644]
zypp/media/MediaHandler.h [new file with mode: 0644]

index 66fb83061416d5d1a0de1125e80e6a97d301aaec..5f43f7fb81f9e84cfe0c3196c118674240cd9051 100644 (file)
@@ -114,6 +114,9 @@ dnl checks for header files
 AC_CHECK_HEADERS([boost/scoped_ptr.hpp],
         [],
         AC_MSG_ERROR([boost header not found. please install boost-devel]))
+AC_CHECK_HEADERS([curl/curl.h],
+        [],
+        AC_MSG_ERROR([libcurl header not found. please install curl-devel.]))
 dnl ==================================================
 dnl checks for typedefs
 dnl ==================================================
@@ -150,7 +153,8 @@ AC_OUTPUT(  \
        zypp/parser/yum/schema/Makefile \
        zypp/solver/Makefile            \
        zypp/source/Makefile            \
-       zypp/source/yum/Makefile
+       zypp/source/yum/Makefile        \
+       zypp/media/Makefile             \
 )
 dnl ==================================================
 
index 56f709c0ba8cae83c3547cce1f9dae0a3e26400b..6afdca210dde3a3a3aba6486b16a655998796f72 100644 (file)
@@ -1,7 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 ## ##################################################
 
-SUBDIRS = base parser capability solver source detail
+SUBDIRS = base parser capability solver source detail media
 
 ## ##################################################
 
@@ -25,9 +25,9 @@ include_HEADERS = NeedAType.h \
        Changelog.h     \
                        \
        ExternalProgram.h       \
-       Pathname.cc     \
-       PathInfo.cc     \
-       Digest.cc
+       Pathname.     \
+       PathInfo.     \
+       Digest.h
 
 ## ##################################################
 
@@ -66,6 +66,8 @@ lib@PACKAGE@_la_LIBADD =        base/lib@PACKAGE@_base.la     \
                                capability/lib@PACKAGE@_capability.la   \
                                solver/lib@PACKAGE@_solver.la   \
                                parser/lib@PACKAGE@_parser.la   \
-                               source/lib@PACKAGE@_source.la
+                               source/lib@PACKAGE@_source.la   \
+                               media/lib@PACKAGE@_media.la     \
+                               -lutil
 
 ## ##################################################
index 6d619823244307ab00592441a9142132c0aaf270..03728af4d11b54d8734b566bf069dd9844637048 100644 (file)
@@ -10,8 +10,9 @@ INCLUDES = -I$(oldincludedir)/libxml2
 include_HEADERS =              \
        MediaException.h        \
        MediaAccess.h           \
-       MediaHandler.h          \
-       MediaCurl.h
+       MediaHandler.h          
+
+#      MediaCurl.h
 
 
 noinst_LTLIBRARIES =   lib@PACKAGE@_media.la
@@ -20,8 +21,9 @@ noinst_LTLIBRARIES =  lib@PACKAGE@_media.la
 
 lib@PACKAGE@_media_la_SOURCES = \
        MediaAccess.cc          \
-       MediaHandler.cc         \
-       MediaCurl.cc
+       MediaHandler.cc         
+
+#      MediaCurl.cc
 
 lib@PACKAGE@_media_la_LIBADD = -lcurl
 
diff --git a/zypp/media/MediaAccess.cc b/zypp/media/MediaAccess.cc
new file mode 100644 (file)
index 0000000..5e0db37
--- /dev/null
@@ -0,0 +1,390 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaAccess.cc
+ *
+*/
+
+#include <ctype.h>
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/media/MediaException.h"
+#include "zypp/media/MediaAccess.h"
+#include "zypp/media/MediaHandler.h"
+
+/*
+#include "zypp/media/MediaCD.h"
+#include "zypp/media/MediaDIR.h"
+#include "zypp/media/MediaDISK.h"
+#include "zypp/media/MediaNFS.h"
+#include "zypp/media/MediaSMB.h"
+#include "zypp/media/MediaCIFS.h"
+*/
+//#include "zypp/media/MediaCurl.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaAccess
+//
+///////////////////////////////////////////////////////////////////
+
+const Pathname MediaAccess::_noPath; // empty path
+
+///////////////////////////////////////////////////////////////////
+// constructor
+MediaAccess::MediaAccess ()
+    : _handler (0)
+{
+}
+
+// destructor
+MediaAccess::~MediaAccess()
+{
+#warning FIXME uncomment
+#if 0
+  DBG << *this << endl;
+#endif
+  close(); // !!! make sure handler gets properly deleted.
+}
+
+// open URL
+void
+MediaAccess::open (const Url& url, const Pathname & preferred_attach_point)
+{
+#warning FIXME uncomment once media backends get ready
+#if 0
+    if(!url.isValid()) {
+        ERR << Error::E_bad_url << " opening " << url << endl;
+       return Error::E_bad_url;
+    }
+
+    close();
+
+    switch ( url.protocol() ) {
+      case Url::cd:
+      case Url::dvd:
+        _handler = new MediaCD (url,preferred_attach_point);
+        break;
+      case Url::nfs:
+        _handler = new MediaNFS (url,preferred_attach_point);
+        break;
+      case Url::file:
+      case Url::dir:
+        _handler = new MediaDIR (url,preferred_attach_point);
+        break;
+      case Url::hd:
+        _handler = new MediaDISK (url,preferred_attach_point);
+        break;
+      case Url::smb:
+      case Url::cifs:
+        _handler = new MediaCIFS (url,preferred_attach_point);
+        break;
+      case Url::ftp:
+      case Url::http:
+      case Url::https:
+        _handler = new MediaCurl (url,preferred_attach_point);
+        break;
+      case Url::unknown:
+       ERR << Error::E_bad_media_type << " opening " << url << endl;
+       return Error::E_bad_media_type;
+       break;
+    }
+
+    // check created handler
+    if ( !_handler ){
+      ERR << "Failed to create media handler" << endl;
+      return Error::E_system;
+    }
+
+    MIL << "Opened: " << *this << endl;
+    return Error::E_ok;
+#endif
+}
+
+// Type of media if open, otherwise NONE.
+#warning FIXME uncomment once real Url class is implemented
+#if 0
+Url::Protocol
+MediaAccess::protocol() const
+{
+  if ( !_handler )
+    return Url::unknown;
+
+  return _handler->protocol();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaAccess::url
+//     METHOD TYPE : Url
+//
+Url MediaAccess::url() const
+{
+  if ( !_handler )
+    return Url();
+
+  return _handler->url();
+}
+
+// close handler
+void
+MediaAccess::close ()
+{
+  ///////////////////////////////////////////////////////////////////
+  // !!! make shure handler gets properly deleted.
+  // I.e. release attached media before deleting the handler.
+  ///////////////////////////////////////////////////////////////////
+  if ( _handler ) {
+    try {
+      _handler->release();
+    }
+    catch (const MediaException & excpt_r)
+    {
+#warning FIXME uncomment, rethrow
+#if 0
+      WAR << "Close: " << *this << " (" << err << ")" << endl;
+      ZYPP_RETHROW(excpt_r);
+#endif
+    }
+#warning FIXME uncomment
+#if 0
+    MIL << "Close: " << *this << " (" << err << ")" << endl;
+#endif
+    delete _handler;
+    _handler = 0;
+  }
+}
+
+
+// attach media
+void MediaAccess::attach (bool next)
+{
+  if ( !_handler ) {
+    INT << "Error::E_not_open" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+  }
+  _handler->attach(next);
+}
+
+// True if media is open and attached.
+bool
+MediaAccess::isAttached() const
+{
+  return( _handler && _handler->isAttached() );
+}
+
+// local directory that corresponds to medias url
+// If media is not open an empty pathname.
+const Pathname &
+MediaAccess::localRoot() const
+{
+  if ( !_handler )
+    return _noPath;
+
+  return _handler->localRoot();
+}
+
+// Short for 'localRoot() + pathname', but returns an empty
+// * pathname if media is not open.
+Pathname
+MediaAccess::localPath( const Pathname & pathname ) const
+{
+  if ( !_handler )
+    return _noPath;
+
+  return _handler->localPath( pathname );
+}
+
+void
+MediaAccess::disconnect()
+{
+  if ( !_handler )
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+
+  _handler->disconnect();
+}
+
+// release attached media
+void
+MediaAccess::release( bool eject )
+{
+  if ( !_handler )
+    return;
+
+  _handler->release( eject );
+}
+
+
+// provide file denoted by path to attach dir
+//
+// filename is interpreted relative to the attached url
+// and a path prefix is preserved to destination
+void
+MediaAccess::provideFile( const Pathname & filename, bool cached, bool checkonly) const
+{
+  if ( cached ) {
+    PathInfo pi( localPath( filename ) );
+    if ( pi.isExist() )
+      return;
+  }
+
+  if(checkonly)
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_error");
+
+  if ( !_handler ) {
+    INT << "Error::E_not_open" << " on provideFile(" << filename << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+  }
+
+  _handler->provideFile( filename );
+}
+
+void
+MediaAccess::releaseFile( const Pathname & filename ) const
+{
+  if ( !_handler )
+    return;
+
+  _handler->releaseFile( filename );
+}
+
+// provide directory tree denoted by path to attach dir
+//
+// dirname is interpreted relative to the attached url
+// and a path prefix is preserved to destination
+void
+MediaAccess::provideDir( const Pathname & dirname ) const
+{
+  if ( !_handler ) {
+    INT << "Error::E_not_open" << " on provideDir(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+  }
+
+  _handler->provideDir( dirname );
+}
+
+void
+MediaAccess::provideDirTree( const Pathname & dirname ) const
+{
+  if ( !_handler ) {
+    INT << "Error::E_not_open" << " on provideDirTree(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+  }
+
+  _handler->provideDirTree( dirname );
+}
+
+void
+MediaAccess::releaseDir( const Pathname & dirname ) const
+{
+  if ( !_handler )
+    return;
+
+  _handler->releaseDir( dirname );
+}
+
+void
+MediaAccess::releasePath( const Pathname & pathname ) const
+{
+  if ( !_handler )
+    return;
+
+  _handler->releasePath( pathname );
+}
+
+// Return content of directory on media
+void
+MediaAccess::dirInfo( list<string> & retlist, const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !_handler ) {
+    INT << "Error::E_not_open" << " on dirInfo(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+  }
+
+  _handler->dirInfo( retlist, dirname, dots );
+}
+
+// Return content of directory on media
+void
+MediaAccess::dirInfo( PathInfo::dircontent & retlist, const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !_handler ) {
+    INT << "Error::E_not_open" << " on dirInfo(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_open");
+  }
+
+  _handler->dirInfo( retlist, dirname, dots );
+}
+
+std::ostream &
+MediaAccess::dumpOn( std::ostream & str ) const
+{
+  if ( !_handler )
+    return str << "MediaAccess( closed )";
+
+#warning FIXME uncomment once real Url class is implemented
+#if 0
+  string tstr = Url::protocolToString( _handler->protocol() );
+  str << tstr << "(" << *_handler << ")";
+#endif
+  return str;
+}
+
+void MediaAccess::getFile( const Url &from, const Pathname &to )
+{
+  DBG << "From: " << from << endl << "To: " << to << endl;
+
+  Pathname path
+#warning FIXME uncomment once real Url class is implemented
+#if 0
+ = from.path()
+#endif
+;
+  Pathname dir = path.dirname();
+  string base = path.basename();
+
+  Url u = from;
+#warning FIXME uncomment once real Url class is implemented
+#if 0
+  u.setPath( dir.asString() );
+#endif
+
+  MediaAccess media;
+
+  try {
+    media.open( u );
+    media.attach();
+    media._handler->provideFileCopy( base, to );
+    media.release();
+  }
+  catch (const MediaException & excpt_r)
+  {
+#warning FIXME uncomment, add message into log
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaAccess.h b/zypp/media/MediaAccess.h
new file mode 100644 (file)
index 0000000..3440c55
--- /dev/null
@@ -0,0 +1,396 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaAccess.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAACCESS_H
+#define ZYPP_MEDIA_MEDIAACCESS_H
+
+#include <iosfwd>
+#include <map>
+#include <list>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/media/MediaException.h"
+
+#warning FIXME use real Url class
+// #include "zypp/@Review/Url.h"
+typedef std::string Url;
+
+namespace zypp {
+  namespace media {
+
+    class MediaHandler;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaAccess
+    /**
+     * @short Handle access to a medium
+     *
+     * The concrete @ref MediaHandler for a certain url is created
+     * on @ref open and deleted on @close.
+     *
+     * The inteface here basically checks whether the handler exists,
+     * then forwards the request to @ref MediaHandler.
+     **/
+    class MediaAccess : public base::ReferenceCounted, private base::NonCopyable
+    {
+    public:
+       typedef shared_ptr<MediaAccess> Ptr;
+       typedef shared_ptr<const MediaAccess> constPtr;
+
+    private:
+
+       static const Pathname _noPath;
+
+       /**
+        * handler for 'physical' media
+        * == 0 if not open
+        **/
+       MediaHandler * _handler;
+
+    public:
+
+       /**
+        * constructor
+        **/
+       MediaAccess();
+
+       /**
+        * open url. If preferred_attach_point is given,
+        * try to use it as attach point.
+        *
+        * <b>Caution:</b> The medium can choose a different attach point.
+        * Only getAttachPoint() knows the real attach point.
+        *
+        * \throws MediaException
+        *
+        **/
+       void open( const Url& url, const Pathname & preferred_attach_point = "" );
+
+       /**
+        * True if media is open.
+        **/
+       bool isOpen() const { return( _handler != 0 ); }
+
+       /**
+        * Used Protocol if media is opened, otherwise 'unknown'.
+        **/
+#warning FIXME uncomment once real Url class is implemented
+#if 0
+        Url::Protocol protocol() const;
+#endif
+
+       /**
+        * Url if media is opened, otherwise empty.
+        **/
+        Url url() const;
+
+       /**
+        * close url
+        *
+        * \throws MediaException
+        *
+        **/
+       void close();
+
+    public:
+
+       /**
+        * Use concrete handler to attach the media.
+        *
+        * @param next try next available device in turn until end of device
+        * list is reached (for media which are accessible through multiple
+        * devices like cdroms).
+        *
+        * \throws MediaException
+        *
+        **/
+       void attach(bool next = false);
+
+       /**
+        * True if media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       bool isAttached() const;
+
+       /**
+        * Return the local directory that corresponds to medias url,
+        * no matter if media isAttached or not. Files requested will
+        * be available at 'localRoot() + filename' or better
+        * 'localPath( filename )'.
+        *
+        * If media is not open an empty pathname is returned.
+        **/
+       const Pathname & localRoot() const;
+
+       /**
+        * Short for 'localRoot() + pathname', but returns an empty
+        * pathname if media is not open.
+        *
+        * Files provided will be available at 'localPath(filename)'.
+        **/
+       Pathname localPath( const Pathname & pathname ) const;
+
+        /**
+          Use concrete handler to disconnect the media.
+
+          This is useful for media which e.g. holds open a connection to a
+          server like FTP. After calling disconnect() the media object still is
+          valid and files are present.
+
+          After calling disconnect() it's not possible to call provideFile() or
+          provideDir() anymore.
+        *
+        * \throws MediaException
+        *
+        */
+        void disconnect();
+
+       /**
+        * Use concrete handler to release the media.
+        * @param eject if true, physically eject the media * (i.e. CD-ROM)
+        *
+        * \throws MediaException
+        *
+        **/
+       void release( bool eject = false );
+
+       /**
+        * Use concrete handler to provide file denoted by path below
+        * 'attach point'. Filename is interpreted relative to the
+        * attached url and a path prefix is preserved.
+         *
+         * @param cached  If cached is set to true, the function checks, if
+         *                the file already exists and doesn't download it again
+         *                if it does. Currently only the existence is checked,
+         *                no other file attributes.
+        * @param checkonly If this and 'cached' are set to true only the
+        *                  existence of the file is checked but it's not
+        *                  downloaded. If 'cached' is unset an errer is
+        *                  returned always.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideFile( const Pathname & filename, bool cached = false, bool checkonly = false ) const;
+
+       /**
+        * Remove filename below attach point IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseFile( const Pathname & filename ) const;
+
+       /**
+        * Use concrete handler to provide directory denoted
+        * by path below 'attach point' (not recursive!).
+        * 'dirname' is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDir( const Pathname & dirname ) const;
+
+       /**
+        * Use concrete handler to provide directory tree denoted
+        * by path below 'attach point' (recursive!!).
+        * 'dirname' is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDirTree( const Pathname & dirname ) const;
+
+       /**
+        * Remove directory tree below attach point IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseDir( const Pathname & dirname ) const;
+
+       /**
+        * Remove pathname below attach point IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * If pathname denotes a directory it is recursively removed.
+        * If pathname is empty or '/' everything below the attachpoint
+        * is recursively removed.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releasePath( const Pathname & pathname ) const;
+
+    public:
+
+       /**
+        * Return content of directory on media via retlist. If dots is false
+        * entries starting with '.' are not reported.
+        *
+        * The request is forwarded to the concrete handler,
+        * which may atempt to retieve the content e.g. via 'readdir'
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+        void dirInfo( std::list<std::string> & retlist,
+                        const Pathname & dirname, bool dots = true ) const;
+
+       /**
+        * Basically the same as dirInfo above. The content is returned as
+        * PathInfo::dircontent, which includes name and filetype of each directory
+        * entry. Retrieving the filetype usg. requires an additional ::stat call for
+        * each entry, thus it's more expensive than a simple readdir.
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void dirInfo( PathInfo::dircontent & retlist,
+                        const Pathname & dirname, bool dots = true ) const;
+
+       /**
+        * Destructor
+        **/
+       virtual ~MediaAccess();
+
+    public:
+
+       virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    public:
+        /**
+         * Get file from location at specified by URL and copy it to
+         * destination.
+         *
+         * @param from Source URL
+         * @param to   Destination file name
+        *
+        * \throws MediaException
+        *
+         **/
+        void getFile( const Url &from, const Pathname &to );
+
+    public:
+
+      /**
+       * Helper class that provides file on construction
+       * and cleans up on destruction.
+       *
+       * <b>Caution:</b> There's no synchronisation between multiple
+       * FileProvider instances, that provide the same file from the
+       * same media. If the first one goes out of scope, the file is
+       * cleaned. It's just a convenience for 'access and forgett'.
+       *
+       * <b>Caution:</b> We should either store the reference MediaAccess'
+       * MediaHandler here (for this MediaHandler must become a
+       * ref counting pointer class), or we need more info from MediaHandler
+       * (whether he's downloading to the local fs. If not, no releasefile
+       * is necessary).
+       * Currently we can not releaseFile after the media was closed
+       * (it's passed to the handler, which is deleted on close).
+       *
+       * \throws MediaException
+       **/
+      class FileProvider {
+       FileProvider( const FileProvider & );             // no copy
+       FileProvider & operator=( const FileProvider & ); // no assign
+       private:
+         MediaAccess::constPtr _media;
+         Pathname              _file;
+         Pathname              _local_file;
+       public:
+         /**
+           * \throws MediaException
+           */
+         FileProvider( MediaAccess::constPtr media_r, const Pathname & file_r )
+           : _media( media_r )
+           , _file( file_r )
+           , _local_file( "" )
+         {
+           if ( _file.empty() ) {
+             throw MediaException(ZYPP_EX_CODELOCATION, "E_bad_filename");
+           } else if ( _media ) {
+             try {
+               _media->provideFile( _file );
+               _local_file = _media->localPath( _file );
+             }
+             catch (const MediaException & excpt_r)
+              {
+               _media.reset();
+#warning FIXME rethrow the exception
+#if 0
+               ZYPP_RETHROW(excpt_r);
+#endif
+             }
+           }
+         }
+
+         ~FileProvider() {
+           if ( _media )
+           {
+             try {
+               _media->releaseFile( _file );
+             }
+             catch (const MediaException &excpt_r)
+             {
+               INT << "Exception raised while releasing file" << std::endl;
+             }
+           }
+         }
+
+       public:
+
+         /**
+          * If no error, expect operator() to return the local
+          * Pathname of the provided file.
+          **/
+         Pathname localFile() const { return _local_file; }
+
+         /**
+          * Return the local Pathname of the provided file or
+          * an empty Pathname on error.
+          **/
+         Pathname operator()() const {
+           if ( _media )
+             return _media->localPath( _file );
+           return Pathname();
+         }
+      };
+    };
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIAACCESS_H
+
diff --git a/zypp/media/MediaCurl.cc b/zypp/media/MediaCurl.cc
new file mode 100644 (file)
index 0000000..b6cb573
--- /dev/null
@@ -0,0 +1,663 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCurl.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/base/stringutil.h"
+//#include <y2util/SysConfig.h>
+
+#include "zypp/media/MediaCurl.h"
+//#include "zypp/media/MediaCallbacks.h"
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include "config.h"
+
+using namespace std;
+using namespace zypp::base;
+//using namespace MediaCallbacks;
+
+namespace zypp {
+  namespace media {
+
+Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
+
+MediaCurl::Callbacks *MediaCurl::_callbacks = 0;
+
+bool MediaCurl::_globalInit = false;
+
+///////////////////////////////////////////////////////////////////
+
+static inline void escape( string & str_r,
+                          const char char_r, const string & escaped_r ) {
+  for ( string::size_type pos = str_r.find( char_r );
+       pos != string::npos; pos = str_r.find( char_r, pos ) ) {
+    str_r.replace( pos, 1, escaped_r );
+  }
+}
+
+static inline string escapedPath( string path_r ) {
+  escape( path_r, ' ', "%20" );
+  return path_r;
+}
+
+static inline string unEscape( string text_r ) {
+  char * tmp = curl_unescape( text_r.c_str(), 0 );
+  string ret( tmp );
+  curl_free( tmp );
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaCurl
+//
+///////////////////////////////////////////////////////////////////
+
+MediaCurl::MediaCurl( const Url &      url_r,
+                     const Pathname & attach_point_hint_r )
+    : MediaHandler( url_r, attach_point_hint_r,
+                   "/", // urlpath at attachpoint
+                   true ), // does_download
+      _curl( 0 ), _connected( false )
+{
+  if ( ! _globalInit )
+    {
+      _globalInit = true;
+      CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
+      if ( ret != 0 )
+        WAR << "curl global init failed" << endl;
+    }
+}
+
+void MediaCurl::setCookieFile( const Pathname &fileName )
+{
+  _cookieFile = fileName;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::attachTo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
+//
+void MediaCurl::attachTo (bool next)
+{
+  if ( next )
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_supported_by_media");
+
+  if ( !_url.isValid() )
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_bad_url");
+
+  _curl = curl_easy_init();
+  if ( !_curl ) {
+    ERR << "curl easy init failed" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_error");
+  }
+
+  _connected = true;
+
+  CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
+  if ( ret != 0 ) {
+    ERR << "Error setting error buffer" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+  }
+
+  ret = curl_easy_setopt( _curl, CURLOPT_FAILONERROR, true );
+  if ( ret != 0 ) {
+    ERR << _curlError << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+  }
+
+  if ( _url.protocol() == Url::http ) {
+    // follow any Location: header that the server sends as part of
+    // an HTTP header (#113275)
+    ret = curl_easy_setopt ( _curl, CURLOPT_FOLLOWLOCATION, true );
+    if ( ret != 0) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+    ret = curl_easy_setopt ( _curl, CURLOPT_MAXREDIRS, 3L );
+    if ( ret != 0) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+  }
+
+  // XXX: wasn't that the wrong fix for some problem? this should be
+  // removed
+  if ( _url.protocol() == Url::https ) {
+    WAR << "Disable certificate verification for https." << endl;
+    ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYPEER, 0 );
+    if ( ret != 0 ) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+
+    ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYHOST, 0 );
+    if ( ret != 0 ) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+  }
+
+
+  /*---------------------------------------------------------------*
+   CURLOPT_USERPWD: [user name]:[password]
+
+   Url::username/password -> CURLOPT_USERPWD
+   If not provided, anonymous FTP identification
+   *---------------------------------------------------------------*/
+
+  if ( _url.username().empty() ) {
+    if ( _url.protocol() == Url::ftp ) {
+      string id = "yast2@";
+      id += VERSION;
+      DBG << "Anonymous FTP identification: '" << id << "'" << endl;
+      _userpwd = "anonymous:" + id;
+    }
+  } else {
+    _userpwd = _url.username();
+    if ( _url.password().size() ) {
+      _userpwd += ":" + _url.password();
+    }
+  }
+
+  if ( _userpwd.size() ) {
+    _userpwd = unEscape( _userpwd );
+    ret = curl_easy_setopt( _curl, CURLOPT_USERPWD, _userpwd.c_str() );
+    if ( ret != 0 ) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+  }
+
+  /*---------------------------------------------------------------*
+   CURLOPT_PROXY: host[:port]
+
+   Url::option(proxy and proxyport) -> CURLOPT_PROXY
+   If not provided, /etc/sysconfig/proxy is evaluated
+   *---------------------------------------------------------------*/
+
+  _proxy = _url.option( "proxy" );
+
+  if ( ! _proxy.empty() ) {
+    string proxyport( _url.option( "proxyport" ) );
+    if ( ! proxyport.empty() ) {
+      _proxy += ":" + proxyport;
+    }
+
+#warning FIXME enable proxy via sysconfig somehow
+#if 0
+  } else {
+
+    SysConfig cfg( "proxy" );
+
+    if ( cfg.readBoolEntry( "PROXY_ENABLED", false ) ) {
+      bool useproxy = true;
+
+      std::vector<std::string> nope;
+      stringutil::split( cfg.readEntry( "NO_PROXY" ), nope, ", \t" );
+      for ( unsigned i = 0; i < nope.size(); ++i ) {
+       // no proxy: if nope equals host,
+       // or is a suffix preceeded by a '.'
+       string::size_type pos = _url.host().find( nope[i] );
+       if ( pos != string::npos
+            && ( pos + nope[i].size() == _url.host().size() )
+            && ( pos == 0 || _url.host()[pos -1] == '.' ) ) {
+         DBG << "NO_PROXY: " << nope[i] << " matches host " << _url.host() << endl;
+         useproxy = false;
+         break;
+       }
+      }
+
+      if ( useproxy ) {
+       if ( _url.protocol() == Url::ftp ) {
+         _proxy = cfg.readEntry( "FTP_PROXY" );
+       } else if ( _url.protocol() == Url::http ) {
+         _proxy = cfg.readEntry( "HTTP_PROXY" );
+       } else if ( _url.protocol() == Url::https ) {
+         _proxy = cfg.readEntry( "HTTPS_PROXY" );
+       }
+      }
+    }
+#endif
+  }
+
+
+  DBG << "Proxy: " << _proxy << endl;
+
+  if ( ! _proxy.empty() ) {
+
+    ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
+    if ( ret != 0 ) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+
+    /*---------------------------------------------------------------*
+     CURLOPT_PROXYUSERPWD: [user name]:[password]
+
+     Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
+     If not provided, $HOME/.curlrc is evaluated
+     *---------------------------------------------------------------*/
+
+    _proxyuserpwd = _url.option( "proxyuser" );
+
+    if ( ! _proxyuserpwd.empty() ) {
+
+      string proxypassword( _url.option( "proxypassword" ) );
+      if ( ! proxypassword.empty() ) {
+       _proxyuserpwd += ":" + proxypassword;
+      }
+
+#warning FIXME enable proxy via sysconfig somehow
+#if 0
+    } else {
+
+      string curlrcFile = string( getenv("HOME") ) + string( "/.curlrc" );
+      SysConfig curlrc( curlrcFile );
+      _proxyuserpwd = curlrc.readEntry( "proxy-user" );
+
+#endif
+    }
+
+    _proxyuserpwd = unEscape( _proxyuserpwd );
+    ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
+    if ( ret != 0 ) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+
+  }
+
+  /*---------------------------------------------------------------*
+   *---------------------------------------------------------------*/
+
+  _currentCookieFile = _cookieFile.asString();
+
+  ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
+                          _currentCookieFile.c_str() );
+  if ( ret != 0 ) {
+    ERR << _curlError << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+  }
+
+  ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
+                          _currentCookieFile.c_str() );
+  if ( ret != 0 ) {
+    ERR << _curlError << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+  }
+
+  ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
+                          &MediaCurl::progressCallback );
+  if ( ret != 0 ) {
+    ERR << _curlError << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+  }
+
+  ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
+  if ( ret != 0 ) {
+    ERR << _curlError << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::disconnectFrom
+//     METHOD TYPE : PMError
+//
+void MediaCurl::disconnectFrom()
+{
+  if ( _connected ) curl_easy_cleanup( _curl );
+  _connected = false ;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::releaseFrom
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached.
+//
+void MediaCurl::releaseFrom( bool eject )
+{
+  disconnect();
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : MediaCurl::getFile
+//     METHOD TYPE : PMError
+//
+
+void MediaCurl::getFile( const Pathname & filename ) const
+{
+    // Use absolute file name to prevent access of files outside of the
+    // hierarchy below the attach point.
+    getFileCopy(filename, localPath(filename).absolutename());
+}
+
+
+void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
+{
+    DBG << filename.asString() << endl;
+
+    if(!_url.isValid())
+      throw MediaException(ZYPP_EX_CODELOCATION, string("Error::E_bad_url") + " " + _url.asString());
+
+    if(_url.host().empty())
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_no_host_specified");
+
+    string path = _url.path();
+    if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
+         filename.absolute() ) {
+      // If url has a path with trailing slash, remove the leading slash from
+      // the absolute file name
+      path += filename.asString().substr( 1, filename.asString().size() - 1 );
+    } else if ( filename.relative() ) {
+      // Add trailing slash to path, if not already there
+      if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
+      // Remove "./" from begin of relative file name
+      path += filename.asString().substr( 2, filename.asString().size() - 2 );
+    } else {
+      path += filename.asString();
+    }
+
+    Url url( _url );
+    url.setPath( escapedPath(path) );
+
+    Pathname dest = target.absolutename();
+    string destNew = target.asString() + ".new.yast.37456";
+
+    DBG << "dest: " << dest << endl;
+    DBG << "destNew: " << destNew << endl;
+
+    if( PathInfo::assert_dir( dest.dirname() ) )
+    {
+      DBG << "assert_dir " << dest.dirname() << " failed" << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, string("Error::E_system") + string(" ") + dest.dirname().asString());
+    }
+
+    DBG << "URL: " << url.asString().c_str() << endl;
+    // Use URL without options (not RFC conform) and without
+    // username and passwd (some proxies dislike them in the URL.
+    // Curloptions for these were set in attachTo().
+    Url curlUrl( url );
+    curlUrl.setUsername( "" );
+    curlUrl.setPassword( "" );
+    string urlBuffer = curlUrl.asString(true,false,true); // without options
+
+    CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
+                                     urlBuffer.c_str() );
+    if ( ret != 0 ) {
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+
+    FILE *file = fopen( destNew.c_str(), "w" );
+    if ( !file ) {
+      ERR << "fopen failed for file '" << destNew << "'" << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, string("Error::E_write_error") + string(" ") + destNew);
+    }
+
+    ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
+    if ( ret != 0 ) {
+      fclose( file );
+      ERR << _curlError << endl;
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_curl_setopt_failed");
+    }
+
+    // Set callback and perform.
+#warning FIXME reenable callback
+#if 0
+    DownloadProgressReport::Send report( downloadProgressReport );
+    report->start( url, dest );
+    if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &report ) != 0 ) {
+      WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
+    }
+#endif
+
+    ret = curl_easy_perform( _curl );
+    fclose( file );
+
+    if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
+      WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
+    }
+
+    if ( ret != 0 ) {
+      PathInfo::unlink( destNew );
+
+      ERR << "curl error: " << ret << ": " << _curlError << endl;
+      std::string err;
+      switch ( ret ) {
+        case CURLE_UNSUPPORTED_PROTOCOL:
+        case CURLE_URL_MALFORMAT:
+        case CURLE_URL_MALFORMAT_USER:
+          err = "Error::E_bad_url";
+          break;
+        case CURLE_HTTP_NOT_FOUND:
+          {
+            long httpReturnCode;
+            CURLcode infoRet = curl_easy_getinfo( _curl, CURLINFO_HTTP_CODE,
+                                                  &httpReturnCode );
+            if ( infoRet == CURLE_OK ) {
+              string msg = "HTTP return code: " +
+                           stringutil::numstring( httpReturnCode ) +
+                           " (URL: " + url.asString() + ")";
+              DBG << msg << endl;
+              if ( httpReturnCode == 401 )
+             {
+               msg = "URL: " + url.asString();
+                err = "Error::E_login_failed";
+             }
+              else
+             {
+               err = "Error::E_file_not_found";
+             }
+#warning FIXME reenable change report
+#if 0
+             report->stop( err );
+#endif
+             throw MediaException(ZYPP_EX_CODELOCATION, err + string(" ") + _curlError);
+            }
+          }
+          break;
+        case CURLE_FTP_COULDNT_RETR_FILE:
+        case CURLE_FTP_ACCESS_DENIED:
+          err = "Error::E_file_not_found";
+          break;
+        case CURLE_BAD_PASSWORD_ENTERED:
+        case CURLE_FTP_USER_PASSWORD_INCORRECT:
+          err = "Error::E_login_failed";
+          break;
+        case CURLE_COULDNT_RESOLVE_PROXY:
+        case CURLE_COULDNT_RESOLVE_HOST:
+        case CURLE_COULDNT_CONNECT:
+        case CURLE_FTP_CANT_GET_HOST:
+          err = "Error::E_connection_failed";
+          break;
+        case CURLE_WRITE_ERROR:
+          err = "Error::E_write_error";
+          break;
+        case CURLE_ABORTED_BY_CALLBACK:
+          err = "Error::E_user_abort";
+          break;
+        case CURLE_SSL_PEER_CERTIFICATE:
+        default:
+          err = "Error::E_error";
+          break;
+      }
+
+#warning FIXME reenable change report
+#if 0
+      report->stop( err );
+#endif
+      throw MediaException(ZYPP_EX_CODELOCATION, err + string(" ") + _curlError);
+    }
+
+    if ( PathInfo::rename( destNew, dest ) != 0 ) {
+      ERR << "Rename failed" << endl;
+#warning FIXME reenable change report
+#if 0
+      report->stop( Error::E_write_error );
+#endif
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_write_error");
+    }
+
+#warning FIXME reenable change report
+#if 0
+    report->stop( Error::E_ok );
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::getDir
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached
+//
+void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
+{
+  PathInfo::dircontent content;
+  try {
+    getDirInfo( content, dirname, /*dots*/false );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+
+  for ( PathInfo::dircontent::const_iterator it = content.begin(); it != content.end(); ++it ) {
+    try {
+      Pathname filename = dirname + it->name;
+      int res = 0;
+
+      switch ( it->type ) {
+      case PathInfo::NOT_AVAIL: // old directory.yast contains no typeinfo at all
+      case PathInfo::T_FILE:
+       getFile( filename );
+       break;
+      case PathInfo::T_DIR: // newer directory.yast contain at least directory info
+       if ( recurse_r ) {
+         getDir( filename, recurse_r );
+       } else {
+         res = PathInfo::assert_dir( localPath( filename ) );
+         if ( res ) {
+           WAR << "Ignore error (" << res <<  ") on creating local directory '" << localPath( filename ) << "'" << endl;
+         }
+       }
+       break;
+      default:
+       // don't provide devices, sockets, etc.
+       break;
+      }
+    }
+    catch (const MediaException & excpt_r)
+    {
+#warning FIXME rethrow
+#if 0
+      ZYPP_RETHROW(excpt_r);
+#endif
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::getDirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached and retlist is empty.
+//
+void MediaCurl::getDirInfo( std::list<std::string> & retlist,
+                              const Pathname & dirname, bool dots ) const
+{
+  try {
+    getDirectoryYast( retlist, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_supported_by_media");
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::getDirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached and retlist is empty.
+//
+void MediaCurl::getDirInfo( PathInfo::dircontent & retlist,
+                              const Pathname & dirname, bool dots ) const
+{
+  try {
+    getDirectoryYast( retlist, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_supported_by_media");
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaCurl::progressCallback
+//     METHOD TYPE : int
+//
+//     DESCRIPTION : Progress callback triggered from MediaCurl::getFile
+//
+int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
+                                 double ultotal, double ulnow )
+{
+#warning FIXME this function
+#if 0
+  DownloadProgressReport::Send * reportP = reinterpret_cast<DownloadProgressReport::Send*>( clientp );
+  if ( reportP ) {
+    ProgressData pd( 0, int(dltotal), int(dlnow) );
+    if ( (*reportP)->progress( pd ) == false )
+      return 1; // abort requested by user
+  }
+
+#warning YOU callbacks still active
+  if ( _callbacks ) {
+    if ( _callbacks->progress( int( dlnow * 100 / dltotal ) ) ) return 0;
+    else return 1;
+  }
+#endif
+  return 0;
+}
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaCurl.h b/zypp/media/MediaCurl.h
new file mode 100644 (file)
index 0000000..242eb3f
--- /dev/null
@@ -0,0 +1,94 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCurl.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIACURL_H
+#define ZYPP_MEDIA_MEDIACURL_H
+
+#include "zypp/media/MediaHandler.h"
+
+#include <curl/curl.h>
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaCurl
+/**
+ * @short Implementation class for FTP, HTTP and HTTPS MediaHandler
+ * @see MediaHandler
+ **/
+class MediaCurl : public MediaHandler {
+
+  protected:
+
+    MEDIA_HANDLER_API;
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void disconnectFrom();
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void getFileCopy( const Pathname & srcFilename, const Pathname & targetFilename) const;
+
+
+  public:
+
+    MediaCurl( const Url &      url_r,
+              const Pathname & attach_point_hint_r );
+
+    virtual ~MediaCurl() { release(); }
+
+    static void setCookieFile( const Pathname & );
+
+    class Callbacks
+    {
+      public:
+       virtual ~Callbacks() {}
+        virtual bool progress( int percent ) = 0;
+    };
+
+    static void setCallbacks( Callbacks *c ) { _callbacks = c; }
+
+  protected:
+
+    static int progressCallback( void *clientp, double dltotal, double dlnow,
+                                 double ultotal, double ulnow );
+
+  private:
+    CURL *_curl;
+    char _curlError[ CURL_ERROR_SIZE ];
+
+    std::string _userpwd;
+    std::string _proxy;
+    std::string _proxyuserpwd;
+    std::string _currentCookieFile;
+
+    static Pathname _cookieFile;
+
+    static Callbacks *_callbacks;
+
+    bool _connected;
+
+    static bool _globalInit;
+};
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIACURL_H
diff --git a/zypp/media/MediaException.h b/zypp/media/MediaException.h
new file mode 100644 (file)
index 0000000..51a5dd6
--- /dev/null
@@ -0,0 +1,46 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaException.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAEXCEPTION_H
+#define ZYPP_MEDIA_MEDIAEXCEPTION_H
+
+#include <iosfwd>
+#include <stdexcept>
+
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  namespace media {
+    ///////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaException
+    /** Just inherits Exception to separate media exceptions
+     *
+     **/
+    class MediaException : public Exception
+    {
+      typedef exception_detail::CodeLocation CodeLocation;
+    public:
+      /** Ctor taking CodeLocation and message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      MediaException( const CodeLocation & where_r, const std::string & msg_r )
+      : Exception( msg_r )
+      {}
+    };
+  
+  /////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MEDIA_MEDIAEXCEPTION_H
diff --git a/zypp/media/MediaHandler.cc b/zypp/media/MediaHandler.cc
new file mode 100644 (file)
index 0000000..6568aa0
--- /dev/null
@@ -0,0 +1,693 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaHandler.cc
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/media/MediaHandler.h"
+#include "zypp/base/stringutil.h"
+
+using namespace std;
+using namespace zypp::base;
+
+// use directory.yast on every media (not just via ftp/http)
+#define NONREMOTE_DIRECTORY_YAST 1
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaHandler
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::MediaHandler
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+MediaHandler::MediaHandler ( const Url &      url_r,
+                            const Pathname & attach_point_r,
+                            const Pathname & urlpath_below_attachpoint_r,
+                            const bool       does_download_r )
+    : _attachPoint( attach_point_r )
+    , _tmp_attachPoint( false )
+    , _does_download( does_download_r )
+    , _isAttached( false )
+    , _url( url_r )
+{
+  if ( _attachPoint.empty() ) {
+    ///////////////////////////////////////////////////////////////////
+    // provide a default attachpoint
+    ///////////////////////////////////////////////////////////////////
+
+    Pathname aroot;
+    PathInfo adir;
+    const char * defmounts[] = { "/var/adm/mount", "/var/tmp", /**/NULL/**/ };
+    for ( const char ** def = defmounts; *def; ++def ) {
+      adir( *def );
+      if ( adir.isDir() && adir.userMayRWX() ) {
+       aroot = adir.path();
+       break;
+      }
+    }
+    if ( aroot.empty() ) {
+      ERR << "Create attach point: Can't find a writable directory to create an attach point" << endl;
+      return;
+    }
+
+    Pathname abase( aroot + "AP_" );
+    Pathname apoint;
+
+    for ( unsigned i = 1; i < 1000; ++i ) {
+      adir( Pathname::extend( abase, stringutil::hexstring( i ) ) );
+      if ( ! adir.isExist() && PathInfo::mkdir( adir.path() ) == 0 ) {
+       apoint = adir.path();
+       break;
+      }
+    }
+
+    if ( apoint.empty() ) {
+      ERR << "Unable to create an attach point below " << aroot << endl;
+      return;
+    }
+
+    // success
+    _attachPoint = apoint;
+    _tmp_attachPoint = true;
+    MIL << "Created default attach point " << _attachPoint << endl;
+
+  } else {
+    ///////////////////////////////////////////////////////////////////
+    // check if provided attachpoint is usable.
+    ///////////////////////////////////////////////////////////////////
+    PathInfo adir( _attachPoint );
+    if ( !adir.isDir() ) {
+      ERR << "Provided attach point is not a directory: " << adir << endl;
+      _attachPoint = Pathname();
+    }
+
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // must init _localRoot after _attachPoint is determined.
+  ///////////////////////////////////////////////////////////////////
+
+  if ( !_attachPoint.empty() ) {
+    _localRoot = _attachPoint + urlpath_below_attachpoint_r;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::~MediaHandler
+//     METHOD TYPE : Destructor
+//
+//     DESCRIPTION :
+//
+MediaHandler::~MediaHandler()
+{
+  if ( _isAttached ) {
+    INT << "MediaHandler deleted with media attached." << endl;
+    return; // no cleanup if media still mounted!
+  }
+
+  if ( _tmp_attachPoint ) {
+    int res = PathInfo::recursive_rmdir( _attachPoint );
+    if ( res == 0 ) {
+      MIL << "Deleted default attach point " << _attachPoint << endl;
+    } else {
+      ERR << "Failed to Delete default attach point " << _attachPoint
+       << " errno(" << res << ")" << endl;
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attach
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::attach( bool next )
+{
+  if ( _isAttached )
+    return;
+
+  if ( _attachPoint.empty() ) {
+    ERR << "Error::E_bad_attachpoint" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_bad_attachpoint");
+  }
+
+  try {
+    attachTo( next ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "Attach failed: " << excpt_r << " " << *this << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  _isAttached = true;
+  MIL << "Attached: " << *this << endl;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::localPath
+//     METHOD TYPE : Pathname
+//
+Pathname MediaHandler::localPath( const Pathname & pathname ) const {
+    if ( _localRoot.empty() )
+        return _localRoot;
+
+    // we must check maximum file name length
+    // this is important for fetching the suseservers, the
+    // url with all parameters can get too long (bug #42021)
+
+    return _localRoot + pathname.absolutename();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::disconnect
+//     METHOD TYPE : PMError
+//
+void MediaHandler::disconnect()
+{
+  if ( !_isAttached )
+    return;
+
+  try {
+    disconnectFrom(); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "Disconnect failed: " << excpt_r << " " << *this << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  MIL << "Disconnected: " << *this << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::release
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::release( bool eject )
+{
+  if ( !_isAttached ) {
+    if ( eject )
+      forceEject();
+    return;
+  }
+
+  try {
+    releaseFrom( eject ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "Release failed: " << excpt_r << " " << *this << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  _isAttached = false;
+  MIL << "Released: " << *this << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::provideFile
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::provideFileCopy( Pathname srcFilename,
+                                       Pathname targetFilename ) const
+{
+  if ( !_isAttached ) {
+    INT << "Error::E_not_attached" << " on provideFileCopy(" << srcFilename 
+        << "," << targetFilename << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_attached");
+  }
+
+  try {
+    getFileCopy( srcFilename, targetFilename ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "provideFileCopy(" << srcFilename << "," << targetFilename << "): " << excpt_r << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  DBG << "provideFileCopy(" << srcFilename << "," << targetFilename  << ")" << endl;
+}
+
+void MediaHandler::provideFile( Pathname filename ) const
+{
+  if ( !_isAttached ) {
+    INT << "Error::E_not_attached" << " on provideFile(" << filename << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_attached");
+  }
+  
+  try {
+    getFile( filename ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "provideFile(" << filename << "): " << excpt_r << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  DBG << "provideFile(" << filename << ")" << endl;
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::provideDir
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::provideDir( Pathname dirname ) const
+{
+  if ( !_isAttached ) {
+    INT << "Error::E_not_attached" << " on provideDir(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_attached");
+  }
+  try {
+    getDir( dirname, /*recursive*/false ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "provideDir(" << dirname << "): " << excpt_r << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  MIL << "provideDir(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::provideDirTree
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::provideDirTree( Pathname dirname ) const
+{
+  if ( !_isAttached ) {
+    INT << "Error::E_not_attached" << " on provideDirTree(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_attached");
+  }
+
+  try {
+    getDir( dirname, /*recursive*/true ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "provideDirTree(" << dirname << "): " << excpt_r << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+
+  MIL << "provideDirTree(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::releasePath
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::releasePath( Pathname pathname ) const
+{
+  if ( ! _does_download || _attachPoint.empty() )
+    return;
+
+  PathInfo info( localPath( pathname ) );
+
+  if ( info.isFile() ) {
+    PathInfo::unlink( info.path() );
+  } else if ( info.isDir() ) {
+    if ( info.path() != _localRoot ) {
+      PathInfo::recursive_rmdir( info.path() );
+    } else {
+      PathInfo::clean_dir( info.path() );
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::dirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::dirInfo( list<string> & retlist,
+                              const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !_isAttached ) {
+    INT << "Error::E_not_attached" << " on dirInfo(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_attached");
+  }
+
+  try {
+    getDirInfo( retlist, dirname, dots ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "dirInfo(" << dirname << "): " << excpt_r << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  MIL << "dirInfo(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::dirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::dirInfo( PathInfo::dircontent & retlist,
+                              const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !_isAttached ) {
+    INT << "Error::E_not_attached" << " on dirInfo(" << dirname << ")" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_attached");
+  }
+
+  try {
+    getDirInfo( retlist, dirname, dots ); // pass to concrete handler
+  }
+  catch (const MediaException & excpt_r)
+  {
+    WAR << "dirInfo(" << dirname << "): " << excpt_r << endl;
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  MIL << "dirInfo(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirectoryYast
+//     METHOD TYPE : PMError
+//
+void MediaHandler::getDirectoryYast( std::list<std::string> & retlist,
+                                       const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  PathInfo::dircontent content;
+  try {
+    getDirectoryYast( content, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+
+  // convert to std::list<std::string>
+  for ( PathInfo::dircontent::const_iterator it = content.begin(); it != content.end(); ++it ) {
+    retlist.push_back( it->name );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirectoryYast
+//     METHOD TYPE : PMError
+//
+void MediaHandler::getDirectoryYast( PathInfo::dircontent & retlist,
+                                       const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  // look for directory.yast
+  Pathname dirFile = dirname + "directory.yast";
+  try {
+    getFile( dirFile );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#warning FIXME rethrow
+#if 0
+    ERR << "provideFile(" << dirFile << "): " << excpt_r << endl;
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  DBG << "provideFile(" << dirFile << "): " << "OK" << endl;
+
+  // using directory.yast
+  ifstream dir( localPath( dirFile ).asString().c_str() );
+  if ( dir.fail() ) {
+    ERR << "Unable to load '" << localPath( dirFile ) << "'" << endl;
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_system");
+  }
+
+  string line;
+  while( getline( dir, line ) ) {
+    if ( line.empty() ) continue;
+    if ( line == "directory.yast" ) continue;
+
+    // Newer directory.yast append '/' to directory names
+    // Remaining entries are unspecified, although most probabely files.
+    PathInfo::file_type type = PathInfo::NOT_AVAIL;
+    if ( *line.rbegin() == '/' ) {
+      line.erase( line.end()-1 );
+      type = PathInfo::T_DIR;
+    }
+
+    if ( dots ) {
+      if ( line == "." || line == ".." ) continue;
+    } else {
+      if ( *line.begin() == '.' ) continue;
+    }
+
+    retlist.push_back( PathInfo::direntry( line, type ) );
+  }
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const MediaHandler & obj )
+{
+  str << obj.url() << ( obj.isAttached() ? "" : " not" )
+    << " attached; localRoot \"" << obj.localRoot() << "\"";
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getFile
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getFile( const Pathname & filename ) const
+{
+    PathInfo info( localPath( filename ) );
+    if( info.isFile() ) {
+        return;
+    }
+
+    if (info.isExist())
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_a_file");
+    else
+      throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_file_not_found");
+}
+
+
+void MediaHandler::getFileCopy ( const Pathname & srcFilename, const Pathname & targetFilename ) const
+{
+  try {
+    getFile(srcFilename);
+  }
+  catch (const MediaException & excpt_r)
+  {
+#warning FIXME rethrow
+#if 0
+    ZYPP_RETHROW(excpt_r);
+#endif
+  }
+  
+  if ( PathInfo::copy( localPath( srcFilename ), targetFilename ) != 0 ) {
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_write_error");
+  }
+}
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDir
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getDir( const Pathname & dirname, bool recurse_r ) const
+{
+  PathInfo info( localPath( dirname ) );
+  if( info.isDir() ) {
+    return;
+  }
+
+  if (info.isExist())
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_a_file");
+  else
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_file_not_found");
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached and retlist is empty.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots ) const
+{
+  PathInfo info( localPath( dirname ) );
+  if( ! info.isDir() ) {
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_a_directory");
+  }
+
+#if NONREMOTE_DIRECTORY_YAST
+  // use directory.yast if available
+  try {
+    getDirectoryYast( retlist, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#endif
+
+  // readdir
+  int res = PathInfo::readdir( retlist, info.path(), dots );
+  if ( res )
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_system");
+
+#if NONREMOTE_DIRECTORY_YAST
+  }
+#endif
+
+  return;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached and retlist is empty.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getDirInfo( PathInfo::dircontent & retlist,
+                                 const Pathname & dirname, bool dots ) const
+{
+  PathInfo info( localPath( dirname ) );
+  if( ! info.isDir() ) {
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_not_a_directory");
+  }
+
+#if NONREMOTE_DIRECTORY_YAST
+  // use directory.yast if available
+  try {
+    getDirectoryYast( retlist, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#endif
+
+  // readdir
+  int res = PathInfo::readdir( retlist, info.path(), dots );
+  if ( res )
+    throw MediaException(ZYPP_EX_CODELOCATION, "Error::E_system");
+#if NONREMOTE_DIRECTORY_YAST
+  }
+#endif
+}
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaHandler.h b/zypp/media/MediaHandler.h
new file mode 100644 (file)
index 0000000..438cee8
--- /dev/null
@@ -0,0 +1,487 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaHandler.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAHANDLERL_H
+#define ZYPP_MEDIA_MEDIAHANDLERL_H
+
+#include <iosfwd>
+#include <string>
+#include <list>
+
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+#warning FIXME use real Url class
+// #include "zypp/@Review/Url.h"
+typedef std::string Url;
+
+#include "zypp/media/MediaAccess.h"
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaHandler
+/**
+ * @short Abstract base class for 'physical' MediaHandler like MediaCD, etc.
+ *
+ * Handles the requests forwarded by @ref MediaAccess. The public interface
+ * contains nonvirtual methods, which should do common sanitychecks and
+ * logging. For the real action they call virtual methods overloaded by the
+ * concrete handler.
+ **/
+class MediaHandler {
+    friend std::ostream & operator<<( std::ostream & str, const MediaHandler & obj );
+
+    public:
+       typedef shared_ptr<MediaHandler> Ptr;
+       typedef shared_ptr<const MediaHandler> constPtr;
+
+    private:
+
+       /**
+        * this is where the media will be actually "mounted"
+        * all files are provided 'below' this directory.
+        **/
+       Pathname _attachPoint;
+
+       /**
+        * If a default attach point was created, it has to be
+        * removed on destuction.
+        **/
+       bool _tmp_attachPoint;
+
+       /**
+        * The local directory that corresponds to the media url.
+        * With NFS it's the '_attachPoint', as the directory on the
+        * server is mounted. With CD/DVD it's 'attach point+_url.path()'
+        * because the CDs root directory is mounted. And with CIFS
+        * it's '_url.path() without the shares name'.
+        **/
+       Pathname _localRoot;
+
+       /**
+        * True if concrete handler downloads files to the local
+        * filesystem. If true releaseFile/Dir will delete them.
+        **/
+       bool _does_download;
+
+        /**
+        * True, if media is attached.
+        **/
+       bool _isAttached;
+
+    protected:
+
+        /**
+        * Url to handle
+        **/
+       const Url _url;
+
+        /**
+        * Attachpoint to use
+        **/
+       const Pathname & attachPoint() const { return _attachPoint; }
+
+    protected:
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        // Real action interface to be overloaded by concrete handler.
+        //
+        ///////////////////////////////////////////////////////////////////
+
+       /**
+        * Call concrete handler to attach the media.
+        *
+        * Asserted that not already attached, and attachPoint is a directory.
+        *
+        * @param next try next available device in turn until end of device
+        * list is reached (for media which are accessible through multiple
+        * devices like cdroms).
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void attachTo(bool next = false) = 0;
+
+        /**
+         * Call concrete handler to disconnect media.
+        *
+        * Asserted that media is attached.
+        *
+         * This is useful for media which e.g. holds open a connection to a
+         * server like FTP. After calling disconnect() the media object still is
+         * valid and files are present.
+        *
+         * After calling disconnect() it's not possible to call provideFile() or
+         * provideDir() anymore.
+        *
+        * \throws MediaException
+        *
+        **/
+        virtual void disconnectFrom() { return; }
+
+       /**
+        * Call concrete handler to release the media.
+        * If eject is true, physically eject the media (i.e. CD-ROM).
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void releaseFrom( bool eject ) = 0;
+
+       /**
+        * Call concrete handler to physically eject the media (i.e. CD-ROM)
+        * in case the media is not attached..
+        *
+        * Asserted that media is not attached.
+        **/
+       virtual void forceEject() {}
+
+       /**
+        * Call concrete handler to provide file below attach point.
+        *
+        * Default implementation provided, that returns whether a file
+        * is located at '_localRoot + filename'.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void getFile( const Pathname & filename ) const = 0;
+
+        /**
+         * Call concrete handler to provide a file under a different place 
+         * in the file system (usually not under attach point) as a copy.
+         * Media must be attached before by callee.
+         *
+         * Default implementation provided that calls getFile(srcFilename)
+         * and copies the result around.
+        *
+        * \throws MediaException
+        *
+         **/
+        virtual void getFileCopy( const Pathname & srcFilename, const Pathname & targetFilename ) const;
+                         
+
+       /**
+        * Call concrete handler to provide directory content (not recursive!)
+        * below attach point.
+        *
+        * Return E_not_supported_by_media if media does not support retrieval of
+        * directory content.
+        *
+        * Default implementation provided, that returns whether a directory
+        * is located at '_localRoot + dirname'.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const = 0;
+
+       /**
+        * Call concrete handler to provide a content list of directory on media
+        * via retlist. If dots is false entries starting with '.' are not reported.
+        *
+        * Return E_not_supported_by_media if media does not support retrieval of
+        * directory content.
+        *
+        * Default implementation provided, that returns the content of a
+        * directory at '_localRoot + dirnname' retrieved via 'readdir'.
+        *
+        * Asserted that media is attached and retlist is empty.
+        *
+        * \throws MediaException
+        *
+        **/
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                   const Pathname & dirname, bool dots = true ) const = 0;
+
+       /**
+        * Basically the same as getDirInfo above. The content list is returned as
+        * PathInfo::dircontent, which includes name and filetype of each directory
+        * entry. Retrieving the filetype usg. requires an additional ::stat call for
+        * each entry, thus it's more expensive than a simple readdir.
+        *
+        * Asserted that media is attached and retlist is empty.
+        *
+        * \throws MediaException
+        *
+        **/
+        virtual void getDirInfo( PathInfo::dircontent & retlist,
+                                   const Pathname & dirname, bool dots = true ) const = 0;
+
+  protected:
+
+        /**
+        * Retrieve and if available scan dirname/directory.yast.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+        void getDirectoryYast( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+
+        /**
+        * Retrieve and if available scan dirname/directory.yast.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+        void getDirectoryYast( PathInfo::dircontent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+
+  public:
+
+       /**
+        * If the concrete media handler provides a nonempty
+        * attach_point, it must be an existing directory.
+        *
+        * On an empty attach_point, MediaHandler will create
+        * a temporay directory, which will be erased from
+        * destructor.
+        *
+        * On any error, the attach_point is set to an empty Pathname,
+        * which should lead to E_bad_attachpoint.
+        **/
+       MediaHandler ( const Url&       url_r,
+                      const Pathname & attach_point_r,
+                      const Pathname & urlpath_below_attachpoint_r,
+                      const bool       does_download_r );
+
+       /**
+        * Contolling MediaAccess takes care, that attached media is released
+        * prior to deleting this.
+        **/
+       virtual ~MediaHandler();
+
+    public:
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        // MediaAccess interface. Does common checks and logging.
+        // Invokes real action if necessary.
+        //
+        ///////////////////////////////////////////////////////////////////
+
+        /**
+        * Protocol hint for MediaAccess.
+        **/
+#warning FIXME uncomment once real Url class is implemented
+#if 0
+        Url::Protocol protocol() const { return _url.protocol(); }
+#endif
+
+       /**
+        * Url used.
+        **/
+        Url url() const { return _url; }
+
+       /**
+        * Use concrete handler to attach the media.
+        *
+        * @param next try next available device in turn until end of device
+        * list is reached (for media which are accessible through multiple
+        * devices like cdroms).
+        *
+        * \throws MediaException
+        *
+        **/
+       void attach(bool next);
+
+       /**
+        * True if media is attached.
+        **/
+       bool isAttached() const { return _isAttached; }
+
+       /**
+        * Return the local directory that corresponds to medias url,
+        * no matter if media isAttached or not. Files requested will
+        * be available at 'localRoot() + filename' or better
+        * 'localPath( filename )'.
+        *
+        * Returns empty pathname if E_bad_attachpoint
+        **/
+       const Pathname & localRoot() const { return _localRoot; }
+
+       /**
+        * Files provided will be available at 'localPath(filename)'.
+        *
+        * Returns empty pathname if E_bad_attachpoint
+        **/
+         Pathname localPath( const Pathname & pathname ) const;
+
+        /**
+        * Use concrete handler to isconnect media.
+        *
+        * This is useful for media which e.g. holds open a connection to a
+        * server like FTP. After calling disconnect() the media object still is
+        * valid and files are present.
+        *
+        * After calling disconnect() it's not possible to call provideFile() or
+        * provideDir() anymore.
+        *
+        * \throws MediaException
+        *
+        **/
+        void disconnect();
+
+       /**
+        * Use concrete handler to release the media.
+        * @param eject if true, physically eject the media * (i.e. CD-ROM)
+        *
+        * \throws MediaException
+        *
+        **/
+       void release( bool eject = false );
+
+       /**
+        * Use concrete handler to provide file denoted by path below
+        * 'localRoot'. Filename is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideFile( Pathname filename ) const;
+    
+       /**
+        * Call concrete handler to provide a copy of a file under a different place 
+         * in the file system (usually not under attach point) as a copy.
+         * Media must be attached before by callee.
+         *
+         * @param srcFilename    Filename of source file on the media
+         * @param targetFilename Filename for the target in the file system
+        *
+        * \throws MediaException
+        *
+        **/
+        void provideFileCopy( Pathname srcFilename, Pathname targetFilename) const;
+    
+       /**
+        * Use concrete handler to provide directory denoted
+        * by path below 'localRoot' (not recursive!).
+        * dirname is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDir( Pathname dirname ) const;
+
+       /**
+        * Use concrete handler to provide directory tree denoted
+        * by path below 'localRoot' (recursive!!).
+        * dirname is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDirTree( Pathname dirname ) const;
+
+       /**
+        * Remove filename below localRoot IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseFile( const Pathname & filename ) const { return releasePath( filename ); }
+
+       /**
+        * Remove directory tree below localRoot IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseDir( const Pathname & dirname ) const { return releasePath( dirname ); }
+
+       /**
+        * Remove pathname below localRoot IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * If pathname denotes a directory it is recursively removed.
+        * If pathname is empty or '/' everything below the localRoot
+        * is recursively removed.
+        * If pathname denotes a file it is unlinked.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releasePath( Pathname pathname ) const;
+
+    public:
+
+       /**
+        * Return content of directory on media via retlist. If dots is false
+        * entries starting with '.' are not reported.
+        *
+        * The request is forwarded to the concrete handler,
+        * which may atempt to retieve the content e.g. via 'readdir'
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+        void dirInfo( std::list<std::string> & retlist,
+                        const Pathname & dirname, bool dots = true ) const;
+
+       /**
+        * Basically the same as dirInfo above. The content is returned as
+        * PathInfo::dircontent, which includes name and filetype of each directory
+        * entry. Retrieving the filetype usg. requires an additional ::stat call for
+        * each entry, thus it's more expensive than a simple readdir.
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void dirInfo( PathInfo::dircontent & retlist,
+                        const Pathname & dirname, bool dots = true ) const;
+};
+
+///////////////////////////////////////////////////////////////////
+
+#define        MEDIA_HANDLER_API                                               \
+    protected:                                                         \
+       virtual void attachTo (bool next = false);                      \
+       virtual void releaseFrom( bool eject );                 \
+       virtual void getFile( const Pathname & filename ) const;        \
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const;  \
+        virtual void getDirInfo( std::list<std::string> & retlist,     \
+                                   const Pathname & dirname, bool dots = true ) const; \
+        virtual void getDirInfo( PathInfo::dircontent & retlist,       \
+                                   const Pathname & dirname, bool dots = true ) const;
+
+  } // namespace media
+} // namespace zypp
+
+
+#endif // ZYPP_MEDIA_MEDIAHANDLERL_H
+
+