libzypp 17.31.6
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 // If no head requests allowed (?head_requests=no):
976 // Instead of returning no data with NOBODY, we return
977 // little data, that works with broken servers, and
978 // works for ftp as well, because retrieving only headers
979 // ftp will return always OK code ?
980 // See http://curl.haxx.se/docs/knownbugs.html #58
982 struct TempSetHeadRequest
983 {
984 TempSetHeadRequest( CURL * curl_r, bool doHttpHeadRequest_r )
985 : _curl { curl_r }
986 , _doHttpHeadRequest { doHttpHeadRequest_r }
987 {
988 if ( _doHttpHeadRequest ) {
989 curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
990 } else {
991 curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
992 }
993 }
994 ~TempSetHeadRequest() {
995 if ( _doHttpHeadRequest ) {
996 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
997 /* yes, this is why we never got to get NOBODY working before,
998 because setting it changes this option too, and we also*
999 need to reset it
1000 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1001 */
1002 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
1003 } else {
1004 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1005 }
1006 }
1007 private:
1008 CURL * _curl;
1009 bool _doHttpHeadRequest;
1010 } _guard( _curl, (_url.getScheme() == "http" || _url.getScheme() == "https") && _settings.headRequestsAllowed() );
1011
1012
1013 AutoFILE file { ::fopen( "/dev/null", "w" ) };
1014 if ( !file ) {
1015 ERR << "fopen failed for /dev/null" << endl;
1016 ZYPP_THROW(MediaWriteException("/dev/null"));
1017 }
1018
1019 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, (*file) );
1020 if ( ret != 0 ) {
1021 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1022 }
1023
1024 CURLcode ok = curl_easy_perform( _curl );
1025 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
1026
1027 // as we are not having user interaction, the user can't cancel
1028 // the file existence checking, a callback or timeout return code
1029 // will be always a timeout.
1030 try {
1031 evaluateCurlCode( filename, ok, true /* timeout */);
1032 }
1033 catch ( const MediaFileNotFoundException &e ) {
1034 // if the file did not exist then we can return false
1035 return false;
1036 }
1037 catch ( const MediaException &e ) {
1038 // some error, we are not sure about file existence, rethrw
1039 ZYPP_RETHROW(e);
1040 }
1041 // exists
1042 return ( ok == CURLE_OK );
1043}
1044
1046
1047
1048#if DETECT_DIR_INDEX
1049bool MediaCurl::detectDirIndex() const
1050{
1051 if(_url.getScheme() != "http" && _url.getScheme() != "https")
1052 return false;
1053 //
1054 // try to check the effective url and set the not_a_file flag
1055 // if the url path ends with a "/", what usually means, that
1056 // we've received a directory index (index.html content).
1057 //
1058 // Note: This may be dangerous and break file retrieving in
1059 // case of some server redirections ... ?
1060 //
1061 bool not_a_file = false;
1062 char *ptr = NULL;
1063 CURLcode ret = curl_easy_getinfo( _curl,
1064 CURLINFO_EFFECTIVE_URL,
1065 &ptr);
1066 if ( ret == CURLE_OK && ptr != NULL)
1067 {
1068 try
1069 {
1070 Url eurl( ptr);
1071 std::string path( eurl.getPathName());
1072 if( !path.empty() && path != "/" && *path.rbegin() == '/')
1073 {
1074 DBG << "Effective url ("
1075 << eurl
1076 << ") seems to provide the index of a directory"
1077 << endl;
1078 not_a_file = true;
1079 }
1080 }
1081 catch( ... )
1082 {}
1083 }
1084 return not_a_file;
1085}
1086#endif
1087
1089
1090void MediaCurl::doGetFileCopy( const OnMediaLocation &srcFile , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1091{
1092 Pathname dest = target.absolutename();
1093 if( assert_dir( dest.dirname() ) )
1094 {
1095 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
1096 ZYPP_THROW( MediaSystemException(getFileUrl(srcFile.filename()), "System error on " + dest.dirname().asString()) );
1097 }
1098
1099 ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) };
1100 AutoFILE file;
1101 {
1102 AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
1103 if( ! buf )
1104 {
1105 ERR << "out of memory for temp file name" << endl;
1106 ZYPP_THROW(MediaSystemException(getFileUrl(srcFile.filename()), "out of memory for temp file name"));
1107 }
1108
1109 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1110 if( tmp_fd == -1 )
1111 {
1112 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
1113 ZYPP_THROW(MediaWriteException(destNew));
1114 }
1115 destNew = ManagedFile( (*buf), filesystem::unlink );
1116
1117 file = ::fdopen( tmp_fd, "we" );
1118 if ( ! file )
1119 {
1120 ERR << "fopen failed for file '" << destNew << "'" << endl;
1121 ZYPP_THROW(MediaWriteException(destNew));
1122 }
1123 tmp_fd.resetDispose(); // don't close it here! ::fdopen moved ownership to file
1124 }
1125
1126 DBG << "dest: " << dest << endl;
1127 DBG << "temp: " << destNew << endl;
1128
1129 // set IFMODSINCE time condition (no download if not modified)
1130 if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
1131 {
1132 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1133 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
1134 }
1135 else
1136 {
1137 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1138 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1139 }
1140 try
1141 {
1142 doGetFileCopyFile( srcFile, dest, file, report, options);
1143 }
1144 catch (Exception &e)
1145 {
1146 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1147 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1148 ZYPP_RETHROW(e);
1149 }
1150
1151 long httpReturnCode = 0;
1152 CURLcode infoRet = curl_easy_getinfo(_curl,
1153 CURLINFO_RESPONSE_CODE,
1154 &httpReturnCode);
1155 bool modified = true;
1156 if (infoRet == CURLE_OK)
1157 {
1158 DBG << "HTTP response: " + str::numstring(httpReturnCode);
1159 if ( httpReturnCode == 304
1160 || ( httpReturnCode == 213 && (_url.getScheme() == "ftp" || _url.getScheme() == "tftp") ) ) // not modified
1161 {
1162 DBG << " Not modified.";
1163 modified = false;
1164 }
1165 DBG << endl;
1166 }
1167 else
1168 {
1169 WAR << "Could not get the response code." << endl;
1170 }
1171
1172 if (modified || infoRet != CURLE_OK)
1173 {
1174 // apply umask
1175 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
1176 {
1177 ERR << "Failed to chmod file " << destNew << endl;
1178 }
1179
1180 file.resetDispose(); // we're going to close it manually here
1181 if ( ::fclose( file ) )
1182 {
1183 ERR << "Fclose failed for file '" << destNew << "'" << endl;
1184 ZYPP_THROW(MediaWriteException(destNew));
1185 }
1186
1187 // move the temp file into dest
1188 if ( rename( destNew, dest ) != 0 ) {
1189 ERR << "Rename failed" << endl;
1190 ZYPP_THROW(MediaWriteException(dest));
1191 }
1192 destNew.resetDispose(); // no more need to unlink it
1193 }
1194
1195 DBG << "done: " << PathInfo(dest) << endl;
1196}
1197
1199
1200void MediaCurl::doGetFileCopyFile( const OnMediaLocation & srcFile, const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1201{
1202 DBG << srcFile.filename().asString() << endl;
1203
1204 if(!_url.isValid())
1205 ZYPP_THROW(MediaBadUrlException(_url));
1206
1207 if(_url.getHost().empty())
1208 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
1209
1210 Url url(getFileUrl(srcFile.filename()));
1211
1212 DBG << "URL: " << url.asString() << endl;
1213 // Use URL without options and without username and passwd
1214 // (some proxies dislike them in the URL).
1215 // Curl seems to need the just scheme, hostname and a path;
1216 // the rest was already passed as curl options (in attachTo).
1217 Url curlUrl( clearQueryString(url) );
1218
1219 //
1220 // See also Bug #154197 and ftp url definition in RFC 1738:
1221 // The url "ftp://user@host/foo/bar/file" contains a path,
1222 // that is relative to the user's home.
1223 // The url "ftp://user@host//foo/bar/file" (or also with
1224 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1225 // contains an absolute path.
1226 //
1227 _lastRedirect.clear();
1228 std::string urlBuffer( curlUrl.asString());
1229 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1230 urlBuffer.c_str() );
1231 if ( ret != 0 ) {
1232 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1233 }
1234
1235 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1236 if ( ret != 0 ) {
1237 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1238 }
1239
1240 // Set callback and perform.
1241 internal::ProgressData progressData(_curl, _settings.timeout(), url, srcFile.downloadSize(), &report);
1242 if (!(options & OPTION_NO_REPORT_START))
1243 report->start(url, dest);
1244 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1245 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1246 }
1247
1248 ret = curl_easy_perform( _curl );
1249#if CURLVERSION_AT_LEAST(7,19,4)
1250 // bnc#692260: If the client sends a request with an If-Modified-Since header
1251 // with a future date for the server, the server may respond 200 sending a
1252 // zero size file.
1253 // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
1254 if ( ftell(file) == 0 && ret == 0 )
1255 {
1256 long httpReturnCode = 33;
1257 if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1258 {
1259 long conditionUnmet = 33;
1260 if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1261 {
1262 WAR << "TIMECONDITION unmet - retry without." << endl;
1263 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1264 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1265 ret = curl_easy_perform( _curl );
1266 }
1267 }
1268 }
1269#endif
1270
1271 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1272 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1273 }
1274
1275 if ( ret != 0 )
1276 {
1277 ERR << "curl error: " << ret << ": " << _curlError
1278 << ", temp file size " << ftell(file)
1279 << " bytes." << endl;
1280
1281 // the timeout is determined by the progress data object
1282 // which holds whether the timeout was reached or not,
1283 // otherwise it would be a user cancel
1284 try {
1285
1286 if ( progressData.fileSizeExceeded )
1287 ZYPP_THROW(MediaFileSizeExceededException(url, progressData._expectedFileSize));
1288
1289 evaluateCurlCode( srcFile.filename(), ret, progressData.reached );
1290 }
1291 catch ( const MediaException &e ) {
1292 // some error, we are not sure about file existence, rethrw
1293 ZYPP_RETHROW(e);
1294 }
1295 }
1296
1297#if DETECT_DIR_INDEX
1298 if (!ret && detectDirIndex())
1299 {
1300 ZYPP_THROW(MediaNotAFileException(_url, filename));
1301 }
1302#endif // DETECT_DIR_INDEX
1303}
1304
1306
1307void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
1308{
1309 filesystem::DirContent content;
1310 getDirInfo( content, dirname, /*dots*/false );
1311
1312 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1313 Pathname filename = dirname + it->name;
1314 int res = 0;
1315
1316 switch ( it->type ) {
1317 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
1319 getFile( OnMediaLocation( filename ) );
1320 break;
1321 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
1322 if ( recurse_r ) {
1323 getDir( filename, recurse_r );
1324 } else {
1325 res = assert_dir( localPath( filename ) );
1326 if ( res ) {
1327 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
1328 }
1329 }
1330 break;
1331 default:
1332 // don't provide devices, sockets, etc.
1333 break;
1334 }
1335 }
1336}
1337
1339
1340void MediaCurl::getDirInfo( std::list<std::string> & retlist,
1341 const Pathname & dirname, bool dots ) const
1342{
1343 getDirectoryYast( retlist, dirname, dots );
1344}
1345
1347
1349 const Pathname & dirname, bool dots ) const
1350{
1351 getDirectoryYast( retlist, dirname, dots );
1352}
1353
1355//
1356int MediaCurl::aliveCallback( void *clientp, double /*dltotal*/, double dlnow, double /*ultotal*/, double /*ulnow*/ )
1357{
1358 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1359 if( pdata )
1360 {
1361 // Do not propagate dltotal in alive callbacks. MultiCurl uses this to
1362 // prevent a percentage raise while downloading a metalink file. Download
1363 // activity however is indicated by propagating the download rate (via dlnow).
1364 pdata->updateStats( 0.0, dlnow );
1365 return pdata->reportProgress();
1366 }
1367 return 0;
1368}
1369
1370int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow )
1371{
1372 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1373 if( pdata )
1374 {
1375 // work around curl bug that gives us old data
1376 long httpReturnCode = 0;
1377 if ( curl_easy_getinfo( pdata->curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1378 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1379
1380 pdata->updateStats( dltotal, dlnow );
1381 return pdata->reportProgress();
1382 }
1383 return 0;
1384}
1385
1387{
1388 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>(clientp);
1389 return pdata ? pdata->curl : 0;
1390}
1391
1393
1394std::string MediaCurl::getAuthHint() const
1395{
1396 long auth_info = CURLAUTH_NONE;
1397
1398 CURLcode infoRet =
1399 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1400
1401 if(infoRet == CURLE_OK)
1402 {
1403 return CurlAuthData::auth_type_long2str(auth_info);
1404 }
1405
1406 return "";
1407}
1408
1413void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
1414{
1415 internal::ProgressData *data = reinterpret_cast<internal::ProgressData *>(clientp);
1416 if ( data ) {
1417 data->_expectedFileSize = expectedFileSize;
1418 }
1419}
1420
1422
1423bool MediaCurl::authenticate(const std::string & availAuthTypes, bool firstTry) const
1424{
1426 CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
1427 CurlAuthData_Ptr credentials;
1428
1429 // get stored credentials
1430 AuthData_Ptr cmcred = cm.getCred(_url);
1431
1432 if (cmcred && firstTry)
1433 {
1434 credentials.reset(new CurlAuthData(*cmcred));
1435 DBG << "got stored credentials:" << endl << *credentials << endl;
1436 }
1437 // if not found, ask user
1438 else
1439 {
1440
1441 CurlAuthData_Ptr curlcred;
1442 curlcred.reset(new CurlAuthData());
1444
1445 // preset the username if present in current url
1446 if (!_url.getUsername().empty() && firstTry)
1447 curlcred->setUsername(_url.getUsername());
1448 // if CM has found some credentials, preset the username from there
1449 else if (cmcred)
1450 curlcred->setUsername(cmcred->username());
1451
1452 // indicate we have no good credentials from CM
1453 cmcred.reset();
1454
1455 std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % _url.asString();
1456
1457 // set available authentication types from the exception
1458 // might be needed in prompt
1459 curlcred->setAuthType(availAuthTypes);
1460
1461 // ask user
1462 if (auth_report->prompt(_url, prompt_msg, *curlcred))
1463 {
1464 DBG << "callback answer: retry" << endl
1465 << "CurlAuthData: " << *curlcred << endl;
1466
1467 if (curlcred->valid())
1468 {
1469 credentials = curlcred;
1470 // if (credentials->username() != _url.getUsername())
1471 // _url.setUsername(credentials->username());
1479 }
1480 }
1481 else
1482 {
1483 DBG << "callback answer: cancel" << endl;
1484 }
1485 }
1486
1487 // set username and password
1488 if (credentials)
1489 {
1490 // HACK, why is this const?
1491 const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
1492 const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
1493
1494 // set username and password
1495 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
1496 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
1497
1498 // set available authentication types from the exception
1499 if (credentials->authType() == CURLAUTH_NONE)
1500 credentials->setAuthType(availAuthTypes);
1501
1502 // set auth type (seems this must be set _after_ setting the userpwd)
1503 if (credentials->authType() != CURLAUTH_NONE)
1504 {
1505 // FIXME: only overwrite if not empty?
1506 const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
1507 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
1508 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
1509 }
1510
1511 if (!cmcred)
1512 {
1513 credentials->setUrl(_url);
1514 cm.addCred(*credentials);
1515 cm.save();
1516 }
1517
1518 return true;
1519 }
1520
1521 return false;
1522}
1523
1524//need a out of line definiton, otherwise vtable is emitted for every translation unit
1526
1527 } // namespace media
1528} // namespace zypp
1529//
#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
TrueBool _guard
Definition: TargetImpl.cc:1563
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:1413
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:1356
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:1370
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:1090
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:1340
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:1200
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:1307
bool authenticate(const std::string &availAuthTypes, bool firstTry) const
Definition: MediaCurl.cc:1423
static CURL * progressCallback_getcurl(void *clientp)
Definition: MediaCurl.cc:1386
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:1394
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