1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaCurl.cc
15 #include "zypp/base/Logger.h"
16 #include "zypp/ExternalProgram.h"
17 #include "zypp/base/String.h"
18 #include "zypp/base/Sysconfig.h"
20 #include "zypp/media/MediaCurl.h"
21 #include "zypp/media/proxyinfo/ProxyInfos.h"
22 #include "zypp/media/ProxyInfo.h"
23 #include "zypp/thread/Once.h"
25 #include <sys/types.h>
27 #include <sys/mount.h>
34 #define DETECT_DIR_INDEX 0
37 using namespace zypp::base;
41 zypp::thread::OnceFlag g_InitOnceFlag = PTHREAD_ONCE_INIT;
42 zypp::thread::OnceFlag g_FreeOnceFlag = PTHREAD_ONCE_INIT;
44 extern "C" void _do_free_once()
46 curl_global_cleanup();
49 extern "C" void globalFreeOnce()
51 zypp::thread::callOnce(g_FreeOnceFlag, _do_free_once);
54 extern "C" void _do_init_once()
56 CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
59 WAR << "curl global init failed" << endl;
63 // register at exit handler ?
64 // this may cause trouble, because we can protect it
65 // against ourself only.
66 // if the app sets an atexit handler as well, it will
67 // cause a double free while the second of them runs.
69 //std::atexit( globalFreeOnce);
72 inline void globalInitOnce()
74 zypp::thread::callOnce(g_InitOnceFlag, _do_init_once);
81 Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
82 std::string MediaCurl::_agent = "Novell ZYPP Installer";
84 ///////////////////////////////////////////////////////////////////
86 static inline void escape( string & str_r,
87 const char char_r, const string & escaped_r ) {
88 for ( string::size_type pos = str_r.find( char_r );
89 pos != string::npos; pos = str_r.find( char_r, pos ) ) {
90 str_r.replace( pos, 1, escaped_r );
94 static inline string escapedPath( string path_r ) {
95 escape( path_r, ' ', "%20" );
99 static inline string unEscape( string text_r ) {
100 char * tmp = curl_unescape( text_r.c_str(), 0 );
106 ///////////////////////////////////////////////////////////////////
108 // CLASS NAME : MediaCurl
110 ///////////////////////////////////////////////////////////////////
112 MediaCurl::MediaCurl( const Url & url_r,
113 const Pathname & attach_point_hint_r )
114 : MediaHandler( url_r, attach_point_hint_r,
115 "/", // urlpath at attachpoint
116 true ), // does_download
119 _curlError[0] = '\0';
121 MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
125 if( !attachPoint().empty())
127 PathInfo ainfo(attachPoint());
128 Pathname apath(attachPoint() + "XXXXXX");
129 char *atemp = ::strdup( apath.asString().c_str());
131 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
132 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
134 WAR << "attach point " << ainfo.path()
135 << " is not useable for " << url_r.getScheme() << endl;
136 setAttachPoint("", true);
138 else if( atest != NULL)
146 void MediaCurl::setCookieFile( const Pathname &fileName )
148 _cookieFile = fileName;
151 ///////////////////////////////////////////////////////////////////
154 // METHOD NAME : MediaCurl::attachTo
155 // METHOD TYPE : PMError
157 // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
159 void MediaCurl::attachTo (bool next)
162 ZYPP_THROW(MediaNotSupportedException(_url));
164 if ( !_url.isValid() )
165 ZYPP_THROW(MediaBadUrlException(_url));
167 curl_version_info_data *curl_info = NULL;
168 curl_info = curl_version_info(CURLVERSION_NOW);
169 // curl_info does not need any free (is static)
170 if (curl_info->protocols)
172 const char * const *proto;
173 std::string scheme( _url.getScheme());
175 for(proto=curl_info->protocols; !found && *proto; ++proto)
177 if( scheme == std::string((const char *)*proto))
182 std::string msg("Unsupported protocol '");
185 ZYPP_THROW(MediaBadUrlException(_url, msg));
189 if( !isUseableAttachPoint(attachPoint()))
191 std::string mountpoint = createAttachPoint().asString();
193 if( mountpoint.empty())
194 ZYPP_THROW( MediaBadAttachPointException(url()));
196 setAttachPoint( mountpoint, true);
199 disconnectFrom(); // clean _curl if needed
200 _curl = curl_easy_init();
202 ZYPP_THROW(MediaCurlInitException(_url));
205 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
208 ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
211 ret = curl_easy_setopt( _curl, CURLOPT_FAILONERROR, true );
214 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
217 ret = curl_easy_setopt( _curl, CURLOPT_NOSIGNAL, 1 );
220 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
224 ** Don't block "forever" on system calls. Curl seems to
225 ** recover nicely, if the ftp server has e.g. a 30sec
226 ** timeout. If required, it closes the connection, trys
227 ** to reopen and fetch it - this works in many cases
228 ** without to report any error to us.
230 ** Disabled, because it breaks normal operations over a
233 ret = curl_easy_setopt( _curl, CURLOPT_TIMEOUT, 600 );
236 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
243 ret = curl_easy_setopt( _curl, CURLOPT_CONNECTTIMEOUT, 60);
246 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
249 if ( _url.getScheme() == "http" ) {
250 // follow any Location: header that the server sends as part of
251 // an HTTP header (#113275)
252 ret = curl_easy_setopt ( _curl, CURLOPT_FOLLOWLOCATION, true );
255 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
257 ret = curl_easy_setopt ( _curl, CURLOPT_MAXREDIRS, 3L );
260 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
262 ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, _agent.c_str() );
265 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
269 if ( _url.getScheme() == "https" ) {
270 ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYPEER, 1 );
273 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
275 ret = curl_easy_setopt( _curl, CURLOPT_CAPATH, "/etc/ssl/certs/" );
278 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
280 ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYHOST, 2 );
283 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
285 ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, _agent.c_str() );
288 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
293 /*---------------------------------------------------------------*
294 CURLOPT_USERPWD: [user name]:[password]
296 Url::username/password -> CURLOPT_USERPWD
297 If not provided, anonymous FTP identification
298 *---------------------------------------------------------------*/
300 if ( _url.getUsername().empty() ) {
301 if ( _url.getScheme() == "ftp" ) {
302 string id = "yast2@";
304 DBG << "Anonymous FTP identification: '" << id << "'" << endl;
305 _userpwd = "anonymous:" + id;
308 _userpwd = _url.getUsername();
309 if ( _url.getPassword().size() ) {
310 _userpwd += ":" + _url.getPassword();
314 if ( _userpwd.size() ) {
315 _userpwd = unEscape( _userpwd );
316 ret = curl_easy_setopt( _curl, CURLOPT_USERPWD, _userpwd.c_str() );
319 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
322 if( !_url.getQueryParam("auth").empty() &&
323 (_url.getScheme() == "http" || _url.getScheme() == "https"))
325 std::vector<std::string> list;
326 std::vector<std::string>::const_iterator it;
327 str::split(_url.getQueryParam("auth"), std::back_inserter(list), ",");
329 long auth = CURLAUTH_NONE;
330 for(it = list.begin(); it != list.end(); ++it)
334 auth |= CURLAUTH_BASIC;
339 auth |= CURLAUTH_DIGEST;
342 if((curl_info && (curl_info->features & CURL_VERSION_NTLM)) &&
345 auth |= CURLAUTH_NTLM;
348 if((curl_info && (curl_info->features & CURL_VERSION_SPNEGO)) &&
349 (*it == "spnego" || *it == "negotiate"))
351 // there is no separate spnego flag for auth
352 auth |= CURLAUTH_GSSNEGOTIATE;
355 if((curl_info && (curl_info->features & CURL_VERSION_GSSNEGOTIATE)) &&
356 (*it == "gssnego" || *it == "negotiate"))
358 auth |= CURLAUTH_GSSNEGOTIATE;
362 std::string msg("Unsupported HTTP authentication method '");
366 ZYPP_THROW(MediaBadUrlException(_url, msg));
370 if( auth != CURLAUTH_NONE)
372 DBG << "Enabling HTTP authentication methods: "
373 << _url.getQueryParam("auth") << std::endl;
375 ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
378 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
384 /*---------------------------------------------------------------*
385 CURLOPT_PROXY: host[:port]
387 Url::option(proxy and proxyport) -> CURLOPT_PROXY
388 If not provided, /etc/sysconfig/proxy is evaluated
389 *---------------------------------------------------------------*/
391 _proxy = _url.getQueryParam( "proxy" );
393 if ( ! _proxy.empty() ) {
394 string proxyport( _url.getQueryParam( "proxyport" ) );
395 if ( ! proxyport.empty() ) {
396 _proxy += ":" + proxyport;
400 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
402 if ( proxy_info.enabled())
404 bool useproxy = true;
406 std::list<std::string> nope = proxy_info.noProxy();
407 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
408 it != proxy_info.noProxyEnd();
411 std::string host( str::toLower(_url.getHost()));
412 std::string temp( str::toLower(*it));
414 // no proxy if it points to a suffix
415 // preceeded by a '.', that maches
416 // the trailing portion of the host.
417 if( temp.size() > 1 && temp.at(0) == '.')
419 if(host.size() > temp.size() &&
420 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
422 DBG << "NO_PROXY: '" << *it << "' matches host '"
423 << host << "'" << endl;
429 // no proxy if we have an exact match
432 DBG << "NO_PROXY: '" << *it << "' matches host '"
433 << host << "'" << endl;
440 _proxy = proxy_info.proxy(_url.getScheme());
446 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
448 if ( ! _proxy.empty() ) {
450 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
453 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
456 /*---------------------------------------------------------------*
457 CURLOPT_PROXYUSERPWD: [user name]:[password]
459 Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
460 If not provided, $HOME/.curlrc is evaluated
461 *---------------------------------------------------------------*/
463 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
465 if ( ! _proxyuserpwd.empty() ) {
467 string proxypassword( _url.getQueryParam( "proxypassword" ) );
468 if ( ! proxypassword.empty() ) {
469 _proxyuserpwd += ":" + proxypassword;
473 char *home = getenv("HOME");
476 Pathname curlrcFile = string( home ) + string( "/.curlrc" );
478 PathInfo h_info(string(home), PathInfo::LSTAT);
479 PathInfo c_info(curlrcFile, PathInfo::LSTAT);
481 if( h_info.isDir() && h_info.owner() == getuid() &&
482 c_info.isFile() && c_info.owner() == getuid())
484 map<string,string> rc_data = base::sysconfig::read( curlrcFile );
486 map<string,string>::const_iterator it = rc_data.find("proxy-user");
487 if (it != rc_data.end())
488 _proxyuserpwd = it->second;
492 WAR << "Not allowed to parse '" << curlrcFile
493 << "': bad file owner" << std::endl;
498 _proxyuserpwd = unEscape( _proxyuserpwd );
499 ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
502 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
506 /*---------------------------------------------------------------*
507 *---------------------------------------------------------------*/
509 _currentCookieFile = _cookieFile.asString();
511 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
512 _currentCookieFile.c_str() );
515 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
518 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
519 _currentCookieFile.c_str() );
522 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
525 ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
529 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
532 ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
535 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
538 // FIXME: need a derived class to propelly compare url's
539 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
540 setMediaSource(media);
544 MediaCurl::checkAttachPoint(const Pathname &apoint) const
546 return MediaHandler::checkAttachPoint( apoint, true, true);
549 ///////////////////////////////////////////////////////////////////
552 // METHOD NAME : MediaCurl::disconnectFrom
553 // METHOD TYPE : PMError
555 void MediaCurl::disconnectFrom()
559 curl_easy_cleanup( _curl );
564 ///////////////////////////////////////////////////////////////////
567 // METHOD NAME : MediaCurl::releaseFrom
568 // METHOD TYPE : PMError
570 // DESCRIPTION : Asserted that media is attached.
572 void MediaCurl::releaseFrom( bool eject )
578 ///////////////////////////////////////////////////////////////////
580 // METHOD NAME : MediaCurl::getFile
581 // METHOD TYPE : PMError
584 void MediaCurl::getFile( const Pathname & filename ) const
586 // Use absolute file name to prevent access of files outside of the
587 // hierarchy below the attach point.
588 getFileCopy(filename, localPath(filename).absolutename());
592 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
594 callback::SendReport<DownloadProgressReport> report;
599 doGetFileCopy(filename, target, report);
601 catch (MediaException & excpt_r)
603 // FIXME: this will not match the first URL
604 // FIXME: error number fix
605 report->finish(url, zypp::media::DownloadProgressReport::NOT_FOUND, excpt_r.msg());
606 ZYPP_RETHROW(excpt_r);
608 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
611 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
613 DBG << filename.asString() << endl;
616 ZYPP_THROW(MediaBadUrlException(_url));
618 if(_url.getHost().empty())
619 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
621 string path = _url.getPathName();
622 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
623 filename.absolute() ) {
624 // If url has a path with trailing slash, remove the leading slash from
625 // the absolute file name
626 path += filename.asString().substr( 1, filename.asString().size() - 1 );
627 } else if ( filename.relative() ) {
628 // Add trailing slash to path, if not already there
629 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
630 // Remove "./" from begin of relative file name
631 path += filename.asString().substr( 2, filename.asString().size() - 2 );
633 path += filename.asString();
637 url.setPathName( path );
639 Pathname dest = target.absolutename();
640 if( assert_dir( dest.dirname() ) )
642 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
643 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
646 DBG << "URL: " << url.asString() << endl;
647 // Use URL without options and without username and passwd
648 // (some proxies dislike them in the URL).
649 // Curl seems to need the just scheme, hostname and a path;
650 // the rest was already passed as curl options (in attachTo).
653 // Use asString + url::ViewOptions instead?
654 curlUrl.setUsername( "" );
655 curlUrl.setPassword( "" );
656 curlUrl.setPathParams( "" );
657 curlUrl.setQueryString( "" );
658 curlUrl.setFragment( "" );
661 // See also Bug #154197 and ftp url definition in RFC 1738:
662 // The url "ftp://user@host/foo/bar/file" contains a path,
663 // that is relative to the user's home.
664 // The url "ftp://user@host//foo/bar/file" (or also with
665 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
666 // contains an absolute path.
668 string urlBuffer( curlUrl.asString());
669 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
672 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
675 string destNew = target.asString() + ".new.zypp.XXXXXX";
676 char *buf = ::strdup( destNew.c_str());
679 ERR << "out of memory for temp file name" << endl;
680 ZYPP_THROW(MediaSystemException(
681 url, "out of memory for temp file name"
685 int tmp_fd = ::mkstemp( buf );
689 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
690 ZYPP_THROW(MediaWriteException(destNew));
695 FILE *file = ::fdopen( tmp_fd, "w" );
698 filesystem::unlink( destNew );
699 ERR << "fopen failed for file '" << destNew << "'" << endl;
700 ZYPP_THROW(MediaWriteException(destNew));
703 DBG << "dest: " << dest << endl;
704 DBG << "temp: " << destNew << endl;
706 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
709 filesystem::unlink( destNew );
710 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
713 // Set callback and perform.
714 report->start(url, dest);
715 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &report ) != 0 ) {
716 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
719 ret = curl_easy_perform( _curl );
721 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
722 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
727 filesystem::unlink( destNew );
728 ERR << "curl error: " << ret << ": " << _curlError << endl;
731 bool err_file_not_found = false;
733 case CURLE_UNSUPPORTED_PROTOCOL:
734 case CURLE_URL_MALFORMAT:
735 case CURLE_URL_MALFORMAT_USER:
737 case CURLE_HTTP_RETURNED_ERROR:
739 long httpReturnCode = 0;
740 CURLcode infoRet = curl_easy_getinfo( _curl,
741 CURLINFO_RESPONSE_CODE,
743 if ( infoRet == CURLE_OK ) {
744 string msg = "HTTP response: " +
745 str::numstring( httpReturnCode );
746 if ( httpReturnCode == 401 )
748 err = " Login failed";
751 if ( httpReturnCode == 404)
753 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
757 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
758 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
762 string msg = "Unable to retrieve HTTP response:";
764 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
765 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
769 case CURLE_FTP_COULDNT_RETR_FILE:
770 case CURLE_FTP_ACCESS_DENIED:
771 err = "File not found";
772 err_file_not_found = true;
774 case CURLE_BAD_PASSWORD_ENTERED:
775 case CURLE_FTP_USER_PASSWORD_INCORRECT:
776 err = "Login failed";
778 case CURLE_COULDNT_RESOLVE_PROXY:
779 case CURLE_COULDNT_RESOLVE_HOST:
780 case CURLE_COULDNT_CONNECT:
781 case CURLE_FTP_CANT_GET_HOST:
782 err = "Connection failed";
784 case CURLE_WRITE_ERROR:
787 case CURLE_ABORTED_BY_CALLBACK:
790 case CURLE_SSL_PEER_CERTIFICATE:
792 err = "Unrecognized error";
795 if( err_file_not_found)
797 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
801 ZYPP_THROW(MediaCurlException(url, err, _curlError));
804 catch (const MediaException & excpt_r)
806 ZYPP_RETHROW(excpt_r);
811 if(curlUrl.getScheme() == "http" ||
812 curlUrl.getScheme() == "https")
815 // try to check the effective url and set the not_a_file flag
816 // if the url path ends with a "/", what usually means, that
817 // we've received a directory index (index.html content).
819 // Note: This may be dangerous and break file retrieving in
820 // case of some server redirections ... ?
822 bool not_a_file = false;
824 CURLcode ret = curl_easy_getinfo( _curl,
825 CURLINFO_EFFECTIVE_URL,
827 if ( ret == CURLE_OK && ptr != NULL)
832 std::string path( eurl.getPathName());
833 if( !path.empty() && path != "/" && *path.rbegin() == '/')
835 DBG << "Effective url ("
837 << ") seems to provide the index of a directory"
849 filesystem::unlink( destNew );
850 ZYPP_THROW(MediaNotAFileException(_url, filename));
853 #endif // DETECT_DIR_INDEX
856 // getumask() would be fine, but does not exist
857 // [ the linker can't find it in glibc :-( ].
858 mask = ::umask(0022); ::umask(mask);
859 if ( ::fchmod( ::fileno(file), 0644 & ~mask))
861 ERR << "Failed to chmod file " << destNew << endl;
865 if ( rename( destNew, dest ) != 0 ) {
866 ERR << "Rename failed" << endl;
867 ZYPP_THROW(MediaWriteException(dest));
872 ///////////////////////////////////////////////////////////////////
875 // METHOD NAME : MediaCurl::getDir
876 // METHOD TYPE : PMError
878 // DESCRIPTION : Asserted that media is attached
880 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
882 filesystem::DirContent content;
883 getDirInfo( content, dirname, /*dots*/false );
885 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
886 Pathname filename = dirname + it->name;
889 switch ( it->type ) {
890 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
891 case filesystem::FT_FILE:
894 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
896 getDir( filename, recurse_r );
898 res = assert_dir( localPath( filename ) );
900 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
905 // don't provide devices, sockets, etc.
911 ///////////////////////////////////////////////////////////////////
914 // METHOD NAME : MediaCurl::getDirInfo
915 // METHOD TYPE : PMError
917 // DESCRIPTION : Asserted that media is attached and retlist is empty.
919 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
920 const Pathname & dirname, bool dots ) const
922 getDirectoryYast( retlist, dirname, dots );
925 ///////////////////////////////////////////////////////////////////
928 // METHOD NAME : MediaCurl::getDirInfo
929 // METHOD TYPE : PMError
931 // DESCRIPTION : Asserted that media is attached and retlist is empty.
933 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
934 const Pathname & dirname, bool dots ) const
936 getDirectoryYast( retlist, dirname, dots );
939 ///////////////////////////////////////////////////////////////////
942 // METHOD NAME : MediaCurl::progressCallback
945 // DESCRIPTION : Progress callback triggered from MediaCurl::getFile
947 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
948 double ultotal, double ulnow )
950 callback::SendReport<DownloadProgressReport> *report
951 = reinterpret_cast<callback::SendReport<DownloadProgressReport>*>( clientp );
955 if (! (*report)->progress(int( dlnow * 100 / dltotal ), Url() ))