00001
00002
00003
00004
00005
00006
00007
00008
00013 #include <iostream>
00014 #include <list>
00015
00016 #include "zypp/base/Logger.h"
00017 #include "zypp/ExternalProgram.h"
00018 #include "zypp/base/String.h"
00019 #include "zypp/base/Gettext.h"
00020 #include "zypp/base/Sysconfig.h"
00021 #include "zypp/base/Gettext.h"
00022
00023 #include "zypp/media/MediaCurl.h"
00024 #include "zypp/media/ProxyInfo.h"
00025 #include "zypp/media/MediaUserAuth.h"
00026 #include "zypp/media/CredentialManager.h"
00027 #include "zypp/media/CurlConfig.h"
00028 #include "zypp/thread/Once.h"
00029 #include "zypp/Target.h"
00030 #include "zypp/ZYppFactory.h"
00031 #include "zypp/ZConfig.h"
00032
00033 #include <cstdlib>
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <sys/mount.h>
00037 #include <errno.h>
00038 #include <dirent.h>
00039 #include <unistd.h>
00040 #include <boost/format.hpp>
00041
00042 #define DETECT_DIR_INDEX 0
00043 #define CONNECT_TIMEOUT 60
00044 #define TRANSFER_TIMEOUT_MAX 60 * 60
00045
00046 #define EXPLICITLY_NO_PROXY "_none_"
00047
00048 #undef CURLVERSION_AT_LEAST
00049 #define CURLVERSION_AT_LEAST(M,N,O) LIBCURL_VERSION_NUM >= ((((M)<<8)+(N))<<8)+(O)
00050
00051 using namespace std;
00052 using namespace zypp::base;
00053
00054 namespace
00055 {
00056 zypp::thread::OnceFlag g_InitOnceFlag = PTHREAD_ONCE_INIT;
00057 zypp::thread::OnceFlag g_FreeOnceFlag = PTHREAD_ONCE_INIT;
00058
00059 extern "C" void _do_free_once()
00060 {
00061 curl_global_cleanup();
00062 }
00063
00064 extern "C" void globalFreeOnce()
00065 {
00066 zypp::thread::callOnce(g_FreeOnceFlag, _do_free_once);
00067 }
00068
00069 extern "C" void _do_init_once()
00070 {
00071 CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
00072 if ( ret != 0 )
00073 {
00074 WAR << "curl global init failed" << endl;
00075 }
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 }
00086
00087 inline void globalInitOnce()
00088 {
00089 zypp::thread::callOnce(g_InitOnceFlag, _do_init_once);
00090 }
00091
00092 int log_curl(CURL *curl, curl_infotype info,
00093 char *ptr, size_t len, void *max_lvl)
00094 {
00095 std::string pfx(" ");
00096 long lvl = 0;
00097 switch( info)
00098 {
00099 case CURLINFO_TEXT: lvl = 1; pfx = "*"; break;
00100 case CURLINFO_HEADER_IN: lvl = 2; pfx = "<"; break;
00101 case CURLINFO_HEADER_OUT: lvl = 2; pfx = ">"; break;
00102 default: break;
00103 }
00104 if( lvl > 0 && max_lvl != NULL && lvl <= *((long *)max_lvl))
00105 {
00106 std::string msg(ptr, len);
00107 std::list<std::string> lines;
00108 std::list<std::string>::const_iterator line;
00109 zypp::str::split(msg, std::back_inserter(lines), "\r\n");
00110 for(line = lines.begin(); line != lines.end(); ++line)
00111 {
00112 DBG << pfx << " " << *line << endl;
00113 }
00114 }
00115 return 0;
00116 }
00117
00118 static size_t
00119 log_redirects_curl(
00120 void *ptr, size_t size, size_t nmemb, void *stream)
00121 {
00122
00123
00124 char * lstart = (char *)ptr, * lend = (char *)ptr;
00125 size_t pos = 0;
00126 size_t max = size * nmemb;
00127 while (pos + 1 < max)
00128 {
00129
00130 for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
00131
00132
00133 string line(lstart, lend);
00134 if (line.find("Location") != string::npos)
00135 {
00136 DBG << "redirecting to " << line << endl;
00137 return max;
00138 }
00139
00140
00141 if (pos + 1 < max)
00142 {
00143 ++lend;
00144 ++pos;
00145 }
00146 else
00147 break;
00148 }
00149
00150 return max;
00151 }
00152 }
00153
00154 namespace zypp {
00155 namespace media {
00156
00157 namespace {
00158 struct ProgressData
00159 {
00160 ProgressData(const long _timeout, const zypp::Url &_url = zypp::Url(),
00161 callback::SendReport<DownloadProgressReport> *_report=NULL)
00162 : timeout(_timeout)
00163 , reached(false)
00164 , report(_report)
00165 , drate_period(-1)
00166 , dload_period(0)
00167 , secs(0)
00168 , drate_avg(-1)
00169 , ltime( time(NULL))
00170 , dload( 0)
00171 , uload( 0)
00172 , url(_url)
00173 {}
00174 long timeout;
00175 bool reached;
00176 callback::SendReport<DownloadProgressReport> *report;
00177
00178 double drate_period;
00179
00180 double dload_period;
00181
00182 long secs;
00183
00184 double drate_avg;
00185
00186 time_t ltime;
00187
00188 double dload;
00189
00190 double uload;
00191 zypp::Url url;
00192 };
00193
00195
00196 inline void escape( string & str_r,
00197 const char char_r, const string & escaped_r ) {
00198 for ( string::size_type pos = str_r.find( char_r );
00199 pos != string::npos; pos = str_r.find( char_r, pos ) ) {
00200 str_r.replace( pos, 1, escaped_r );
00201 }
00202 }
00203
00204 inline string escapedPath( string path_r ) {
00205 escape( path_r, ' ', "%20" );
00206 return path_r;
00207 }
00208
00209 inline string unEscape( string text_r ) {
00210 char * tmp = curl_unescape( text_r.c_str(), 0 );
00211 string ret( tmp );
00212 curl_free( tmp );
00213 return ret;
00214 }
00215
00216 }
00217
00222 void fillSettingsFromUrl( const Url &url, TransferSettings &s )
00223 {
00224 std::string param(url.getQueryParam("timeout"));
00225 if( !param.empty())
00226 {
00227 long num = str::strtonum<long>(param);
00228 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
00229 s.setTimeout(num);
00230 }
00231
00232 if ( ! url.getUsername().empty() )
00233 {
00234 s.setUsername(url.getUsername());
00235 if ( url.getPassword().size() )
00236 s.setPassword(url.getPassword());
00237 }
00238 else
00239 {
00240
00241 if ( ( url.getScheme() == "ftp" || url.getScheme() == "tftp" ) && s.username().empty() )
00242 s.setAnonymousAuth();
00243 }
00244
00245 if ( url.getScheme() == "https" )
00246 {
00247 s.setVerifyPeerEnabled(false);
00248 s.setVerifyHostEnabled(false);
00249
00250 std::string verify( url.getQueryParam("ssl_verify"));
00251 if( verify.empty() ||
00252 verify == "yes")
00253 {
00254 s.setVerifyPeerEnabled(true);
00255 s.setVerifyHostEnabled(true);
00256 }
00257 else if( verify == "no")
00258 {
00259 s.setVerifyPeerEnabled(false);
00260 s.setVerifyHostEnabled(false);
00261 }
00262 else
00263 {
00264 std::vector<std::string> flags;
00265 std::vector<std::string>::const_iterator flag;
00266 str::split( verify, std::back_inserter(flags), ",");
00267 for(flag = flags.begin(); flag != flags.end(); ++flag)
00268 {
00269 if( *flag == "host")
00270 s.setVerifyHostEnabled(true);
00271 else if( *flag == "peer")
00272 s.setVerifyPeerEnabled(true);
00273 else
00274 ZYPP_THROW(MediaBadUrlException(url, "Unknown ssl_verify flag"));
00275 }
00276 }
00277 }
00278
00279 Pathname ca_path( url.getQueryParam("ssl_capath") );
00280 if( ! ca_path.empty())
00281 {
00282 if( !PathInfo(ca_path).isDir() || ! ca_path.absolute())
00283 ZYPP_THROW(MediaBadUrlException(url, "Invalid ssl_capath path"));
00284 else
00285 s.setCertificateAuthoritiesPath(ca_path);
00286 }
00287
00288 param = url.getQueryParam( "proxy" );
00289 if ( ! param.empty() )
00290 {
00291 if ( param == EXPLICITLY_NO_PROXY ) {
00292
00293
00294
00295
00296 s.setProxy(EXPLICITLY_NO_PROXY);
00297 s.setProxyEnabled(false);
00298 }
00299 else {
00300 string proxyport( url.getQueryParam( "proxyport" ) );
00301 if ( ! proxyport.empty() ) {
00302 param += ":" + proxyport;
00303 }
00304 s.setProxy(param);
00305 s.setProxyEnabled(true);
00306 }
00307 }
00308
00309 param = url.getQueryParam( "proxyuser" );
00310 if ( ! param.empty() )
00311 {
00312 s.setProxyUsername(param);
00313 s.setProxyPassword(url.getQueryParam( "proxypass" ));
00314 }
00315
00316
00317 param = url.getQueryParam("auth");
00318 if (!param.empty() && (url.getScheme() == "http" || url.getScheme() == "https"))
00319 {
00320 try
00321 {
00322 CurlAuthData::auth_type_str2long(param);
00323 }
00324 catch (MediaException & ex_r)
00325 {
00326 DBG << "Rethrowing as MediaUnauthorizedException.";
00327 ZYPP_THROW(MediaUnauthorizedException(url, ex_r.msg(), "", ""));
00328 }
00329 s.setAuthType(param);
00330 }
00331
00332
00333 param = url.getQueryParam("head_requests");
00334 if( !param.empty() && param == "no" )
00335 s.setHeadRequestsAllowed(false);
00336 }
00337
00342 void fillSettingsSystemProxy( const Url&url, TransferSettings &s )
00343 {
00344 ProxyInfo proxy_info;
00345 if ( proxy_info.useProxyFor( url ) )
00346 {
00347
00348
00349 try {
00350 Url u( proxy_info.proxy( url ) );
00351 s.setProxy( u.asString( url::ViewOption::WITH_SCHEME + url::ViewOption::WITH_HOST + url::ViewOption::WITH_PORT ) );
00352
00353 if ( s.proxyUsername().empty() )
00354 {
00355 s.setProxyUsername( u.getUsername( url::E_ENCODED ) );
00356 s.setProxyPassword( u.getPassword( url::E_ENCODED ) );
00357 }
00358 s.setProxyEnabled( true );
00359 }
00360 catch (...) {}
00361 }
00362 }
00363
00364 Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
00365
00370 static const char *const anonymousIdHeader()
00371 {
00372
00373
00374
00375
00376 static const std::string _value(
00377 str::trim( str::form(
00378 "X-ZYpp-AnonymousId: %s",
00379 Target::anonymousUniqueId( Pathname() ).c_str() ) )
00380 );
00381 return _value.c_str();
00382 }
00383
00388 static const char *const distributionFlavorHeader()
00389 {
00390
00391
00392
00393
00394 static const std::string _value(
00395 str::trim( str::form(
00396 "X-ZYpp-DistributionFlavor: %s",
00397 Target::distributionFlavor( Pathname() ).c_str() ) )
00398 );
00399 return _value.c_str();
00400 }
00401
00406 static const char *const agentString()
00407 {
00408
00409
00410
00411
00412 static const std::string _value(
00413 str::form(
00414 "ZYpp %s (curl %s) %s"
00415 , VERSION
00416 , curl_version_info(CURLVERSION_NOW)->version
00417 , Target::targetDistribution( Pathname() ).c_str()
00418 )
00419 );
00420 return _value.c_str();
00421 }
00422
00423
00424
00426 #define SET_OPTION(opt,val) do { \
00427 ret = curl_easy_setopt ( _curl, opt, val ); \
00428 if ( ret != 0) { \
00429 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \
00430 } \
00431 } while ( false )
00432
00433 #define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
00434 #define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
00435 #define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
00436
00437 MediaCurl::MediaCurl( const Url & url_r,
00438 const Pathname & attach_point_hint_r )
00439 : MediaHandler( url_r, attach_point_hint_r,
00440 "/",
00441 true ),
00442 _curl( NULL ),
00443 _customHeaders(0L)
00444 {
00445 _curlError[0] = '\0';
00446 _curlDebug = 0L;
00447
00448 MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00449
00450 globalInitOnce();
00451
00452 if( !attachPoint().empty())
00453 {
00454 PathInfo ainfo(attachPoint());
00455 Pathname apath(attachPoint() + "XXXXXX");
00456 char *atemp = ::strdup( apath.asString().c_str());
00457 char *atest = NULL;
00458 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
00459 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
00460 {
00461 WAR << "attach point " << ainfo.path()
00462 << " is not useable for " << url_r.getScheme() << endl;
00463 setAttachPoint("", true);
00464 }
00465 else if( atest != NULL)
00466 ::rmdir(atest);
00467
00468 if( atemp != NULL)
00469 ::free(atemp);
00470 }
00471 }
00472
00473 Url MediaCurl::clearQueryString(const Url &url) const
00474 {
00475 Url curlUrl (url);
00476 curlUrl.setUsername( "" );
00477 curlUrl.setPassword( "" );
00478 curlUrl.setPathParams( "" );
00479 curlUrl.setFragment( "" );
00480 curlUrl.delQueryParam("cookies");
00481 curlUrl.delQueryParam("proxy");
00482 curlUrl.delQueryParam("proxyport");
00483 curlUrl.delQueryParam("proxyuser");
00484 curlUrl.delQueryParam("proxypass");
00485 curlUrl.delQueryParam("ssl_capath");
00486 curlUrl.delQueryParam("ssl_verify");
00487 curlUrl.delQueryParam("timeout");
00488 curlUrl.delQueryParam("auth");
00489 curlUrl.delQueryParam("username");
00490 curlUrl.delQueryParam("password");
00491 curlUrl.delQueryParam("mediahandler");
00492 return curlUrl;
00493 }
00494
00495 TransferSettings & MediaCurl::settings()
00496 {
00497 return _settings;
00498 }
00499
00500
00501 void MediaCurl::setCookieFile( const Pathname &fileName )
00502 {
00503 _cookieFile = fileName;
00504 }
00505
00507
00508 void MediaCurl::checkProtocol(const Url &url) const
00509 {
00510 curl_version_info_data *curl_info = NULL;
00511 curl_info = curl_version_info(CURLVERSION_NOW);
00512
00513 if (curl_info->protocols)
00514 {
00515 const char * const *proto;
00516 std::string scheme( url.getScheme());
00517 bool found = false;
00518 for(proto=curl_info->protocols; !found && *proto; ++proto)
00519 {
00520 if( scheme == std::string((const char *)*proto))
00521 found = true;
00522 }
00523 if( !found)
00524 {
00525 std::string msg("Unsupported protocol '");
00526 msg += scheme;
00527 msg += "'";
00528 ZYPP_THROW(MediaBadUrlException(_url, msg));
00529 }
00530 }
00531 }
00532
00533 void MediaCurl::setupEasy()
00534 {
00535 {
00536 char *ptr = getenv("ZYPP_MEDIA_CURL_DEBUG");
00537 _curlDebug = (ptr && *ptr) ? str::strtonum<long>( ptr) : 0L;
00538 if( _curlDebug > 0)
00539 {
00540 curl_easy_setopt( _curl, CURLOPT_VERBOSE, 1L);
00541 curl_easy_setopt( _curl, CURLOPT_DEBUGFUNCTION, log_curl);
00542 curl_easy_setopt( _curl, CURLOPT_DEBUGDATA, &_curlDebug);
00543 }
00544 }
00545
00546 curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
00547 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
00548 if ( ret != 0 ) {
00549 ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
00550 }
00551
00552 SET_OPTION(CURLOPT_FAILONERROR, 1L);
00553 SET_OPTION(CURLOPT_NOSIGNAL, 1L);
00554
00555
00556
00557 TransferSettings vol_settings(_settings);
00558
00559
00560 vol_settings.addHeader(anonymousIdHeader());
00561 vol_settings.addHeader(distributionFlavorHeader());
00562 vol_settings.addHeader("Pragma:");
00563
00564 _settings.setTimeout(ZConfig::instance().download_transfer_timeout());
00565 _settings.setConnectTimeout(CONNECT_TIMEOUT);
00566
00567 _settings.setUserAgentString(agentString());
00568
00569
00570 try
00571 {
00572 fillSettingsFromUrl(_url, _settings);
00573 }
00574 catch ( const MediaException &e )
00575 {
00576 disconnectFrom();
00577 ZYPP_RETHROW(e);
00578 }
00579
00580 if ( _settings.proxy().empty() )
00581 {
00582
00583 fillSettingsSystemProxy(_url, _settings);
00584 }
00585
00589 SET_OPTION(CURLOPT_CONNECTTIMEOUT, _settings.connectTimeout());
00590
00591
00592
00593 SET_OPTION(CURLOPT_FOLLOWLOCATION, 1L);
00594
00595 SET_OPTION(CURLOPT_MAXREDIRS, 6L);
00596
00597 if ( _url.getScheme() == "https" )
00598 {
00599 #if CURLVERSION_AT_LEAST(7,19,4)
00600
00601 SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
00602 #endif
00603
00604 if( _settings.verifyPeerEnabled() ||
00605 _settings.verifyHostEnabled() )
00606 {
00607 SET_OPTION(CURLOPT_CAPATH, _settings.certificateAuthoritiesPath().c_str());
00608 }
00609
00610 #ifdef CURLSSLOPT_ALLOW_BEAST
00611
00612 ret = curl_easy_setopt( _curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
00613
00614 #endif
00615 SET_OPTION(CURLOPT_SSL_VERIFYPEER, _settings.verifyPeerEnabled() ? 1L : 0L);
00616 SET_OPTION(CURLOPT_SSL_VERIFYHOST, _settings.verifyHostEnabled() ? 2L : 0L);
00617 }
00618
00619 SET_OPTION(CURLOPT_USERAGENT, _settings.userAgentString().c_str() );
00620
00621
00622
00623
00624
00625
00626
00627
00628 if ( _settings.userPassword().size() )
00629 {
00630 SET_OPTION(CURLOPT_USERPWD, _settings.userPassword().c_str());
00631 string use_auth = _settings.authType();
00632 if (use_auth.empty())
00633 use_auth = "digest,basic";
00634 long auth = CurlAuthData::auth_type_str2long(use_auth);
00635 if( auth != CURLAUTH_NONE)
00636 {
00637 DBG << "Enabling HTTP authentication methods: " << use_auth
00638 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
00639 SET_OPTION(CURLOPT_HTTPAUTH, auth);
00640 }
00641 }
00642
00643 if ( _settings.proxyEnabled() && ! _settings.proxy().empty() )
00644 {
00645 DBG << "Proxy: '" << _settings.proxy() << "'" << endl;
00646 SET_OPTION(CURLOPT_PROXY, _settings.proxy().c_str());
00647 SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
00648
00649
00650
00651
00652
00653
00654
00655 string proxyuserpwd = _settings.proxyUserPassword();
00656
00657 if ( proxyuserpwd.empty() )
00658 {
00659 CurlConfig curlconf;
00660 CurlConfig::parseConfig(curlconf);
00661 if ( curlconf.proxyuserpwd.empty() )
00662 DBG << "Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
00663 else
00664 {
00665 proxyuserpwd = curlconf.proxyuserpwd;
00666 DBG << "Proxy: using proxy-user from ~/.curlrc" << endl;
00667 }
00668 }
00669 else
00670 {
00671 DBG << "Proxy: using provided proxy-user '" << _settings.proxyUsername() << "'" << endl;
00672 }
00673
00674 if ( ! proxyuserpwd.empty() )
00675 {
00676 SET_OPTION(CURLOPT_PROXYUSERPWD, unEscape( proxyuserpwd ).c_str());
00677 }
00678 }
00679 #if CURLVERSION_AT_LEAST(7,19,4)
00680 else if ( _settings.proxy() == EXPLICITLY_NO_PROXY )
00681 {
00682
00683
00684 DBG << "Proxy: explicitly NOPROXY" << endl;
00685 SET_OPTION(CURLOPT_NOPROXY, "*");
00686 }
00687 #endif
00688 else
00689 {
00690
00691 DBG << "Proxy: not explicitly set" << endl;
00692 }
00693
00695 if ( _settings.minDownloadSpeed() != 0 )
00696 {
00697 SET_OPTION(CURLOPT_LOW_SPEED_LIMIT, _settings.minDownloadSpeed());
00698
00699 SET_OPTION(CURLOPT_LOW_SPEED_TIME, 10L);
00700 }
00701
00702 #if CURLVERSION_AT_LEAST(7,15,5)
00703 if ( _settings.maxDownloadSpeed() != 0 )
00704 SET_OPTION_OFFT(CURLOPT_MAX_RECV_SPEED_LARGE, _settings.maxDownloadSpeed());
00705 #endif
00706
00707
00708
00709
00710 _currentCookieFile = _cookieFile.asString();
00711 if ( str::strToBool( _url.getQueryParam( "cookies" ), true ) )
00712 SET_OPTION(CURLOPT_COOKIEFILE, _currentCookieFile.c_str() );
00713 else
00714 MIL << "No cookies requested" << endl;
00715 SET_OPTION(CURLOPT_COOKIEJAR, _currentCookieFile.c_str() );
00716 SET_OPTION(CURLOPT_PROGRESSFUNCTION, &progressCallback );
00717 SET_OPTION(CURLOPT_NOPROGRESS, 0L);
00718
00719 #if CURLVERSION_AT_LEAST(7,18,0)
00720
00721 SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
00722 #endif
00723
00724 for ( TransferSettings::Headers::const_iterator it = vol_settings.headersBegin();
00725 it != vol_settings.headersEnd();
00726 ++it )
00727 {
00728 MIL << "HEADER " << *it << std::endl;
00729
00730 _customHeaders = curl_slist_append(_customHeaders, it->c_str());
00731 if ( !_customHeaders )
00732 ZYPP_THROW(MediaCurlInitException(_url));
00733 }
00734
00735 SET_OPTION(CURLOPT_HTTPHEADER, _customHeaders);
00736 }
00737
00739
00740
00741 void MediaCurl::attachTo (bool next)
00742 {
00743 if ( next )
00744 ZYPP_THROW(MediaNotSupportedException(_url));
00745
00746 if ( !_url.isValid() )
00747 ZYPP_THROW(MediaBadUrlException(_url));
00748
00749 checkProtocol(_url);
00750 if( !isUseableAttachPoint(attachPoint()))
00751 {
00752 std::string mountpoint = createAttachPoint().asString();
00753
00754 if( mountpoint.empty())
00755 ZYPP_THROW( MediaBadAttachPointException(url()));
00756
00757 setAttachPoint( mountpoint, true);
00758 }
00759
00760 disconnectFrom();
00761 _curl = curl_easy_init();
00762 if ( !_curl ) {
00763 ZYPP_THROW(MediaCurlInitException(_url));
00764 }
00765 try
00766 {
00767 setupEasy();
00768 }
00769 catch (Exception & ex)
00770 {
00771 disconnectFrom();
00772 ZYPP_RETHROW(ex);
00773 }
00774
00775
00776 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
00777 setMediaSource(media);
00778 }
00779
00780 bool
00781 MediaCurl::checkAttachPoint(const Pathname &apoint) const
00782 {
00783 return MediaHandler::checkAttachPoint( apoint, true, true);
00784 }
00785
00787
00788 void MediaCurl::disconnectFrom()
00789 {
00790 if ( _customHeaders )
00791 {
00792 curl_slist_free_all(_customHeaders);
00793 _customHeaders = 0L;
00794 }
00795
00796 if ( _curl )
00797 {
00798 curl_easy_cleanup( _curl );
00799 _curl = NULL;
00800 }
00801 }
00802
00804
00805 void MediaCurl::releaseFrom( const std::string & ejectDev )
00806 {
00807 disconnect();
00808 }
00809
00810 Url MediaCurl::getFileUrl( const Pathname & filename_r ) const
00811 {
00812 std::string path( _url.getPathName() );
00813
00814
00815
00816
00817
00818
00819 if ( path.empty() || path == "/" )
00820 {
00821 path = filename_r.absolutename().asString();
00822 }
00823 else if ( *path.rbegin() == '/' )
00824 {
00825 path += filename_r.absolutename().asString().substr(1);
00826 }
00827 else
00828 {
00829 path += filename_r.absolutename().asString();
00830 }
00831 Url newurl( _url );
00832 newurl.setPathName( path );
00833 return newurl;
00834 }
00835
00837
00838 void MediaCurl::getFile( const Pathname & filename ) const
00839 {
00840
00841
00842 getFileCopy(filename, localPath(filename).absolutename());
00843 }
00844
00846
00847 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
00848 {
00849 callback::SendReport<DownloadProgressReport> report;
00850
00851 Url fileurl(getFileUrl(filename));
00852
00853 bool retry = false;
00854
00855 do
00856 {
00857 try
00858 {
00859 doGetFileCopy(filename, target, report);
00860 retry = false;
00861 }
00862
00863 catch (MediaUnauthorizedException & ex_r)
00864 {
00865 if(authenticate(ex_r.hint(), !retry))
00866 retry = true;
00867 else
00868 {
00869 report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
00870 ZYPP_RETHROW(ex_r);
00871 }
00872 }
00873
00874 catch (MediaException & excpt_r)
00875 {
00876
00877 report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserHistory());
00878 ZYPP_RETHROW(excpt_r);
00879 }
00880 }
00881 while (retry);
00882
00883 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
00884 }
00885
00887
00888 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
00889 {
00890 bool retry = false;
00891
00892 do
00893 {
00894 try
00895 {
00896 return doGetDoesFileExist( filename );
00897 }
00898
00899 catch (MediaUnauthorizedException & ex_r)
00900 {
00901 if(authenticate(ex_r.hint(), !retry))
00902 retry = true;
00903 else
00904 ZYPP_RETHROW(ex_r);
00905 }
00906
00907 catch (MediaException & excpt_r)
00908 {
00909 ZYPP_RETHROW(excpt_r);
00910 }
00911 }
00912 while (retry);
00913
00914 return false;
00915 }
00916
00918
00919 void MediaCurl::evaluateCurlCode( const Pathname &filename,
00920 CURLcode code,
00921 bool timeout_reached ) const
00922 {
00923 if ( code != 0 )
00924 {
00925 Url url;
00926 if (filename.empty())
00927 url = _url;
00928 else
00929 url = getFileUrl(filename);
00930 std::string err;
00931 try
00932 {
00933 switch ( code )
00934 {
00935 case CURLE_UNSUPPORTED_PROTOCOL:
00936 case CURLE_URL_MALFORMAT:
00937 case CURLE_URL_MALFORMAT_USER:
00938 err = " Bad URL";
00939 break;
00940 case CURLE_LOGIN_DENIED:
00941 ZYPP_THROW(
00942 MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
00943 break;
00944 case CURLE_HTTP_RETURNED_ERROR:
00945 {
00946 long httpReturnCode = 0;
00947 CURLcode infoRet = curl_easy_getinfo( _curl,
00948 CURLINFO_RESPONSE_CODE,
00949 &httpReturnCode );
00950 if ( infoRet == CURLE_OK )
00951 {
00952 string msg = "HTTP response: " + str::numstring( httpReturnCode );
00953 switch ( httpReturnCode )
00954 {
00955 case 401:
00956 {
00957 string auth_hint = getAuthHint();
00958
00959 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
00960 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
00961
00962 ZYPP_THROW(MediaUnauthorizedException(
00963 url, "Login failed.", _curlError, auth_hint
00964 ));
00965 }
00966
00967 case 503:
00968 ZYPP_THROW(MediaTemporaryProblemException(url));
00969 case 504:
00970 ZYPP_THROW(MediaTimeoutException(url));
00971 case 403:
00972 {
00973 string msg403;
00974 if (url.asString().find("novell.com") != string::npos)
00975 msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
00976 ZYPP_THROW(MediaForbiddenException(url, msg403));
00977 }
00978 case 404:
00979 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
00980 }
00981
00982 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00983 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00984 }
00985 else
00986 {
00987 string msg = "Unable to retrieve HTTP response:";
00988 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00989 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00990 }
00991 }
00992 break;
00993 case CURLE_FTP_COULDNT_RETR_FILE:
00994 #if CURLVERSION_AT_LEAST(7,16,0)
00995 case CURLE_REMOTE_FILE_NOT_FOUND:
00996 #endif
00997 case CURLE_FTP_ACCESS_DENIED:
00998 case CURLE_TFTP_NOTFOUND:
00999 err = "File not found";
01000 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01001 break;
01002 case CURLE_BAD_PASSWORD_ENTERED:
01003 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01004 err = "Login failed";
01005 break;
01006 case CURLE_COULDNT_RESOLVE_PROXY:
01007 case CURLE_COULDNT_RESOLVE_HOST:
01008 case CURLE_COULDNT_CONNECT:
01009 case CURLE_FTP_CANT_GET_HOST:
01010 err = "Connection failed";
01011 break;
01012 case CURLE_WRITE_ERROR:
01013 err = "Write error";
01014 break;
01015 case CURLE_PARTIAL_FILE:
01016 case CURLE_OPERATION_TIMEDOUT:
01017 timeout_reached = true;
01018
01019 case CURLE_ABORTED_BY_CALLBACK:
01020 if( timeout_reached )
01021 {
01022 err = "Timeout reached";
01023 ZYPP_THROW(MediaTimeoutException(url));
01024 }
01025 else
01026 {
01027 err = "User abort";
01028 }
01029 break;
01030 case CURLE_SSL_PEER_CERTIFICATE:
01031 default:
01032 err = "Unrecognized error";
01033 break;
01034 }
01035
01036
01037 ZYPP_THROW(MediaCurlException(url, err, _curlError));
01038 }
01039 catch (const MediaException & excpt_r)
01040 {
01041 ZYPP_RETHROW(excpt_r);
01042 }
01043 }
01044 else
01045 {
01046
01047 }
01048 }
01049
01051
01052 bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
01053 {
01054 DBG << filename.asString() << endl;
01055
01056 if(!_url.isValid())
01057 ZYPP_THROW(MediaBadUrlException(_url));
01058
01059 if(_url.getHost().empty())
01060 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
01061
01062 Url url(getFileUrl(filename));
01063
01064 DBG << "URL: " << url.asString() << endl;
01065
01066
01067
01068
01069 Url curlUrl( clearQueryString(url) );
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079 string urlBuffer( curlUrl.asString());
01080 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
01081 urlBuffer.c_str() );
01082 if ( ret != 0 ) {
01083 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01084 }
01085
01086
01087
01088
01089
01090
01091 if ( (_url.getScheme() == "http" || _url.getScheme() == "https") &&
01092 _settings.headRequestsAllowed() )
01093 ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
01094 else
01095 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
01096
01097 if ( ret != 0 ) {
01098 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
01099 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
01100
01101
01102
01103
01104
01105 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
01106 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01107 }
01108
01109 FILE *file = ::fopen( "/dev/null", "w" );
01110 if ( !file ) {
01111 ERR << "fopen failed for /dev/null" << endl;
01112 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
01113 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
01114
01115
01116
01117
01118
01119 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
01120 if ( ret != 0 ) {
01121 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01122 }
01123 ZYPP_THROW(MediaWriteException("/dev/null"));
01124 }
01125
01126 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
01127 if ( ret != 0 ) {
01128 ::fclose(file);
01129 std::string err( _curlError);
01130 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
01131 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
01132
01133
01134
01135
01136
01137 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
01138 if ( ret != 0 ) {
01139 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01140 }
01141 ZYPP_THROW(MediaCurlSetOptException(url, err));
01142 }
01143
01144 CURLcode ok = curl_easy_perform( _curl );
01145 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
01146
01147
01148 if ( _url.getScheme() == "http" || _url.getScheme() == "https" )
01149 {
01150 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
01151 if ( ret != 0 ) {
01152 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01153 }
01154
01155
01156
01157
01158
01159
01160 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L);
01161 if ( ret != 0 ) {
01162 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01163 }
01164
01165 }
01166 else
01167 {
01168
01169 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL);
01170 if ( ret != 0 ) {
01171 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01172 }
01173 }
01174
01175
01176 if ( ok != 0 )
01177 ::fclose(file);
01178
01179
01180
01181
01182 try {
01183 evaluateCurlCode( filename, ok, true );
01184 }
01185 catch ( const MediaFileNotFoundException &e ) {
01186
01187 return false;
01188 }
01189 catch ( const MediaException &e ) {
01190
01191 ZYPP_RETHROW(e);
01192 }
01193
01194 return ( ok == CURLE_OK );
01195 }
01196
01198
01199
01200 #if DETECT_DIR_INDEX
01201 bool MediaCurl::detectDirIndex() const
01202 {
01203 if(_url.getScheme() != "http" && _url.getScheme() != "https")
01204 return false;
01205
01206
01207
01208
01209
01210
01211
01212
01213 bool not_a_file = false;
01214 char *ptr = NULL;
01215 CURLcode ret = curl_easy_getinfo( _curl,
01216 CURLINFO_EFFECTIVE_URL,
01217 &ptr);
01218 if ( ret == CURLE_OK && ptr != NULL)
01219 {
01220 try
01221 {
01222 Url eurl( ptr);
01223 std::string path( eurl.getPathName());
01224 if( !path.empty() && path != "/" && *path.rbegin() == '/')
01225 {
01226 DBG << "Effective url ("
01227 << eurl
01228 << ") seems to provide the index of a directory"
01229 << endl;
01230 not_a_file = true;
01231 }
01232 }
01233 catch( ... )
01234 {}
01235 }
01236 return not_a_file;
01237 }
01238 #endif
01239
01241
01242 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
01243 {
01244 Pathname dest = target.absolutename();
01245 if( assert_dir( dest.dirname() ) )
01246 {
01247 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
01248 Url url(getFileUrl(filename));
01249 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
01250 }
01251 string destNew = target.asString() + ".new.zypp.XXXXXX";
01252 char *buf = ::strdup( destNew.c_str());
01253 if( !buf)
01254 {
01255 ERR << "out of memory for temp file name" << endl;
01256 Url url(getFileUrl(filename));
01257 ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
01258 }
01259
01260 int tmp_fd = ::mkstemp( buf );
01261 if( tmp_fd == -1)
01262 {
01263 free( buf);
01264 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
01265 ZYPP_THROW(MediaWriteException(destNew));
01266 }
01267 destNew = buf;
01268 free( buf);
01269
01270 FILE *file = ::fdopen( tmp_fd, "w" );
01271 if ( !file ) {
01272 ::close( tmp_fd);
01273 filesystem::unlink( destNew );
01274 ERR << "fopen failed for file '" << destNew << "'" << endl;
01275 ZYPP_THROW(MediaWriteException(destNew));
01276 }
01277
01278 DBG << "dest: " << dest << endl;
01279 DBG << "temp: " << destNew << endl;
01280
01281
01282 if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
01283 {
01284 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
01285 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
01286 }
01287 else
01288 {
01289 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
01290 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
01291 }
01292 try
01293 {
01294 doGetFileCopyFile(filename, dest, file, report, options);
01295 }
01296 catch (Exception &e)
01297 {
01298 ::fclose( file );
01299 filesystem::unlink( destNew );
01300 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
01301 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
01302 ZYPP_RETHROW(e);
01303 }
01304
01305 long httpReturnCode = 0;
01306 CURLcode infoRet = curl_easy_getinfo(_curl,
01307 CURLINFO_RESPONSE_CODE,
01308 &httpReturnCode);
01309 bool modified = true;
01310 if (infoRet == CURLE_OK)
01311 {
01312 DBG << "HTTP response: " + str::numstring(httpReturnCode);
01313 if ( httpReturnCode == 304
01314 || ( httpReturnCode == 213 && (_url.getScheme() == "ftp" || _url.getScheme() == "tftp") ) )
01315 {
01316 DBG << " Not modified.";
01317 modified = false;
01318 }
01319 DBG << endl;
01320 }
01321 else
01322 {
01323 WAR << "Could not get the reponse code." << endl;
01324 }
01325
01326 if (modified || infoRet != CURLE_OK)
01327 {
01328
01329 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
01330 {
01331 ERR << "Failed to chmod file " << destNew << endl;
01332 }
01333 if (::fclose( file ))
01334 {
01335 ERR << "Fclose failed for file '" << destNew << "'" << endl;
01336 ZYPP_THROW(MediaWriteException(destNew));
01337 }
01338
01339 if ( rename( destNew, dest ) != 0 ) {
01340 ERR << "Rename failed" << endl;
01341 ZYPP_THROW(MediaWriteException(dest));
01342 }
01343 }
01344 else
01345 {
01346
01347 ::fclose( file );
01348 filesystem::unlink( destNew );
01349 }
01350
01351 DBG << "done: " << PathInfo(dest) << endl;
01352 }
01353
01355
01356 void MediaCurl::doGetFileCopyFile( const Pathname & filename , const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
01357 {
01358 DBG << filename.asString() << endl;
01359
01360 if(!_url.isValid())
01361 ZYPP_THROW(MediaBadUrlException(_url));
01362
01363 if(_url.getHost().empty())
01364 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
01365
01366 Url url(getFileUrl(filename));
01367
01368 DBG << "URL: " << url.asString() << endl;
01369
01370
01371
01372
01373 Url curlUrl( clearQueryString(url) );
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383 string urlBuffer( curlUrl.asString());
01384 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
01385 urlBuffer.c_str() );
01386 if ( ret != 0 ) {
01387 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01388 }
01389
01390 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
01391 if ( ret != 0 ) {
01392 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01393 }
01394
01395
01396 ProgressData progressData(_settings.timeout(), url, &report);
01397 if (!(options & OPTION_NO_REPORT_START))
01398 report->start(url, dest);
01399 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
01400 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01401 }
01402
01403 ret = curl_easy_perform( _curl );
01404 #if CURLVERSION_AT_LEAST(7,19,4)
01405
01406
01407
01408
01409 if ( ftell(file) == 0 && ret == 0 )
01410 {
01411 long httpReturnCode = 33;
01412 if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
01413 {
01414 long conditionUnmet = 33;
01415 if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
01416 {
01417 WAR << "TIMECONDITION unmet - retry without." << endl;
01418 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
01419 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
01420 ret = curl_easy_perform( _curl );
01421 }
01422 }
01423 }
01424 #endif
01425
01426 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
01427 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01428 }
01429
01430 if ( ret != 0 )
01431 {
01432 ERR << "curl error: " << ret << ": " << _curlError
01433 << ", temp file size " << ftell(file)
01434 << " bytes." << endl;
01435
01436
01437
01438
01439 try {
01440 evaluateCurlCode( filename, ret, progressData.reached);
01441 }
01442 catch ( const MediaException &e ) {
01443
01444 ZYPP_RETHROW(e);
01445 }
01446 }
01447
01448 #if DETECT_DIR_INDEX
01449 if (!ret && detectDirIndex())
01450 {
01451 ZYPP_THROW(MediaNotAFileException(_url, filename));
01452 }
01453 #endif // DETECT_DIR_INDEX
01454 }
01455
01457
01458 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
01459 {
01460 filesystem::DirContent content;
01461 getDirInfo( content, dirname, false );
01462
01463 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
01464 Pathname filename = dirname + it->name;
01465 int res = 0;
01466
01467 switch ( it->type ) {
01468 case filesystem::FT_NOT_AVAIL:
01469 case filesystem::FT_FILE:
01470 getFile( filename );
01471 break;
01472 case filesystem::FT_DIR:
01473 if ( recurse_r ) {
01474 getDir( filename, recurse_r );
01475 } else {
01476 res = assert_dir( localPath( filename ) );
01477 if ( res ) {
01478 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
01479 }
01480 }
01481 break;
01482 default:
01483
01484 break;
01485 }
01486 }
01487 }
01488
01490
01491 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01492 const Pathname & dirname, bool dots ) const
01493 {
01494 getDirectoryYast( retlist, dirname, dots );
01495 }
01496
01498
01499 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01500 const Pathname & dirname, bool dots ) const
01501 {
01502 getDirectoryYast( retlist, dirname, dots );
01503 }
01504
01506
01507 int MediaCurl::progressCallback( void *clientp,
01508 double dltotal, double dlnow,
01509 double ultotal, double ulnow)
01510 {
01511 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01512 if( pdata)
01513 {
01514 time_t now = time(NULL);
01515 if( now > 0)
01516 {
01517
01518
01519 if( pdata->ltime <= 0 || pdata->ltime > now)
01520 {
01521 pdata->ltime = now;
01522 }
01523
01524
01525
01526 time_t dif = 0;
01527 if (dlnow > 0 || ulnow > 0)
01528 {
01529 dif = (now - pdata->ltime);
01530 dif = dif > 0 ? dif : 0;
01531
01532 pdata->secs += dif;
01533 }
01534
01535
01536
01537
01538
01540
01541 if ( pdata->secs > 1 && (dif > 0 || dlnow == dltotal ))
01542 pdata->drate_avg = (dlnow / pdata->secs);
01543
01544 if ( dif > 0 )
01545 {
01546 pdata->drate_period = ((dlnow - pdata->dload_period) / dif);
01547 pdata->dload_period = dlnow;
01548 }
01549 }
01550
01551
01552 if( pdata->report)
01553 {
01554 if (!(*(pdata->report))->progress(int( dltotal ? dlnow * 100 / dltotal : 0 ),
01555 pdata->url,
01556 pdata->drate_avg,
01557 pdata->drate_period))
01558 {
01559 return 1;
01560 }
01561 }
01562
01563
01564 if( pdata->timeout > 0)
01565 {
01566 if( now > 0)
01567 {
01568 bool progress = false;
01569
01570
01571 if( dlnow != pdata->dload)
01572 {
01573 progress = true;
01574 pdata->dload = dlnow;
01575 pdata->ltime = now;
01576 }
01577
01578 if( ulnow != pdata->uload)
01579 {
01580 progress = true;
01581 pdata->uload = ulnow;
01582 pdata->ltime = now;
01583 }
01584
01585 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01586 {
01587 pdata->reached = true;
01588 return 1;
01589 }
01590 }
01591 }
01592 }
01593 return 0;
01594 }
01595
01597
01598 string MediaCurl::getAuthHint() const
01599 {
01600 long auth_info = CURLAUTH_NONE;
01601
01602 CURLcode infoRet =
01603 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
01604
01605 if(infoRet == CURLE_OK)
01606 {
01607 return CurlAuthData::auth_type_long2str(auth_info);
01608 }
01609
01610 return "";
01611 }
01612
01614
01615 bool MediaCurl::authenticate(const string & availAuthTypes, bool firstTry) const
01616 {
01618 Target_Ptr target = zypp::getZYpp()->getTarget();
01619 CredentialManager cm(CredManagerOptions(target ? target->root() : ""));
01620 CurlAuthData_Ptr credentials;
01621
01622
01623 AuthData_Ptr cmcred = cm.getCred(_url);
01624
01625 if (cmcred && firstTry)
01626 {
01627 credentials.reset(new CurlAuthData(*cmcred));
01628 DBG << "got stored credentials:" << endl << *credentials << endl;
01629 }
01630
01631 else
01632 {
01633
01634 CurlAuthData_Ptr curlcred;
01635 curlcred.reset(new CurlAuthData());
01636 callback::SendReport<AuthenticationReport> auth_report;
01637
01638
01639 if (!_url.getUsername().empty() && firstTry)
01640 curlcred->setUsername(_url.getUsername());
01641
01642 else if (cmcred)
01643 curlcred->setUsername(cmcred->username());
01644
01645
01646 cmcred.reset();
01647
01648 string prompt_msg = boost::str(boost::format(
01650 _("Authentication required for '%s'")) % _url.asString());
01651
01652
01653
01654 curlcred->setAuthType(availAuthTypes);
01655
01656
01657 if (auth_report->prompt(_url, prompt_msg, *curlcred))
01658 {
01659 DBG << "callback answer: retry" << endl
01660 << "CurlAuthData: " << *curlcred << endl;
01661
01662 if (curlcred->valid())
01663 {
01664 credentials = curlcred;
01665
01666
01674 }
01675 }
01676 else
01677 {
01678 DBG << "callback answer: cancel" << endl;
01679 }
01680 }
01681
01682
01683 if (credentials)
01684 {
01685
01686 const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
01687 const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
01688
01689
01690 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
01691 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
01692
01693
01694 if (credentials->authType() == CURLAUTH_NONE)
01695 credentials->setAuthType(availAuthTypes);
01696
01697
01698 if (credentials->authType() != CURLAUTH_NONE)
01699 {
01700
01701 const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
01702 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
01703 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
01704 }
01705
01706 if (!cmcred)
01707 {
01708 credentials->setUrl(_url);
01709 cm.addCred(*credentials);
01710 cm.save();
01711 }
01712
01713 return true;
01714 }
01715
01716 return false;
01717 }
01718
01719
01720 }
01721 }
01722