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