libzypp 17.31.4
MediaCurl.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
13#include <iostream>
14#include <list>
15
16#include <zypp/base/Logger.h>
17#include <zypp/ExternalProgram.h>
18#include <zypp/base/String.h>
19#include <zypp/base/Gettext.h>
20#include <zypp-core/parser/Sysconfig>
21#include <zypp/base/Gettext.h>
22
24#include <zypp-curl/ProxyInfo>
25#include <zypp-curl/auth/CurlAuthData>
26#include <zypp-media/auth/CredentialManager>
27#include <zypp-curl/CurlConfig>
28#include <zypp-curl/private/curlhelper_p.h>
29#include <zypp/Target.h>
30#include <zypp/ZYppFactory.h>
31#include <zypp/ZConfig.h>
32
33#include <cstdlib>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <sys/mount.h>
37#include <errno.h>
38#include <dirent.h>
39#include <unistd.h>
40
41using std::endl;
42
43namespace internal {
44 using namespace zypp;
46 struct OptionalDownloadProgressReport : public callback::ReceiveReport<media::DownloadProgressReport>
47 {
48 OptionalDownloadProgressReport( bool isOptional=false )
49 : _oldRec { Distributor::instance().getReceiver() }
50 , _isOptional { isOptional }
51 { connect(); }
52
55
56 void reportbegin() override
57 { if ( _oldRec ) _oldRec->reportbegin(); }
58
59 void reportend() override
60 { if ( _oldRec ) _oldRec->reportend(); }
61
62 void report( const UserData & userData_r = UserData() ) override
63 { if ( _oldRec ) _oldRec->report( userData_r ); }
64
65
66 void start( const Url & file_r, Pathname localfile_r ) override
67 {
68 if ( not _oldRec ) return;
69 if ( _isOptional ) {
70 // delay start until first data are received.
71 _startFile = file_r;
72 _startLocalfile = std::move(localfile_r);
73 return;
74 }
75 _oldRec->start( file_r, localfile_r );
76 }
77
78 bool progress( int value_r, const Url & file_r, double dbps_avg_r = -1, double dbps_current_r = -1 ) override
79 {
80 if ( not _oldRec ) return true;
81 if ( notStarted() ) {
82 if ( not ( value_r || dbps_avg_r || dbps_current_r ) )
83 return true;
84 sendStart();
85 }
86 return _oldRec->progress( value_r, file_r, dbps_avg_r, dbps_current_r );
87 }
88
89 Action problem( const Url & file_r, Error error_r, const std::string & description_r ) override
90 {
91 if ( not _oldRec || notStarted() ) return ABORT;
92 return _oldRec->problem( file_r, error_r, description_r );
93 }
94
95 void finish( const Url & file_r, Error error_r, const std::string & reason_r ) override
96 {
97 if ( not _oldRec || notStarted() ) return;
98 _oldRec->finish( file_r, error_r, reason_r );
99 }
100
101 private:
102 // _isOptional also indicates the delayed start
103 bool notStarted() const
104 { return _isOptional; }
105
107 {
108 if ( _isOptional ) {
109 // we know _oldRec is valid...
110 _oldRec->start( std::move(_startFile), std::move(_startLocalfile) );
111 _isOptional = false;
112 }
113 }
114
115 private:
120 };
121
123 {
124 ProgressData( CURL *_curl, time_t _timeout = 0, const zypp::Url & _url = zypp::Url(),
125 zypp::ByteCount expectedFileSize_r = 0,
127
128 CURL *curl;
130 time_t timeout;
135
136 time_t _timeStart = 0;
137 time_t _timeLast = 0;
138 time_t _timeRcv = 0;
139 time_t _timeNow = 0;
140
141 double _dnlTotal = 0.0;
142 double _dnlLast = 0.0;
143 double _dnlNow = 0.0;
144
145 int _dnlPercent= 0;
146
147 double _drateTotal= 0.0;
148 double _drateLast = 0.0;
149
150 void updateStats( double dltotal = 0.0, double dlnow = 0.0 );
151
152 int reportProgress() const;
153
154
155 // download rate of the last period (cca 1 sec)
157 // bytes downloaded at the start of the last period
159 // seconds from the start of the download
160 long secs;
161 // average download rate
162 double drate_avg;
163 // last time the progress was reported
164 time_t ltime;
165 // bytes downloaded at the moment the progress was last reported
166 double dload;
167 // bytes uploaded at the moment the progress was last reported
168 double uload;
169 };
170
171
172
173 ProgressData::ProgressData(CURL *_curl, time_t _timeout, const Url &_url, ByteCount expectedFileSize_r, zypp::callback::SendReport< zypp::media::DownloadProgressReport> *_report)
174 : curl( _curl )
175 , url( _url )
176 , timeout( _timeout )
177 , reached( false )
178 , fileSizeExceeded ( false )
179 , report( _report )
180 , _expectedFileSize( expectedFileSize_r )
181 {}
182
183 void ProgressData::updateStats(double dltotal, double dlnow)
184 {
185 time_t now = _timeNow = time(0);
186
187 // If called without args (0.0), recompute based on the last values seen
188 if ( dltotal && dltotal != _dnlTotal )
189 _dnlTotal = dltotal;
190
191 if ( dlnow && dlnow != _dnlNow )
192 {
193 _timeRcv = now;
194 _dnlNow = dlnow;
195 }
196
197 // init or reset if time jumps back
198 if ( !_timeStart || _timeStart > now )
199 _timeStart = _timeLast = _timeRcv = now;
200
201 // timeout condition
202 if ( timeout )
203 reached = ( (now - _timeRcv) > timeout );
204
205 // check if the downloaded data is already bigger than what we expected
206 fileSizeExceeded = _expectedFileSize > 0 && _expectedFileSize < static_cast<ByteCount::SizeType>(_dnlNow);
207
208 // percentage:
209 if ( _dnlTotal )
210 _dnlPercent = int(_dnlNow * 100 / _dnlTotal);
211
212 // download rates:
213 _drateTotal = _dnlNow / std::max( int(now - _timeStart), 1 );
214
215 if ( _timeLast < now )
216 {
217 _drateLast = (_dnlNow - _dnlLast) / int(now - _timeLast);
218 // start new period
219 _timeLast = now;
221 }
222 else if ( _timeStart == _timeLast )
224 }
225
227 {
228 if ( fileSizeExceeded )
229 return 1;
230 if ( reached )
231 return 1; // no-data timeout
232 if ( report && !(*report)->progress( _dnlPercent, url, _drateTotal, _drateLast ) )
233 return 1; // user requested abort
234 return 0;
235 }
236
237 const char * anonymousIdHeader()
238 {
239 // we need to add the release and identifier to the
240 // agent string.
241 // The target could be not initialized, and then this information
242 // is guessed.
243 static const std::string _value(
245 "X-ZYpp-AnonymousId: %s",
246 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
247 );
248 return _value.c_str();
249 }
250
252 {
253 // we need to add the release and identifier to the
254 // agent string.
255 // The target could be not initialized, and then this information
256 // is guessed.
257 static const std::string _value(
259 "X-ZYpp-DistributionFlavor: %s",
260 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
261 );
262 return _value.c_str();
263 }
264
265 const char * agentString()
266 {
267 // we need to add the release and identifier to the
268 // agent string.
269 // The target could be not initialized, and then this information
270 // is guessed.
271 static const std::string _value(
272 str::form(
273 "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s"
274 , curl_version_info(CURLVERSION_NOW)->version
275 , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
276 )
277 );
278 return _value.c_str();
279 }
280}
281
282
283using namespace internal;
284using namespace zypp::base;
285
286namespace zypp {
287
288 namespace media {
289
290Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
291
292// we use this define to unbloat code as this C setting option
293// and catching exception is done frequently.
295#define SET_OPTION(opt,val) do { \
296 ret = curl_easy_setopt ( _curl, opt, val ); \
297 if ( ret != 0) { \
298 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \
299 } \
300 } while ( false )
301
302#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
303#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
304#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
305
307 const Pathname & attach_point_hint_r )
308 : MediaNetworkCommonHandler( url_r, attach_point_hint_r,
309 "/", // urlpath at attachpoint
310 true ), // does_download
311 _curl( NULL ),
312 _customHeaders(0L)
313{
314 _curlError[0] = '\0';
315 _curlDebug = 0L;
316
317 MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
318
319 globalInitCurlOnce();
320
321 if( !attachPoint().empty())
322 {
323 PathInfo ainfo(attachPoint());
324 Pathname apath(attachPoint() + "XXXXXX");
325 char *atemp = ::strdup( apath.asString().c_str());
326 char *atest = NULL;
327 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
328 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
329 {
330 WAR << "attach point " << ainfo.path()
331 << " is not useable for " << url_r.getScheme() << endl;
332 setAttachPoint("", true);
333 }
334 else if( atest != NULL)
335 ::rmdir(atest);
336
337 if( atemp != NULL)
338 ::free(atemp);
339 }
340}
341
343{
344 return internal::clearQueryString(url);
345}
346
347void MediaCurl::setCookieFile( const Pathname &fileName )
348{
349 _cookieFile = fileName;
350}
351
353
354void MediaCurl::checkProtocol(const Url &url) const
355{
356 curl_version_info_data *curl_info = NULL;
357 curl_info = curl_version_info(CURLVERSION_NOW);
358 // curl_info does not need any free (is static)
359 if (curl_info->protocols)
360 {
361 const char * const *proto;
362 std::string scheme( url.getScheme());
363 bool found = false;
364 for(proto=curl_info->protocols; !found && *proto; ++proto)
365 {
366 if( scheme == std::string((const char *)*proto))
367 found = true;
368 }
369 if( !found)
370 {
371 std::string msg("Unsupported protocol '");
372 msg += scheme;
373 msg += "'";
374 ZYPP_THROW(MediaBadUrlException(_url, msg));
375 }
376 }
377}
378
380{
381 {
382 _curlDebug = env::ZYPP_MEDIA_CURL_DEBUG();
383 if( _curlDebug > 0)
384 {
385 curl_easy_setopt( _curl, CURLOPT_VERBOSE, 1L);
386 curl_easy_setopt( _curl, CURLOPT_DEBUGFUNCTION, log_curl);
387 curl_easy_setopt( _curl, CURLOPT_DEBUGDATA, &_curlDebug);
388 }
389 }
390
391 curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
392 curl_easy_setopt(_curl, CURLOPT_HEADERDATA, &_lastRedirect);
393 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
394 if ( ret != 0 ) {
395 ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
396 }
397
398 SET_OPTION(CURLOPT_FAILONERROR, 1L);
399 SET_OPTION(CURLOPT_NOSIGNAL, 1L);
400
401 // create non persistant settings
402 // so that we don't add headers twice
403 TransferSettings vol_settings(_settings);
404
405 // add custom headers for download.opensuse.org (bsc#955801)
406 if ( _url.getHost() == "download.opensuse.org" )
407 {
408 vol_settings.addHeader(anonymousIdHeader());
409 vol_settings.addHeader(distributionFlavorHeader());
410 }
411 vol_settings.addHeader("Pragma:");
412
413 _settings.setTimeout(ZConfig::instance().download_transfer_timeout());
414 _settings.setConnectTimeout(CONNECT_TIMEOUT);
415
416 _settings.setUserAgentString(agentString());
417
418 // fill some settings from url query parameters
419 try
420 {
421 fillSettingsFromUrl(_url, _settings);
422 }
423 catch ( const MediaException &e )
424 {
426 ZYPP_RETHROW(e);
427 }
428 // if the proxy was not set (or explicitly unset) by url, then look...
429 if ( _settings.proxy().empty() )
430 {
431 // ...at the system proxy settings
432 fillSettingsSystemProxy(_url, _settings);
433 }
434
436 switch ( env::ZYPP_MEDIA_CURL_IPRESOLVE() )
437 {
438 case 4: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); break;
439 case 6: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); break;
440 }
441
445 SET_OPTION(CURLOPT_CONNECTTIMEOUT, _settings.connectTimeout());
446 // If a transfer timeout is set, also set CURLOPT_TIMEOUT to an upper limit
447 // just in case curl does not trigger its progress callback frequently
448 // enough.
449 if ( _settings.timeout() )
450 {
451 SET_OPTION(CURLOPT_TIMEOUT, 3600L);
452 }
453
454 // follow any Location: header that the server sends as part of
455 // an HTTP header (#113275)
456 SET_OPTION(CURLOPT_FOLLOWLOCATION, 1L);
457 // 3 redirects seem to be too few in some cases (bnc #465532)
458 SET_OPTION(CURLOPT_MAXREDIRS, 6L);
459
460 if ( _url.getScheme() == "https" )
461 {
462#if CURLVERSION_AT_LEAST(7,19,4)
463 // restrict following of redirections from https to https only
464 if ( _url.getHost() == "download.opensuse.org" )
465 SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
466 else
467 SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
468#endif
469
470 if( _settings.verifyPeerEnabled() ||
471 _settings.verifyHostEnabled() )
472 {
473 SET_OPTION(CURLOPT_CAPATH, _settings.certificateAuthoritiesPath().c_str());
474 }
475
476 if( ! _settings.clientCertificatePath().empty() )
477 {
478 SET_OPTION(CURLOPT_SSLCERT, _settings.clientCertificatePath().c_str());
479 }
480 if( ! _settings.clientKeyPath().empty() )
481 {
482 SET_OPTION(CURLOPT_SSLKEY, _settings.clientKeyPath().c_str());
483 }
484
485#ifdef CURLSSLOPT_ALLOW_BEAST
486 // see bnc#779177
487 ret = curl_easy_setopt( _curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
488 if ( ret != 0 ) {
490 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
491 }
492#endif
493 SET_OPTION(CURLOPT_SSL_VERIFYPEER, _settings.verifyPeerEnabled() ? 1L : 0L);
494 SET_OPTION(CURLOPT_SSL_VERIFYHOST, _settings.verifyHostEnabled() ? 2L : 0L);
495 // bnc#903405 - POODLE: libzypp should only talk TLS
496 SET_OPTION(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
497 }
498
499 SET_OPTION(CURLOPT_USERAGENT, _settings.userAgentString().c_str() );
500
501 /* Fixes bsc#1174011 "auth=basic ignored in some cases"
502 * We should proactively add the password to the request if basic auth is configured
503 * and a password is available in the credentials but not in the URL.
504 *
505 * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
506 * and ask the server first about the auth method
507 */
508 if ( _settings.authType() == "basic"
509 && _settings.username().size()
510 && !_settings.password().size() ) {
511
512 CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
513 const auto cred = cm.getCred( _url );
514 if ( cred && cred->valid() ) {
515 if ( !_settings.username().size() )
516 _settings.setUsername(cred->username());
517 _settings.setPassword(cred->password());
518 }
519 }
520
521 /*---------------------------------------------------------------*
522 CURLOPT_USERPWD: [user name]:[password]
523
524 Url::username/password -> CURLOPT_USERPWD
525 If not provided, anonymous FTP identification
526 *---------------------------------------------------------------*/
527
528 if ( _settings.userPassword().size() )
529 {
530 SET_OPTION(CURLOPT_USERPWD, _settings.userPassword().c_str());
531 std::string use_auth = _settings.authType();
532 if (use_auth.empty())
533 use_auth = "digest,basic"; // our default
534 long auth = CurlAuthData::auth_type_str2long(use_auth);
535 if( auth != CURLAUTH_NONE)
536 {
537 DBG << "Enabling HTTP authentication methods: " << use_auth
538 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
539 SET_OPTION(CURLOPT_HTTPAUTH, auth);
540 }
541 }
542
543 if ( _settings.proxyEnabled() && ! _settings.proxy().empty() )
544 {
545 DBG << "Proxy: '" << _settings.proxy() << "'" << endl;
546 SET_OPTION(CURLOPT_PROXY, _settings.proxy().c_str());
547 SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
548 /*---------------------------------------------------------------*
549 * CURLOPT_PROXYUSERPWD: [user name]:[password]
550 *
551 * Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
552 * If not provided, $HOME/.curlrc is evaluated
553 *---------------------------------------------------------------*/
554
555 std::string proxyuserpwd = _settings.proxyUserPassword();
556
557 if ( proxyuserpwd.empty() )
558 {
559 CurlConfig curlconf;
560 CurlConfig::parseConfig(curlconf); // parse ~/.curlrc
561 if ( curlconf.proxyuserpwd.empty() )
562 DBG << "Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
563 else
564 {
565 proxyuserpwd = curlconf.proxyuserpwd;
566 DBG << "Proxy: using proxy-user from ~/.curlrc" << endl;
567 }
568 }
569 else
570 {
571 DBG << "Proxy: using provided proxy-user '" << _settings.proxyUsername() << "'" << endl;
572 }
573
574 if ( ! proxyuserpwd.empty() )
575 {
576 SET_OPTION(CURLOPT_PROXYUSERPWD, curlUnEscape( proxyuserpwd ).c_str());
577 }
578 }
579#if CURLVERSION_AT_LEAST(7,19,4)
580 else if ( _settings.proxy() == EXPLICITLY_NO_PROXY )
581 {
582 // Explicitly disabled in URL (see fillSettingsFromUrl()).
583 // This should also prevent libcurl from looking into the environment.
584 DBG << "Proxy: explicitly NOPROXY" << endl;
585 SET_OPTION(CURLOPT_NOPROXY, "*");
586 }
587#endif
588 else
589 {
590 DBG << "Proxy: not explicitly set" << endl;
591 DBG << "Proxy: libcurl may look into the environment" << endl;
592 }
593
595 if ( _settings.minDownloadSpeed() != 0 )
596 {
597 SET_OPTION(CURLOPT_LOW_SPEED_LIMIT, _settings.minDownloadSpeed());
598 // default to 10 seconds at low speed
599 SET_OPTION(CURLOPT_LOW_SPEED_TIME, 60L);
600 }
601
602#if CURLVERSION_AT_LEAST(7,15,5)
603 if ( _settings.maxDownloadSpeed() != 0 )
604 SET_OPTION_OFFT(CURLOPT_MAX_RECV_SPEED_LARGE, _settings.maxDownloadSpeed());
605#endif
606
607 /*---------------------------------------------------------------*
608 *---------------------------------------------------------------*/
609
612 if ( str::strToBool( _url.getQueryParam( "cookies" ), true ) )
613 SET_OPTION(CURLOPT_COOKIEFILE, _currentCookieFile.c_str() );
614 else
615 MIL << "No cookies requested" << endl;
616 SET_OPTION(CURLOPT_COOKIEJAR, _currentCookieFile.c_str() );
617 SET_OPTION(CURLOPT_PROGRESSFUNCTION, &progressCallback );
618 SET_OPTION(CURLOPT_NOPROGRESS, 0L);
619
620#if CURLVERSION_AT_LEAST(7,18,0)
621 // bnc #306272
622 SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
623#endif
624 // append settings custom headers to curl
625 for ( const auto &header : vol_settings.headers() )
626 {
627 // MIL << "HEADER " << *it << std::endl;
628
629 _customHeaders = curl_slist_append(_customHeaders, header.c_str());
630 if ( !_customHeaders )
631 ZYPP_THROW(MediaCurlInitException(_url));
632 }
633
634 SET_OPTION(CURLOPT_HTTPHEADER, _customHeaders);
635}
636
638
639
640void MediaCurl::attachTo (bool next)
641{
642 if ( next )
643 ZYPP_THROW(MediaNotSupportedException(_url));
644
645 if ( !_url.isValid() )
646 ZYPP_THROW(MediaBadUrlException(_url));
647
650 {
652 }
653
654 disconnectFrom(); // clean _curl if needed
655 _curl = curl_easy_init();
656 if ( !_curl ) {
657 ZYPP_THROW(MediaCurlInitException(_url));
658 }
659 try
660 {
661 setupEasy();
662 }
663 catch (Exception & ex)
664 {
666 ZYPP_RETHROW(ex);
667 }
668
669 // FIXME: need a derived class to propelly compare url's
671 setMediaSource(media);
672}
673
674bool
676{
677 return MediaHandler::checkAttachPoint( apoint, true, true);
678}
679
681
683{
684 if ( _customHeaders )
685 {
686 curl_slist_free_all(_customHeaders);
687 _customHeaders = 0L;
688 }
689
690 if ( _curl )
691 {
692 // bsc#1201092: Strange but within global_dtors we may exceptions here.
693 try { curl_easy_cleanup( _curl ); }
694 catch (...) { ; }
695 _curl = NULL;
696 }
697}
698
700
701void MediaCurl::releaseFrom( const std::string & ejectDev )
702{
703 disconnect();
704}
705
707
708void MediaCurl::getFile( const OnMediaLocation &file ) const
709{
710 // Use absolute file name to prevent access of files outside of the
711 // hierarchy below the attach point.
712 getFileCopy( file, localPath(file.filename()).absolutename() );
713}
714
716
717void MediaCurl::getFileCopy( const OnMediaLocation & srcFile , const Pathname & target ) const
718{
719
720 const auto &filename = srcFile.filename();
721
722 // Optional files will send no report until data are actually received (we know it exists).
723 OptionalDownloadProgressReport reportfilter( srcFile.optional() );
725
726 Url fileurl(getFileUrl(filename));
727
728 bool retry = false;
729
730 do
731 {
732 try
733 {
734 doGetFileCopy( srcFile, target, report );
735 retry = false;
736 }
737 // retry with proper authentication data
738 catch (MediaUnauthorizedException & ex_r)
739 {
740 if(authenticate(ex_r.hint(), !retry))
741 retry = true;
742 else
743 {
744 report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
745 ZYPP_RETHROW(ex_r);
746 }
747 }
748 // unexpected exception
749 catch (MediaException & excpt_r)
750 {
752 if( typeid(excpt_r) == typeid( media::MediaFileNotFoundException ) ||
753 typeid(excpt_r) == typeid( media::MediaNotAFileException ) )
754 {
756 }
757 report->finish(fileurl, reason, excpt_r.asUserHistory());
758 ZYPP_RETHROW(excpt_r);
759 }
760 }
761 while (retry);
762
763 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
764}
765
767
768bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
769{
770 bool retry = false;
771
772 do
773 {
774 try
775 {
776 return doGetDoesFileExist( filename );
777 }
778 // authentication problem, retry with proper authentication data
779 catch (MediaUnauthorizedException & ex_r)
780 {
781 if(authenticate(ex_r.hint(), !retry))
782 retry = true;
783 else
784 ZYPP_RETHROW(ex_r);
785 }
786 // unexpected exception
787 catch (MediaException & excpt_r)
788 {
789 ZYPP_RETHROW(excpt_r);
790 }
791 }
792 while (retry);
793
794 return false;
795}
796
798
800 CURLcode code,
801 bool timeout_reached) const
802{
803 if ( code != 0 )
804 {
805 Url url;
806 if (filename.empty())
807 url = _url;
808 else
809 url = getFileUrl(filename);
810
811 std::string err;
812 {
813 switch ( code )
814 {
815 case CURLE_UNSUPPORTED_PROTOCOL:
816 err = " Unsupported protocol";
817 if ( !_lastRedirect.empty() )
818 {
819 err += " or redirect (";
820 err += _lastRedirect;
821 err += ")";
822 }
823 break;
824 case CURLE_URL_MALFORMAT:
825 case CURLE_URL_MALFORMAT_USER:
826 err = " Bad URL";
827 break;
828 case CURLE_LOGIN_DENIED:
830 MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
831 break;
832 case CURLE_HTTP_RETURNED_ERROR:
833 {
834 long httpReturnCode = 0;
835 CURLcode infoRet = curl_easy_getinfo( _curl,
836 CURLINFO_RESPONSE_CODE,
837 &httpReturnCode );
838 if ( infoRet == CURLE_OK )
839 {
840 std::string msg = "HTTP response: " + str::numstring( httpReturnCode );
841 switch ( httpReturnCode )
842 {
843 case 401:
844 {
845 std::string auth_hint = getAuthHint();
846
847 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
848 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
849
850 ZYPP_THROW(MediaUnauthorizedException(
851 url, "Login failed.", _curlError, auth_hint
852 ));
853 }
854
855 case 502: // bad gateway (bnc #1070851)
856 case 503: // service temporarily unavailable (bnc #462545)
857 ZYPP_THROW(MediaTemporaryProblemException(url));
858 case 504: // gateway timeout
859 ZYPP_THROW(MediaTimeoutException(url));
860 case 403:
861 {
862 std::string msg403;
863 if ( url.getHost().find(".suse.com") != std::string::npos )
864 msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
865 else if (url.asString().find("novell.com") != std::string::npos)
866 msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
867 ZYPP_THROW(MediaForbiddenException(url, msg403));
868 }
869 case 404:
870 case 410:
871 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
872 }
873
874 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
875 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
876 }
877 else
878 {
879 std::string msg = "Unable to retrieve HTTP response:";
880 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
881 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
882 }
883 }
884 break;
885 case CURLE_FTP_COULDNT_RETR_FILE:
886#if CURLVERSION_AT_LEAST(7,16,0)
887 case CURLE_REMOTE_FILE_NOT_FOUND:
888#endif
889 case CURLE_FTP_ACCESS_DENIED:
890 case CURLE_TFTP_NOTFOUND:
891 err = "File not found";
892 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
893 break;
894 case CURLE_BAD_PASSWORD_ENTERED:
895 case CURLE_FTP_USER_PASSWORD_INCORRECT:
896 err = "Login failed";
897 break;
898 case CURLE_COULDNT_RESOLVE_PROXY:
899 case CURLE_COULDNT_RESOLVE_HOST:
900 case CURLE_COULDNT_CONNECT:
901 case CURLE_FTP_CANT_GET_HOST:
902 err = "Connection failed";
903 break;
904 case CURLE_WRITE_ERROR:
905 err = "Write error";
906 break;
907 case CURLE_PARTIAL_FILE:
908 case CURLE_OPERATION_TIMEDOUT:
909 timeout_reached = true; // fall though to TimeoutException
910 // fall though...
911 case CURLE_ABORTED_BY_CALLBACK:
912 if( timeout_reached )
913 {
914 err = "Timeout reached";
915 ZYPP_THROW(MediaTimeoutException(url));
916 }
917 else
918 {
919 err = "User abort";
920 }
921 break;
922 case CURLE_SSL_PEER_CERTIFICATE:
923 default:
924 err = "Curl error " + str::numstring( code );
925 break;
926 }
927
928 // uhm, no 0 code but unknown curl exception
929 ZYPP_THROW(MediaCurlException(url, err, _curlError));
930 }
931 }
932 else
933 {
934 // actually the code is 0, nothing happened
935 }
936}
937
939
940bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
941{
942 DBG << filename.asString() << endl;
943
944 if(!_url.isValid())
945 ZYPP_THROW(MediaBadUrlException(_url));
946
947 if(_url.getHost().empty())
948 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
949
950 Url url(getFileUrl(filename));
951
952 DBG << "URL: " << url.asString() << endl;
953 // Use URL without options and without username and passwd
954 // (some proxies dislike them in the URL).
955 // Curl seems to need the just scheme, hostname and a path;
956 // the rest was already passed as curl options (in attachTo).
957 Url curlUrl( clearQueryString(url) );
958
959 //
960 // See also Bug #154197 and ftp url definition in RFC 1738:
961 // The url "ftp://user@host/foo/bar/file" contains a path,
962 // that is relative to the user's home.
963 // The url "ftp://user@host//foo/bar/file" (or also with
964 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
965 // contains an absolute path.
966 //
967 _lastRedirect.clear();
968 std::string urlBuffer( curlUrl.asString());
969 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
970 urlBuffer.c_str() );
971 if ( ret != 0 ) {
972 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
973 }
974
975 // instead of returning no data with NOBODY, we return
976 // little data, that works with broken servers, and
977 // works for ftp as well, because retrieving only headers
978 // ftp will return always OK code ?
979 // See http://curl.haxx.se/docs/knownbugs.html #58
980 if ( (_url.getScheme() == "http" || _url.getScheme() == "https") &&
981 _settings.headRequestsAllowed() )
982 ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
983 else
984 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
985
986 if ( ret != 0 ) {
987 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
988 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
989 /* yes, this is why we never got to get NOBODY working before,
990 because setting it changes this option too, and we also
991 need to reset it
992 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
993 */
994 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
995 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
996 }
997
998 AutoFILE file { ::fopen( "/dev/null", "w" ) };
999 if ( !file ) {
1000 ERR << "fopen failed for /dev/null" << endl;
1001 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
1002 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1003 /* yes, this is why we never got to get NOBODY working before,
1004 because setting it changes this option too, and we also
1005 need to reset it
1006 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1007 */
1008 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
1009 if ( ret != 0 ) {
1010 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1011 }
1012 ZYPP_THROW(MediaWriteException("/dev/null"));
1013 }
1014
1015 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, (*file) );
1016 if ( ret != 0 ) {
1017 std::string err( _curlError);
1018 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1019 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
1020 /* yes, this is why we never got to get NOBODY working before,
1021 because setting it changes this option too, and we also
1022 need to reset it
1023 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1024 */
1025 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
1026 if ( ret != 0 ) {
1027 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1028 }
1029 ZYPP_THROW(MediaCurlSetOptException(url, err));
1030 }
1031
1032 CURLcode ok = curl_easy_perform( _curl );
1033 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
1034
1035 // reset curl settings
1036 if ( _url.getScheme() == "http" || _url.getScheme() == "https" )
1037 {
1038 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
1039 if ( ret != 0 ) {
1040 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1041 }
1042
1043 /* yes, this is why we never got to get NOBODY working before,
1044 because setting it changes this option too, and we also
1045 need to reset it
1046 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1047 */
1048 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L);
1049 if ( ret != 0 ) {
1050 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1051 }
1052
1053 }
1054 else
1055 {
1056 // for FTP we set different options
1057 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL);
1058 if ( ret != 0 ) {
1059 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1060 }
1061 }
1062
1063 // as we are not having user interaction, the user can't cancel
1064 // the file existence checking, a callback or timeout return code
1065 // will be always a timeout.
1066 try {
1067 evaluateCurlCode( filename, ok, true /* timeout */);
1068 }
1069 catch ( const MediaFileNotFoundException &e ) {
1070 // if the file did not exist then we can return false
1071 return false;
1072 }
1073 catch ( const MediaException &e ) {
1074 // some error, we are not sure about file existence, rethrw
1075 ZYPP_RETHROW(e);
1076 }
1077 // exists
1078 return ( ok == CURLE_OK );
1079}
1080
1082
1083
1084#if DETECT_DIR_INDEX
1085bool MediaCurl::detectDirIndex() const
1086{
1087 if(_url.getScheme() != "http" && _url.getScheme() != "https")
1088 return false;
1089 //
1090 // try to check the effective url and set the not_a_file flag
1091 // if the url path ends with a "/", what usually means, that
1092 // we've received a directory index (index.html content).
1093 //
1094 // Note: This may be dangerous and break file retrieving in
1095 // case of some server redirections ... ?
1096 //
1097 bool not_a_file = false;
1098 char *ptr = NULL;
1099 CURLcode ret = curl_easy_getinfo( _curl,
1100 CURLINFO_EFFECTIVE_URL,
1101 &ptr);
1102 if ( ret == CURLE_OK && ptr != NULL)
1103 {
1104 try
1105 {
1106 Url eurl( ptr);
1107 std::string path( eurl.getPathName());
1108 if( !path.empty() && path != "/" && *path.rbegin() == '/')
1109 {
1110 DBG << "Effective url ("
1111 << eurl
1112 << ") seems to provide the index of a directory"
1113 << endl;
1114 not_a_file = true;
1115 }
1116 }
1117 catch( ... )
1118 {}
1119 }
1120 return not_a_file;
1121}
1122#endif
1123
1125
1126void MediaCurl::doGetFileCopy( const OnMediaLocation &srcFile , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1127{
1128 Pathname dest = target.absolutename();
1129 if( assert_dir( dest.dirname() ) )
1130 {
1131 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
1132 ZYPP_THROW( MediaSystemException(getFileUrl(srcFile.filename()), "System error on " + dest.dirname().asString()) );
1133 }
1134
1135 ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) };
1136 AutoFILE file;
1137 {
1138 AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
1139 if( ! buf )
1140 {
1141 ERR << "out of memory for temp file name" << endl;
1142 ZYPP_THROW(MediaSystemException(getFileUrl(srcFile.filename()), "out of memory for temp file name"));
1143 }
1144
1145 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1146 if( tmp_fd == -1 )
1147 {
1148 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
1149 ZYPP_THROW(MediaWriteException(destNew));
1150 }
1151 destNew = ManagedFile( (*buf), filesystem::unlink );
1152
1153 file = ::fdopen( tmp_fd, "we" );
1154 if ( ! file )
1155 {
1156 ERR << "fopen failed for file '" << destNew << "'" << endl;
1157 ZYPP_THROW(MediaWriteException(destNew));
1158 }
1159 tmp_fd.resetDispose(); // don't close it here! ::fdopen moved ownership to file
1160 }
1161
1162 DBG << "dest: " << dest << endl;
1163 DBG << "temp: " << destNew << endl;
1164
1165 // set IFMODSINCE time condition (no download if not modified)
1166 if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
1167 {
1168 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1169 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
1170 }
1171 else
1172 {
1173 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1174 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1175 }
1176 try
1177 {
1178 doGetFileCopyFile( srcFile, dest, file, report, options);
1179 }
1180 catch (Exception &e)
1181 {
1182 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1183 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1184 ZYPP_RETHROW(e);
1185 }
1186
1187 long httpReturnCode = 0;
1188 CURLcode infoRet = curl_easy_getinfo(_curl,
1189 CURLINFO_RESPONSE_CODE,
1190 &httpReturnCode);
1191 bool modified = true;
1192 if (infoRet == CURLE_OK)
1193 {
1194 DBG << "HTTP response: " + str::numstring(httpReturnCode);
1195 if ( httpReturnCode == 304
1196 || ( httpReturnCode == 213 && (_url.getScheme() == "ftp" || _url.getScheme() == "tftp") ) ) // not modified
1197 {
1198 DBG << " Not modified.";
1199 modified = false;
1200 }
1201 DBG << endl;
1202 }
1203 else
1204 {
1205 WAR << "Could not get the response code." << endl;
1206 }
1207
1208 if (modified || infoRet != CURLE_OK)
1209 {
1210 // apply umask
1211 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
1212 {
1213 ERR << "Failed to chmod file " << destNew << endl;
1214 }
1215
1216 file.resetDispose(); // we're going to close it manually here
1217 if ( ::fclose( file ) )
1218 {
1219 ERR << "Fclose failed for file '" << destNew << "'" << endl;
1220 ZYPP_THROW(MediaWriteException(destNew));
1221 }
1222
1223 // move the temp file into dest
1224 if ( rename( destNew, dest ) != 0 ) {
1225 ERR << "Rename failed" << endl;
1226 ZYPP_THROW(MediaWriteException(dest));
1227 }
1228 destNew.resetDispose(); // no more need to unlink it
1229 }
1230
1231 DBG << "done: " << PathInfo(dest) << endl;
1232}
1233
1235
1236void MediaCurl::doGetFileCopyFile( const OnMediaLocation & srcFile, const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1237{
1238 DBG << srcFile.filename().asString() << endl;
1239
1240 if(!_url.isValid())
1241 ZYPP_THROW(MediaBadUrlException(_url));
1242
1243 if(_url.getHost().empty())
1244 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
1245
1246 Url url(getFileUrl(srcFile.filename()));
1247
1248 DBG << "URL: " << url.asString() << endl;
1249 // Use URL without options and without username and passwd
1250 // (some proxies dislike them in the URL).
1251 // Curl seems to need the just scheme, hostname and a path;
1252 // the rest was already passed as curl options (in attachTo).
1253 Url curlUrl( clearQueryString(url) );
1254
1255 //
1256 // See also Bug #154197 and ftp url definition in RFC 1738:
1257 // The url "ftp://user@host/foo/bar/file" contains a path,
1258 // that is relative to the user's home.
1259 // The url "ftp://user@host//foo/bar/file" (or also with
1260 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1261 // contains an absolute path.
1262 //
1263 _lastRedirect.clear();
1264 std::string urlBuffer( curlUrl.asString());
1265 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1266 urlBuffer.c_str() );
1267 if ( ret != 0 ) {
1268 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1269 }
1270
1271 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1272 if ( ret != 0 ) {
1273 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1274 }
1275
1276 // Set callback and perform.
1277 internal::ProgressData progressData(_curl, _settings.timeout(), url, srcFile.downloadSize(), &report);
1278 if (!(options & OPTION_NO_REPORT_START))
1279 report->start(url, dest);
1280 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1281 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1282 }
1283
1284 ret = curl_easy_perform( _curl );
1285#if CURLVERSION_AT_LEAST(7,19,4)
1286 // bnc#692260: If the client sends a request with an If-Modified-Since header
1287 // with a future date for the server, the server may respond 200 sending a
1288 // zero size file.
1289 // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
1290 if ( ftell(file) == 0 && ret == 0 )
1291 {
1292 long httpReturnCode = 33;
1293 if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1294 {
1295 long conditionUnmet = 33;
1296 if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1297 {
1298 WAR << "TIMECONDITION unmet - retry without." << endl;
1299 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1300 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1301 ret = curl_easy_perform( _curl );
1302 }
1303 }
1304 }
1305#endif
1306
1307 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1308 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1309 }
1310
1311 if ( ret != 0 )
1312 {
1313 ERR << "curl error: " << ret << ": " << _curlError
1314 << ", temp file size " << ftell(file)
1315 << " bytes." << endl;
1316
1317 // the timeout is determined by the progress data object
1318 // which holds whether the timeout was reached or not,
1319 // otherwise it would be a user cancel
1320 try {
1321
1322 if ( progressData.fileSizeExceeded )
1323 ZYPP_THROW(MediaFileSizeExceededException(url, progressData._expectedFileSize));
1324
1325 evaluateCurlCode( srcFile.filename(), ret, progressData.reached );
1326 }
1327 catch ( const MediaException &e ) {
1328 // some error, we are not sure about file existence, rethrw
1329 ZYPP_RETHROW(e);
1330 }
1331 }
1332
1333#if DETECT_DIR_INDEX
1334 if (!ret && detectDirIndex())
1335 {
1336 ZYPP_THROW(MediaNotAFileException(_url, filename));
1337 }
1338#endif // DETECT_DIR_INDEX
1339}
1340
1342
1343void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
1344{
1345 filesystem::DirContent content;
1346 getDirInfo( content, dirname, /*dots*/false );
1347
1348 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1349 Pathname filename = dirname + it->name;
1350 int res = 0;
1351
1352 switch ( it->type ) {
1353 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
1355 getFile( OnMediaLocation( filename ) );
1356 break;
1357 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
1358 if ( recurse_r ) {
1359 getDir( filename, recurse_r );
1360 } else {
1361 res = assert_dir( localPath( filename ) );
1362 if ( res ) {
1363 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
1364 }
1365 }
1366 break;
1367 default:
1368 // don't provide devices, sockets, etc.
1369 break;
1370 }
1371 }
1372}
1373
1375
1376void MediaCurl::getDirInfo( std::list<std::string> & retlist,
1377 const Pathname & dirname, bool dots ) const
1378{
1379 getDirectoryYast( retlist, dirname, dots );
1380}
1381
1383
1385 const Pathname & dirname, bool dots ) const
1386{
1387 getDirectoryYast( retlist, dirname, dots );
1388}
1389
1391//
1392int MediaCurl::aliveCallback( void *clientp, double /*dltotal*/, double dlnow, double /*ultotal*/, double /*ulnow*/ )
1393{
1394 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1395 if( pdata )
1396 {
1397 // Do not propagate dltotal in alive callbacks. MultiCurl uses this to
1398 // prevent a percentage raise while downloading a metalink file. Download
1399 // activity however is indicated by propagating the download rate (via dlnow).
1400 pdata->updateStats( 0.0, dlnow );
1401 return pdata->reportProgress();
1402 }
1403 return 0;
1404}
1405
1406int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow )
1407{
1408 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1409 if( pdata )
1410 {
1411 // work around curl bug that gives us old data
1412 long httpReturnCode = 0;
1413 if ( curl_easy_getinfo( pdata->curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1414 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1415
1416 pdata->updateStats( dltotal, dlnow );
1417 return pdata->reportProgress();
1418 }
1419 return 0;
1420}
1421
1423{
1424 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>(clientp);
1425 return pdata ? pdata->curl : 0;
1426}
1427
1429
1430std::string MediaCurl::getAuthHint() const
1431{
1432 long auth_info = CURLAUTH_NONE;
1433
1434 CURLcode infoRet =
1435 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1436
1437 if(infoRet == CURLE_OK)
1438 {
1439 return CurlAuthData::auth_type_long2str(auth_info);
1440 }
1441
1442 return "";
1443}
1444
1449void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
1450{
1451 internal::ProgressData *data = reinterpret_cast<internal::ProgressData *>(clientp);
1452 if ( data ) {
1453 data->_expectedFileSize = expectedFileSize;
1454 }
1455}
1456
1458
1459bool MediaCurl::authenticate(const std::string & availAuthTypes, bool firstTry) const
1460{
1462 CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
1463 CurlAuthData_Ptr credentials;
1464
1465 // get stored credentials
1466 AuthData_Ptr cmcred = cm.getCred(_url);
1467
1468 if (cmcred && firstTry)
1469 {
1470 credentials.reset(new CurlAuthData(*cmcred));
1471 DBG << "got stored credentials:" << endl << *credentials << endl;
1472 }
1473 // if not found, ask user
1474 else
1475 {
1476
1477 CurlAuthData_Ptr curlcred;
1478 curlcred.reset(new CurlAuthData());
1480
1481 // preset the username if present in current url
1482 if (!_url.getUsername().empty() && firstTry)
1483 curlcred->setUsername(_url.getUsername());
1484 // if CM has found some credentials, preset the username from there
1485 else if (cmcred)
1486 curlcred->setUsername(cmcred->username());
1487
1488 // indicate we have no good credentials from CM
1489 cmcred.reset();
1490
1491 std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % _url.asString();
1492
1493 // set available authentication types from the exception
1494 // might be needed in prompt
1495 curlcred->setAuthType(availAuthTypes);
1496
1497 // ask user
1498 if (auth_report->prompt(_url, prompt_msg, *curlcred))
1499 {
1500 DBG << "callback answer: retry" << endl
1501 << "CurlAuthData: " << *curlcred << endl;
1502
1503 if (curlcred->valid())
1504 {
1505 credentials = curlcred;
1506 // if (credentials->username() != _url.getUsername())
1507 // _url.setUsername(credentials->username());
1515 }
1516 }
1517 else
1518 {
1519 DBG << "callback answer: cancel" << endl;
1520 }
1521 }
1522
1523 // set username and password
1524 if (credentials)
1525 {
1526 // HACK, why is this const?
1527 const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
1528 const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
1529
1530 // set username and password
1531 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
1532 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
1533
1534 // set available authentication types from the exception
1535 if (credentials->authType() == CURLAUTH_NONE)
1536 credentials->setAuthType(availAuthTypes);
1537
1538 // set auth type (seems this must be set _after_ setting the userpwd)
1539 if (credentials->authType() != CURLAUTH_NONE)
1540 {
1541 // FIXME: only overwrite if not empty?
1542 const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
1543 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
1544 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
1545 }
1546
1547 if (!cmcred)
1548 {
1549 credentials->setUrl(_url);
1550 cm.addCred(*credentials);
1551 cm.save();
1552 }
1553
1554 return true;
1555 }
1556
1557 return false;
1558}
1559
1560//need a out of line definiton, otherwise vtable is emitted for every translation unit
1562
1563 } // namespace media
1564} // namespace zypp
1565//
#define SET_OPTION_OFFT(opt, val)
Definition: MediaCurl.cc:302
#define SET_OPTION(opt, val)
Definition: MediaCurl.cc:295
Edition * _value
Definition: SysContent.cc:311
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:94
void resetDispose()
Set no dispose function.
Definition: AutoDispose.h:180
Store and operate with byte count.
Definition: ByteCount.h:31
Base class for Exception.
Definition: Exception.h:146
const char * c_str() const
Definition: IdStringType.h:105
Describes a resource file located on a medium.
bool optional() const
Whether this is an optional resource.
const ByteCount & downloadSize() const
The size of the resource on the server.
const Pathname & filename() const
The path to the resource on the medium.
ProgressData()
Ctor no range [0,0](0).
Definition: progressdata.h:171
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
Definition: Target.cc:127
std::string anonymousUniqueId() const
anonymous unique id
Definition: Target.cc:132
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: Target.cc:102
Url manipulation class.
Definition: Url.h:92
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:497
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:572
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition: Url.cc:660
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition: Url.cc:588
bool isValid() const
Verifies the Url.
Definition: Url.cc:489
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:832
Typesafe passing of user data via callbacks.
Definition: UserData.h:39
Wrapper class for stat/lstat.
Definition: PathInfo.h:221
bool userMayRWX() const
Definition: PathInfo.h:353
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:246
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:173
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
const std::string & asString() const
String representation.
Definition: Pathname.h:91
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
Definition: Pathname.h:139
Implementation class for FTP, HTTP and HTTPS MediaHandler.
Definition: MediaCurl.h:32
virtual void setupEasy()
initializes the curl easy handle with the data from the url
Definition: MediaCurl.cc:379
static void setCookieFile(const Pathname &)
Definition: MediaCurl.cc:347
virtual bool getDoesFileExist(const Pathname &filename) const override
Repeatedly calls doGetDoesFileExist() until it successfully returns, fails unexpectedly,...
Definition: MediaCurl.cc:768
@ OPTION_NO_IFMODSINCE
to not add a IFMODSINCE header if target exists
Definition: MediaCurl.h:43
@ OPTION_NO_REPORT_START
do not send a start ProgressReport
Definition: MediaCurl.h:45
virtual void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
Definition: MediaCurl.cc:708
static void resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
MediaMultiCurl needs to reset the expected filesize in case a metalink file is downloaded otherwise t...
Definition: MediaCurl.cc:1449
std::string _currentCookieFile
Definition: MediaCurl.h:162
static int aliveCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Callback sending just an alive trigger to the UI, without stats (e.g.
Definition: MediaCurl.cc:1392
static Pathname _cookieFile
Definition: MediaCurl.h:163
static int progressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Callback reporting download progress.
Definition: MediaCurl.cc:1406
virtual void getFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename) const override
Definition: MediaCurl.cc:717
virtual void doGetFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename, callback::SendReport< DownloadProgressReport > &_report, RequestOptions options=OPTION_NONE) const
Definition: MediaCurl.cc:1126
std::string _lastRedirect
to log/report redirections
Definition: MediaCurl.h:165
Url clearQueryString(const Url &url) const
Definition: MediaCurl.cc:342
virtual void attachTo(bool next=false) override
Call concrete handler to attach the media.
Definition: MediaCurl.cc:640
virtual void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const override
Call concrete handler to provide a content list of directory on media via retlist.
Definition: MediaCurl.cc:1376
char _curlError[CURL_ERROR_SIZE]
Definition: MediaCurl.h:169
void doGetFileCopyFile(const OnMediaLocation &srcFile, const Pathname &dest, FILE *file, callback::SendReport< DownloadProgressReport > &report, RequestOptions options=OPTION_NONE) const
Definition: MediaCurl.cc:1236
void checkProtocol(const Url &url) const
check the url is supported by the curl library
Definition: MediaCurl.cc:354
MediaCurl(const Url &url_r, const Pathname &attach_point_hint_r)
Definition: MediaCurl.cc:306
bool detectDirIndex() const
void evaluateCurlCode(const zypp::Pathname &filename, CURLcode code, bool timeout) const
Evaluates a curl return code and throws the right MediaException filename Filename being downloaded c...
Definition: MediaCurl.cc:799
virtual bool checkAttachPoint(const Pathname &apoint) const override
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
Definition: MediaCurl.cc:675
virtual void getDir(const Pathname &dirname, bool recurse_r) const override
Call concrete handler to provide directory content (not recursive!) below attach point.
Definition: MediaCurl.cc:1343
bool authenticate(const std::string &availAuthTypes, bool firstTry) const
Definition: MediaCurl.cc:1459
static CURL * progressCallback_getcurl(void *clientp)
Definition: MediaCurl.cc:1422
virtual void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition: MediaCurl.cc:701
virtual void disconnectFrom() override
Definition: MediaCurl.cc:682
virtual bool doGetDoesFileExist(const Pathname &filename) const
Definition: MediaCurl.cc:940
curl_slist * _customHeaders
Definition: MediaCurl.h:170
std::string getAuthHint() const
Return a comma separated list of available authentication methods supported by server.
Definition: MediaCurl.cc:1430
bool isUseableAttachPoint(const Pathname &path, bool mtab=true) const
Ask media manager, if the specified path is already used as attach point or if there are another atta...
Url url() const
Url used.
Definition: MediaHandler.h:503
virtual bool checkAttachPoint(const Pathname &apoint) const
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
void disconnect()
Use concrete handler to isconnect media.
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
Pathname localPath(const Pathname &pathname) const
Files provided will be available at 'localPath(filename)'.
const Url _url
Url to handle.
Definition: MediaHandler.h:113
void getDirectoryYast(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const
Retrieve and if available scan dirname/directory.yast.
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
Pathname attachPoint() const
Return the currently used attach point.
Common baseclass for MediaCurl and MediaNetwork.
Url getFileUrl(const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
Media source internally used by MediaManager and MediaHandler.
Definition: MediaSource.h:37
const char * anonymousIdHeader()
Definition: MediaCurl.cc:237
const char * distributionFlavorHeader()
Definition: MediaCurl.cc:251
const char * agentString()
Definition: MediaCurl.cc:265
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
Definition: PathInfo.h:789
int unlink(const Pathname &path)
Like 'unlink'.
Definition: PathInfo.cc:700
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
Definition: PathInfo.cc:1202
std::list< DirEntry > DirContent
Returned by readdir.
Definition: PathInfo.h:518
std::string numstring(char n, int w=0)
Definition: String.h:289
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:429
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition: ManagedFile.h:27
Optional files will send no report until data are actually received (we know it exists).
Definition: MediaCurl.cc:47
void report(const UserData &userData_r=UserData()) override
The most generic way of sending/receiving data.
Definition: MediaCurl.cc:62
Action problem(const Url &file_r, Error error_r, const std::string &description_r) override
Definition: MediaCurl.cc:89
OptionalDownloadProgressReport(bool isOptional=false)
Definition: MediaCurl.cc:48
void finish(const Url &file_r, Error error_r, const std::string &reason_r) override
Definition: MediaCurl.cc:95
bool progress(int value_r, const Url &file_r, double dbps_avg_r=-1, double dbps_current_r=-1) override
Download progress.
Definition: MediaCurl.cc:78
void start(const Url &file_r, Pathname localfile_r) override
Definition: MediaCurl.cc:66
zypp::ByteCount _expectedFileSize
Definition: MediaCurl.cc:134
void updateStats(double dltotal=0.0, double dlnow=0.0)
Definition: MediaCurl.cc:183
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
Definition: MediaCurl.cc:145
time_t _timeRcv
Start of no-data timeout.
Definition: MediaCurl.cc:138
time_t _timeLast
Start last period(~1sec)
Definition: MediaCurl.cc:137
int reportProgress() const
Definition: MediaCurl.cc:226
double _drateLast
Download rate in last period.
Definition: MediaCurl.cc:148
double _dnlTotal
Bytes to download or 0 if unknown.
Definition: MediaCurl.cc:141
double _dnlNow
Bytes downloaded now.
Definition: MediaCurl.cc:143
double _drateTotal
Download rate so far.
Definition: MediaCurl.cc:147
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
Definition: MediaCurl.cc:133
double _dnlLast
Bytes downloaded at period start.
Definition: MediaCurl.cc:142
time_t _timeStart
Start total stats.
Definition: MediaCurl.cc:136
AutoDispose<int> calling ::close
Definition: AutoDispose.h:301
AutoDispose<FILE*> calling ::fclose
Definition: AutoDispose.h:312
static DistributeReport & instance()
Definition: Callback.h:204
void setReceiver(Receiver &rec_r)
Definition: Callback.h:213
virtual void reportend()
Definition: Callback.h:191
virtual void reportbegin()
Definition: Callback.h:189
virtual void report(const UserData &userData_r=UserData())
The most generic way of sending/receiving data.
Definition: Callback.h:155
callback::UserData UserData
Definition: Callback.h:151
virtual void finish(const Url &, Error, const std::string &)
virtual bool progress(int, const Url &, double dbps_avg=-1, double dbps_current=-1)
Download progress.
virtual void start(const Url &, Pathname)
virtual Action problem(const Url &, Error, const std::string &)
Convenient building of std::string with boost::format.
Definition: String.h:253
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:440
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
#define _(MSG)
Definition: Gettext.h:37
#define DBG
Definition: Logger.h:95
#define MIL
Definition: Logger.h:96
#define ERR
Definition: Logger.h:98
#define WAR
Definition: Logger.h:97