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/Sysconfig.h"
00020 #include "zypp/base/Gettext.h"
00021
00022 #include "zypp/media/MediaCurl.h"
00023 #include "zypp/media/proxyinfo/ProxyInfos.h"
00024 #include "zypp/media/ProxyInfo.h"
00025 #include "zypp/media/MediaUserAuth.h"
00026 #include "zypp/media/CurlConfig.h"
00027 #include "zypp/thread/Once.h"
00028 #include <cstdlib>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 #include <sys/mount.h>
00032 #include <errno.h>
00033 #include <dirent.h>
00034 #include <unistd.h>
00035 #include <boost/format.hpp>
00036
00037 #define DETECT_DIR_INDEX 0
00038 #define CONNECT_TIMEOUT 60
00039 #define TRANSFER_TIMEOUT 60 * 3
00040 #define TRANSFER_TIMEOUT_MAX 60 * 60
00041
00042
00043 using namespace std;
00044 using namespace zypp::base;
00045
00046 namespace
00047 {
00048 zypp::thread::OnceFlag g_InitOnceFlag = PTHREAD_ONCE_INIT;
00049 zypp::thread::OnceFlag g_FreeOnceFlag = PTHREAD_ONCE_INIT;
00050
00051 extern "C" void _do_free_once()
00052 {
00053 curl_global_cleanup();
00054 }
00055
00056 extern "C" void globalFreeOnce()
00057 {
00058 zypp::thread::callOnce(g_FreeOnceFlag, _do_free_once);
00059 }
00060
00061 extern "C" void _do_init_once()
00062 {
00063 CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
00064 if ( ret != 0 )
00065 {
00066 WAR << "curl global init failed" << endl;
00067 }
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077 }
00078
00079 inline void globalInitOnce()
00080 {
00081 zypp::thread::callOnce(g_InitOnceFlag, _do_init_once);
00082 }
00083
00084 int log_curl(CURL *curl, curl_infotype info,
00085 char *ptr, size_t len, void *max_lvl)
00086 {
00087 std::string pfx(" ");
00088 long lvl = 0;
00089 switch( info)
00090 {
00091 case CURLINFO_TEXT: lvl = 1; pfx = "*"; break;
00092 case CURLINFO_HEADER_IN: lvl = 2; pfx = "<"; break;
00093 case CURLINFO_HEADER_OUT: lvl = 2; pfx = ">"; break;
00094 default: break;
00095 }
00096 if( lvl > 0 && max_lvl != NULL && lvl <= *((long *)max_lvl))
00097 {
00098 std::string msg(ptr, len);
00099 std::list<std::string> lines;
00100 std::list<std::string>::const_iterator line;
00101 zypp::str::split(msg, std::back_inserter(lines), "\r\n");
00102 for(line = lines.begin(); line != lines.end(); ++line)
00103 {
00104 DBG << pfx << " " << *line << endl;
00105 }
00106 }
00107 return 0;
00108 }
00109 }
00110
00111 namespace zypp {
00112 namespace media {
00113
00114 namespace {
00115 struct ProgressData
00116 {
00117 ProgressData(const long _timeout, const zypp::Url &_url = zypp::Url(),
00118 callback::SendReport<DownloadProgressReport> *_report=NULL)
00119 : timeout(_timeout)
00120 , reached(false)
00121 , report(_report)
00122 , ltime( time(NULL))
00123 , dload( 0)
00124 , uload( 0)
00125 , url(_url)
00126 {}
00127 long timeout;
00128 bool reached;
00129 callback::SendReport<DownloadProgressReport> *report;
00130 time_t ltime;
00131 double dload;
00132 double uload;
00133 zypp::Url url;
00134 };
00135 }
00136
00137 Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
00138 std::string MediaCurl::_agent = "Novell ZYPP Installer";
00139
00141
00142 static inline void escape( string & str_r,
00143 const char char_r, const string & escaped_r ) {
00144 for ( string::size_type pos = str_r.find( char_r );
00145 pos != string::npos; pos = str_r.find( char_r, pos ) ) {
00146 str_r.replace( pos, 1, escaped_r );
00147 }
00148 }
00149
00150 static inline string escapedPath( string path_r ) {
00151 escape( path_r, ' ', "%20" );
00152 return path_r;
00153 }
00154
00155 static inline string unEscape( string text_r ) {
00156 char * tmp = curl_unescape( text_r.c_str(), 0 );
00157 string ret( tmp );
00158 curl_free( tmp );
00159 return ret;
00160 }
00161
00163
00164
00165
00167
00168 MediaCurl::MediaCurl( const Url & url_r,
00169 const Pathname & attach_point_hint_r )
00170 : MediaHandler( url_r, attach_point_hint_r,
00171 "/",
00172 true ),
00173 _curl( NULL )
00174 {
00175 _curlError[0] = '\0';
00176 _curlDebug = 0L;
00177
00178 MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00179
00180 globalInitOnce();
00181
00182 if( !attachPoint().empty())
00183 {
00184 PathInfo ainfo(attachPoint());
00185 Pathname apath(attachPoint() + "XXXXXX");
00186 char *atemp = ::strdup( apath.asString().c_str());
00187 char *atest = NULL;
00188 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
00189 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
00190 {
00191 WAR << "attach point " << ainfo.path()
00192 << " is not useable for " << url_r.getScheme() << endl;
00193 setAttachPoint("", true);
00194 }
00195 else if( atest != NULL)
00196 ::rmdir(atest);
00197
00198 if( atemp != NULL)
00199 ::free(atemp);
00200 }
00201 }
00202
00203 void MediaCurl::setCookieFile( const Pathname &fileName )
00204 {
00205 _cookieFile = fileName;
00206 }
00207
00209
00210
00211
00212
00213
00214
00215
00216 void MediaCurl::attachTo (bool next)
00217 {
00218 if ( next )
00219 ZYPP_THROW(MediaNotSupportedException(_url));
00220
00221 if ( !_url.isValid() )
00222 ZYPP_THROW(MediaBadUrlException(_url));
00223
00224 CurlConfig curlconf;
00225 CurlConfig::parseConfig(curlconf);
00226
00227 curl_version_info_data *curl_info = NULL;
00228 curl_info = curl_version_info(CURLVERSION_NOW);
00229
00230 if (curl_info->protocols)
00231 {
00232 const char * const *proto;
00233 std::string scheme( _url.getScheme());
00234 bool found = false;
00235 for(proto=curl_info->protocols; !found && *proto; ++proto)
00236 {
00237 if( scheme == std::string((const char *)*proto))
00238 found = true;
00239 }
00240 if( !found)
00241 {
00242 std::string msg("Unsupported protocol '");
00243 msg += scheme;
00244 msg += "'";
00245 ZYPP_THROW(MediaBadUrlException(_url, msg));
00246 }
00247 }
00248
00249 if( !isUseableAttachPoint(attachPoint()))
00250 {
00251 std::string mountpoint = createAttachPoint().asString();
00252
00253 if( mountpoint.empty())
00254 ZYPP_THROW( MediaBadAttachPointException(url()));
00255
00256 setAttachPoint( mountpoint, true);
00257 }
00258
00259 disconnectFrom();
00260 _curl = curl_easy_init();
00261 if ( !_curl ) {
00262 ZYPP_THROW(MediaCurlInitException(_url));
00263 }
00264
00265 {
00266 char *ptr = getenv("ZYPP_MEDIA_CURL_DEBUG");
00267 _curlDebug = (ptr && *ptr) ? str::strtonum<long>( ptr) : 0L;
00268 if( _curlDebug > 0)
00269 {
00270 curl_easy_setopt( _curl, CURLOPT_VERBOSE, 1);
00271 curl_easy_setopt( _curl, CURLOPT_DEBUGFUNCTION, log_curl);
00272 curl_easy_setopt( _curl, CURLOPT_DEBUGDATA, &_curlDebug);
00273 }
00274 }
00275
00276 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
00277 if ( ret != 0 ) {
00278 disconnectFrom();
00279 ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
00280 }
00281
00282 ret = curl_easy_setopt( _curl, CURLOPT_FAILONERROR, true );
00283 if ( ret != 0 ) {
00284 disconnectFrom();
00285 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00286 }
00287
00288 ret = curl_easy_setopt( _curl, CURLOPT_NOSIGNAL, 1 );
00289 if ( ret != 0 ) {
00290 disconnectFrom();
00291 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00292 }
00293
00297 {
00298 _xfer_timeout = TRANSFER_TIMEOUT;
00299
00300 std::string param(_url.getQueryParam("timeout"));
00301 if( !param.empty())
00302 {
00303 long num = str::strtonum<long>( param);
00304 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
00305 _xfer_timeout = num;
00306 }
00307 }
00308
00309
00310
00311
00312 ret = curl_easy_setopt( _curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
00313 if ( ret != 0 ) {
00314 disconnectFrom();
00315 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00316 }
00317
00318 if ( _url.getScheme() == "http" ) {
00319
00320
00321 ret = curl_easy_setopt ( _curl, CURLOPT_FOLLOWLOCATION, true );
00322 if ( ret != 0) {
00323 disconnectFrom();
00324 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00325 }
00326 ret = curl_easy_setopt ( _curl, CURLOPT_MAXREDIRS, 3L );
00327 if ( ret != 0) {
00328 disconnectFrom();
00329 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00330 }
00331 ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, _agent.c_str() );
00332 if ( ret != 0) {
00333 disconnectFrom();
00334 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00335 }
00336 }
00337
00338 if ( _url.getScheme() == "https" )
00339 {
00340 bool verify_peer = false;
00341 bool verify_host = false;
00342
00343 std::string verify( _url.getQueryParam("ssl_verify"));
00344 if( verify.empty() ||
00345 verify == "yes")
00346 {
00347 verify_peer = true;
00348 verify_host = true;
00349 }
00350 else
00351 if( verify == "no")
00352 {
00353 verify_peer = false;
00354 verify_host = false;
00355 }
00356 else
00357 {
00358 std::vector<std::string> flags;
00359 std::vector<std::string>::const_iterator flag;
00360 str::split( verify, std::back_inserter(flags), ",");
00361 for(flag = flags.begin(); flag != flags.end(); ++flag)
00362 {
00363 if( *flag == "host")
00364 {
00365 verify_host = true;
00366 }
00367 else
00368 if( *flag == "peer")
00369 {
00370 verify_peer = true;
00371 }
00372 else
00373 {
00374 disconnectFrom();
00375 ZYPP_THROW(MediaBadUrlException(_url, "Unknown ssl_verify flag"));
00376 }
00377 }
00378 }
00379
00380 _ca_path = Pathname(_url.getQueryParam("ssl_capath")).asString();
00381 if( _ca_path.empty())
00382 {
00383 _ca_path = "/etc/ssl/certs/";
00384 }
00385 else
00386 if( !PathInfo(_ca_path).isDir() || !Pathname(_ca_path).absolute())
00387 {
00388 disconnectFrom();
00389 ZYPP_THROW(MediaBadUrlException(_url, "Invalid ssl_capath path"));
00390 }
00391
00392 if( verify_peer || verify_host)
00393 {
00394 ret = curl_easy_setopt( _curl, CURLOPT_CAPATH, _ca_path.c_str());
00395 if ( ret != 0 ) {
00396 disconnectFrom();
00397 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00398 }
00399 }
00400
00401 ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L);
00402 if ( ret != 0 ) {
00403 disconnectFrom();
00404 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00405 }
00406 ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L);
00407 if ( ret != 0 ) {
00408 disconnectFrom();
00409 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00410 }
00411
00412 ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, _agent.c_str() );
00413 if ( ret != 0) {
00414 disconnectFrom();
00415 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00416 }
00417 }
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 if ( _url.getUsername().empty() ) {
00428 if ( _url.getScheme() == "ftp" ) {
00429 string id = "yast2@";
00430 id += VERSION;
00431 DBG << "Anonymous FTP identification: '" << id << "'" << endl;
00432 _userpwd = "anonymous:" + id;
00433 }
00434 } else {
00435 _userpwd = _url.getUsername();
00436 if ( _url.getPassword().size() ) {
00437 _userpwd += ":" + _url.getPassword();
00438 }
00439 }
00440
00441 if ( _userpwd.size() ) {
00442 _userpwd = unEscape( _userpwd );
00443 ret = curl_easy_setopt( _curl, CURLOPT_USERPWD, _userpwd.c_str() );
00444 if ( ret != 0 ) {
00445 disconnectFrom();
00446 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00447 }
00448
00449
00450 if(_url.getScheme() == "http" || _url.getScheme() == "https")
00451 {
00452 string use_auth = _url.getQueryParam("auth");
00453 if( use_auth.empty())
00454 use_auth = "digest,basic";
00455
00456 try
00457 {
00458 long auth = CurlAuthData::auth_type_str2long(use_auth);
00459 if( auth != CURLAUTH_NONE)
00460 {
00461 DBG << "Enabling HTTP authentication methods: " << use_auth
00462 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
00463
00464 ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
00465 if ( ret != 0 ) {
00466 disconnectFrom();
00467 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00468 }
00469 }
00470 }
00471 catch (MediaException & ex_r)
00472 {
00473 string auth_hint = getAuthHint();
00474
00475 DBG << "Rethrowing as MediaUnauthorizedException. auth hint: '"
00476 << auth_hint << "'" << endl;
00477
00478 ZYPP_THROW(MediaUnauthorizedException(
00479 _url, ex_r.msg(), _curlError, auth_hint
00480 ));
00481 }
00482 }
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492 _proxy = _url.getQueryParam( "proxy" );
00493
00494 if ( ! _proxy.empty() ) {
00495 string proxyport( _url.getQueryParam( "proxyport" ) );
00496 if ( ! proxyport.empty() ) {
00497 _proxy += ":" + proxyport;
00498 }
00499 } else {
00500
00501 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
00502
00503 if ( proxy_info.enabled())
00504 {
00505 bool useproxy = true;
00506
00507 std::list<std::string> nope = proxy_info.noProxy();
00508 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
00509 it != proxy_info.noProxyEnd();
00510 it++)
00511 {
00512 std::string host( str::toLower(_url.getHost()));
00513 std::string temp( str::toLower(*it));
00514
00515
00516
00517
00518 if( temp.size() > 1 && temp.at(0) == '.')
00519 {
00520 if(host.size() > temp.size() &&
00521 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
00522 {
00523 DBG << "NO_PROXY: '" << *it << "' matches host '"
00524 << host << "'" << endl;
00525 useproxy = false;
00526 break;
00527 }
00528 }
00529 else
00530
00531 if( host == temp)
00532 {
00533 DBG << "NO_PROXY: '" << *it << "' matches host '"
00534 << host << "'" << endl;
00535 useproxy = false;
00536 break;
00537 }
00538 }
00539
00540 if ( useproxy ) {
00541 _proxy = proxy_info.proxy(_url.getScheme());
00542 }
00543 }
00544 }
00545
00546
00547 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
00548
00549 if ( ! _proxy.empty() ) {
00550
00551 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
00552 if ( ret != 0 ) {
00553 disconnectFrom();
00554 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00555 }
00556
00557
00558
00559
00560
00561
00562
00563
00564 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
00565
00566 if ( ! _proxyuserpwd.empty() ) {
00567 string proxypassword( _url.getQueryParam( "proxypassword" ) );
00568 if ( ! proxypassword.empty() ) {
00569 _proxyuserpwd += ":" + proxypassword;
00570 }
00571 } else {
00572 if (curlconf.proxyuserpwd.empty())
00573 DBG << "~/.curlrc does not contain the proxy-user option" << endl;
00574 else
00575 {
00576 _proxyuserpwd = curlconf.proxyuserpwd;
00577 DBG << "using proxy-user from ~/.curlrc" << endl;
00578 }
00579 }
00580
00581 _proxyuserpwd = unEscape( _proxyuserpwd );
00582 ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
00583 if ( ret != 0 ) {
00584 disconnectFrom();
00585 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00586 }
00587 }
00588
00589
00590
00591
00592 _currentCookieFile = _cookieFile.asString();
00593
00594 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
00595 _currentCookieFile.c_str() );
00596 if ( ret != 0 ) {
00597 disconnectFrom();
00598 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00599 }
00600
00601 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
00602 _currentCookieFile.c_str() );
00603 if ( ret != 0 ) {
00604 disconnectFrom();
00605 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00606 }
00607
00608 ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
00609 &progressCallback );
00610 if ( ret != 0 ) {
00611 disconnectFrom();
00612 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00613 }
00614
00615 ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
00616 if ( ret != 0 ) {
00617 disconnectFrom();
00618 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00619 }
00620
00621
00622 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
00623 setMediaSource(media);
00624 }
00625
00626 bool
00627 MediaCurl::checkAttachPoint(const Pathname &apoint) const
00628 {
00629 return MediaHandler::checkAttachPoint( apoint, true, true);
00630 }
00631
00633
00634
00635
00636
00637
00638 void MediaCurl::disconnectFrom()
00639 {
00640 if ( _curl )
00641 {
00642 curl_easy_cleanup( _curl );
00643 _curl = NULL;
00644 }
00645 }
00646
00648
00649
00650
00651
00652
00653
00654
00655 void MediaCurl::releaseFrom( bool eject )
00656 {
00657 disconnect();
00658 }
00659
00660
00662
00663
00664
00665
00666
00667 void MediaCurl::getFile( const Pathname & filename ) const
00668 {
00669
00670
00671 getFileCopy(filename, localPath(filename).absolutename());
00672 }
00673
00674
00675 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
00676 {
00677 callback::SendReport<DownloadProgressReport> report;
00678
00679 Url url( _url );
00680
00681 bool retry = false;
00682 CurlAuthData auth_data;
00683
00684 do
00685 {
00686 try
00687 {
00688 doGetFileCopy(filename, target, report);
00689 retry = false;
00690 }
00691
00692 catch (MediaUnauthorizedException & ex_r)
00693 {
00694 callback::SendReport<AuthenticationReport> auth_report;
00695
00696 if (!_url.getUsername().empty() && !retry)
00697 auth_data.setUserName(_url.getUsername());
00698
00699 string prompt_msg;
00700 if (retry || !_url.getUsername().empty())
00701 prompt_msg = _("Invalid user name or password.");
00702 else
00703 prompt_msg = boost::str(boost::format(
00704 _("Authentication required for '%s'")) % _url.asString());
00705
00706
00707 auth_data.setAuthType(ex_r.hint());
00708
00709 if (auth_report->prompt(_url, prompt_msg, auth_data))
00710 {
00711 DBG << "callback answer: retry" << endl
00712 << "CurlAuthData: " << auth_data << endl;
00713
00714 if (auth_data.valid()) {
00715 _userpwd = auth_data.getUserPwd();
00716
00717
00718 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
00719 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00720
00721
00722 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
00723 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00724 }
00725
00726 retry = true;
00727 }
00728 else
00729 {
00730 DBG << "callback answer: cancel" << endl;
00731 report->finish(url, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserString());
00732 ZYPP_RETHROW(ex_r);
00733 }
00734 }
00735
00736 catch (MediaException & excpt_r)
00737 {
00738
00739
00740 report->finish(url, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserString());
00741 ZYPP_RETHROW(excpt_r);
00742 }
00743 }
00744 while (retry);
00745
00746 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
00747 }
00748
00749 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
00750 {
00751 bool retry = false;
00752 CurlAuthData auth_data;
00753
00754 do
00755 {
00756 try
00757 {
00758 return doGetDoesFileExist( filename );
00759 }
00760
00761 catch (MediaUnauthorizedException & ex_r)
00762 {
00763 callback::SendReport<AuthenticationReport> auth_report;
00764
00765 if (!_url.getUsername().empty() && !retry)
00766 auth_data.setUserName(_url.getUsername());
00767
00768 string prompt_msg;
00769 if (retry || !_url.getUsername().empty())
00770 prompt_msg = _("Invalid user name or password.");
00771 else
00772 prompt_msg = boost::str(boost::format(
00773 _("Authentication required for '%s'")) % _url.asString());
00774
00775
00776 auth_data.setAuthType(ex_r.hint());
00777
00778 if (auth_report->prompt(_url, prompt_msg, auth_data))
00779 {
00780 DBG << "callback answer: retry" << endl
00781 << "CurlAuthData: " << auth_data << endl;
00782
00783 if (auth_data.valid()) {
00784 _userpwd = auth_data.getUserPwd();
00785
00786
00787 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
00788 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00789
00790
00791 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
00792 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00793 }
00794
00795 retry = true;
00796 }
00797 else
00798 {
00799 DBG << "callback answer: cancel" << endl;
00800 ZYPP_RETHROW(ex_r);
00801 }
00802 }
00803
00804 catch (MediaException & excpt_r)
00805 {
00806 ZYPP_RETHROW(excpt_r);
00807 }
00808 }
00809 while (retry);
00810
00811 return false;
00812 }
00813
00814 bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
00815 {
00816 DBG << filename.asString() << endl;
00817
00818 if(!_url.isValid())
00819 ZYPP_THROW(MediaBadUrlException(_url));
00820
00821 if(_url.getHost().empty())
00822 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00823
00824 string path = _url.getPathName();
00825 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00826 filename.absolute() ) {
00827
00828
00829 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00830 } else if ( filename.relative() ) {
00831
00832 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00833
00834 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00835 } else {
00836 path += filename.asString();
00837 }
00838
00839 Url url( _url );
00840 url.setPathName( path );
00841
00842 DBG << "URL: " << url.asString() << endl;
00843
00844
00845
00846
00847 Url curlUrl( url );
00848
00849
00850 curlUrl.setUsername( "" );
00851 curlUrl.setPassword( "" );
00852 curlUrl.setPathParams( "" );
00853 curlUrl.setQueryString( "" );
00854 curlUrl.setFragment( "" );
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864 string urlBuffer( curlUrl.asString());
00865 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00866 urlBuffer.c_str() );
00867 if ( ret != 0 ) {
00868 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00869 }
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
00882 if ( ret != 0 ) {
00883 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00884 }
00885
00886 FILE *file = ::fopen( "/dev/null", "w" );
00887 if ( !file ) {
00888 ::fclose(file);
00889 ERR << "fopen failed for /dev/null" << endl;
00890 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00891 if ( ret != 0 ) {
00892 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00893 }
00894 ZYPP_THROW(MediaWriteException("/dev/null"));
00895 }
00896
00897 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00898 if ( ret != 0 ) {
00899 ::fclose(file);
00900 std::string err( _curlError);
00901 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00902 if ( ret != 0 ) {
00903 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00904 }
00905 ZYPP_THROW(MediaCurlSetOptException(url, err));
00906 }
00907
00908
00909
00910
00911
00912
00913
00914 CURLcode ok = curl_easy_perform( _curl );
00915 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
00916
00917
00918 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00919 if ( ret != 0 )
00920 {
00921 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00922 }
00923
00924 if ( ok != 0 )
00925 {
00926 ::fclose( file );
00927
00928 std::string err;
00929 try
00930 {
00931 bool err_file_not_found = false;
00932 switch ( ok )
00933 {
00934 case CURLE_FTP_COULDNT_RETR_FILE:
00935 case CURLE_FTP_ACCESS_DENIED:
00936 err_file_not_found = true;
00937 break;
00938 case CURLE_HTTP_RETURNED_ERROR:
00939 {
00940 long httpReturnCode = 0;
00941 CURLcode infoRet = curl_easy_getinfo( _curl,
00942 CURLINFO_RESPONSE_CODE,
00943 &httpReturnCode );
00944 if ( infoRet == CURLE_OK )
00945 {
00946 string msg = "HTTP response: " +
00947 str::numstring( httpReturnCode );
00948 if ( httpReturnCode == 401 )
00949 {
00950 std::string auth_hint = getAuthHint();
00951
00952 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
00953 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
00954
00955 ZYPP_THROW(MediaUnauthorizedException(
00956 url, "Login failed.", _curlError, auth_hint
00957 ));
00958 }
00959 else
00960 if ( httpReturnCode == 404)
00961 {
00962 err_file_not_found = true;
00963 break;
00964 }
00965
00966 msg += err;
00967 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00968 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00969 }
00970 else
00971 {
00972 string msg = "Unable to retrieve HTTP response:";
00973 msg += err;
00974 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00975 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00976 }
00977 }
00978 break;
00979 case CURLE_UNSUPPORTED_PROTOCOL:
00980 case CURLE_URL_MALFORMAT:
00981 case CURLE_URL_MALFORMAT_USER:
00982 case CURLE_BAD_PASSWORD_ENTERED:
00983 case CURLE_FTP_USER_PASSWORD_INCORRECT:
00984 case CURLE_COULDNT_RESOLVE_PROXY:
00985 case CURLE_COULDNT_RESOLVE_HOST:
00986 case CURLE_COULDNT_CONNECT:
00987 case CURLE_FTP_CANT_GET_HOST:
00988 case CURLE_WRITE_ERROR:
00989 case CURLE_ABORTED_BY_CALLBACK:
00990 case CURLE_SSL_PEER_CERTIFICATE:
00991 default:
00992 err = curl_easy_strerror(ok);
00993 if (err.empty())
00994 err = "Unrecognized error";
00995 break;
00996 }
00997
00998 if( err_file_not_found)
00999 {
01000
01001 return false;
01002 }
01003 else
01004 {
01005
01006 ZYPP_THROW(MediaCurlException(url, string(), _curlError));
01007 }
01008 }
01009 catch (const MediaException & excpt_r)
01010 {
01011 ZYPP_RETHROW(excpt_r);
01012 }
01013 }
01014
01015
01016 return ( ok == CURLE_OK );
01017
01018
01019
01020 }
01021
01022 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
01023 {
01024 DBG << filename.asString() << endl;
01025
01026 if(!_url.isValid())
01027 ZYPP_THROW(MediaBadUrlException(_url));
01028
01029 if(_url.getHost().empty())
01030 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
01031
01032 string path = _url.getPathName();
01033 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
01034 filename.absolute() ) {
01035
01036
01037 path += filename.asString().substr( 1, filename.asString().size() - 1 );
01038 } else if ( filename.relative() ) {
01039
01040 if (path.empty()) path = "/";
01041 else if (*path.rbegin() != '/' ) path += "/";
01042
01043 path += filename.asString().substr( 2, filename.asString().size() - 2 );
01044 } else {
01045 path += filename.asString();
01046 }
01047
01048 Url url( _url );
01049 url.setPathName( path );
01050
01051 Pathname dest = target.absolutename();
01052 if( assert_dir( dest.dirname() ) )
01053 {
01054 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
01055 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
01056 }
01057
01058 DBG << "URL: " << url.asString() << endl;
01059
01060
01061
01062
01063 Url curlUrl( url );
01064
01065
01066 curlUrl.setUsername( "" );
01067 curlUrl.setPassword( "" );
01068 curlUrl.setPathParams( "" );
01069 curlUrl.setQueryString( "" );
01070 curlUrl.setFragment( "" );
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080 string urlBuffer( curlUrl.asString());
01081 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
01082 urlBuffer.c_str() );
01083 if ( ret != 0 ) {
01084 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01085 }
01086
01087 string destNew = target.asString() + ".new.zypp.XXXXXX";
01088 char *buf = ::strdup( destNew.c_str());
01089 if( !buf)
01090 {
01091 ERR << "out of memory for temp file name" << endl;
01092 ZYPP_THROW(MediaSystemException(
01093 url, "out of memory for temp file name"
01094 ));
01095 }
01096
01097 int tmp_fd = ::mkstemp( buf );
01098 if( tmp_fd == -1)
01099 {
01100 free( buf);
01101 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
01102 ZYPP_THROW(MediaWriteException(destNew));
01103 }
01104 destNew = buf;
01105 free( buf);
01106
01107 FILE *file = ::fdopen( tmp_fd, "w" );
01108 if ( !file ) {
01109 ::close( tmp_fd);
01110 filesystem::unlink( destNew );
01111 ERR << "fopen failed for file '" << destNew << "'" << endl;
01112 ZYPP_THROW(MediaWriteException(destNew));
01113 }
01114
01115 DBG << "dest: " << dest << endl;
01116 DBG << "temp: " << destNew << endl;
01117
01118 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
01119 if ( ret != 0 ) {
01120 ::fclose( file );
01121 filesystem::unlink( destNew );
01122 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01123 }
01124
01125
01126 ProgressData progressData(_xfer_timeout, url, &report);
01127 report->start(url, dest);
01128 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
01129 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01130 }
01131
01132 ret = curl_easy_perform( _curl );
01133
01134 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
01135 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01136 }
01137
01138 if ( ret != 0 ) {
01139 ERR << "curl error: " << ret << ": " << _curlError
01140 << ", temp file size " << PathInfo(destNew).size()
01141 << " byte." << endl;
01142
01143 ::fclose( file );
01144 filesystem::unlink( destNew );
01145
01146 std::string err;
01147 try {
01148 bool err_file_not_found = false;
01149 switch ( ret ) {
01150 case CURLE_UNSUPPORTED_PROTOCOL:
01151 case CURLE_URL_MALFORMAT:
01152 case CURLE_URL_MALFORMAT_USER:
01153 err = " Bad URL";
01154 case CURLE_HTTP_RETURNED_ERROR:
01155 {
01156 long httpReturnCode = 0;
01157 CURLcode infoRet = curl_easy_getinfo( _curl,
01158 CURLINFO_RESPONSE_CODE,
01159 &httpReturnCode );
01160 if ( infoRet == CURLE_OK ) {
01161 string msg = "HTTP response: " +
01162 str::numstring( httpReturnCode );
01163 if ( httpReturnCode == 401 )
01164 {
01165 std::string auth_hint = getAuthHint();
01166
01167 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
01168 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
01169
01170 ZYPP_THROW(MediaUnauthorizedException(
01171 url, "Login failed.", _curlError, auth_hint
01172 ));
01173 }
01174 else
01175 if ( httpReturnCode == 404)
01176 {
01177 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01178 }
01179
01180 msg += err;
01181 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01182 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01183 }
01184 else
01185 {
01186 string msg = "Unable to retrieve HTTP response:";
01187 msg += err;
01188 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01189 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01190 }
01191 }
01192 break;
01193 case CURLE_FTP_COULDNT_RETR_FILE:
01194 case CURLE_FTP_ACCESS_DENIED:
01195 err = "File not found";
01196 err_file_not_found = true;
01197 break;
01198 case CURLE_BAD_PASSWORD_ENTERED:
01199 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01200 err = "Login failed";
01201 break;
01202 case CURLE_COULDNT_RESOLVE_PROXY:
01203 case CURLE_COULDNT_RESOLVE_HOST:
01204 case CURLE_COULDNT_CONNECT:
01205 case CURLE_FTP_CANT_GET_HOST:
01206 err = "Connection failed";
01207 break;
01208 case CURLE_WRITE_ERROR:
01209 err = "Write error";
01210 break;
01211 case CURLE_ABORTED_BY_CALLBACK:
01212 if( progressData.reached)
01213 {
01214 err = "Timeout reached";
01215 }
01216 else
01217 {
01218 err = "User abort";
01219 }
01220 break;
01221 case CURLE_SSL_PEER_CERTIFICATE:
01222 default:
01223 err = "Unrecognized error";
01224 break;
01225 }
01226 if( err_file_not_found)
01227 {
01228 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01229 }
01230 else
01231 {
01232 ZYPP_THROW(MediaCurlException(url, err, _curlError));
01233 }
01234 }
01235 catch (const MediaException & excpt_r)
01236 {
01237 ZYPP_RETHROW(excpt_r);
01238 }
01239 }
01240 #if DETECT_DIR_INDEX
01241 else
01242 if(curlUrl.getScheme() == "http" ||
01243 curlUrl.getScheme() == "https")
01244 {
01245
01246
01247
01248
01249
01250
01251
01252
01253 bool not_a_file = false;
01254 char *ptr = NULL;
01255 CURLcode ret = curl_easy_getinfo( _curl,
01256 CURLINFO_EFFECTIVE_URL,
01257 &ptr);
01258 if ( ret == CURLE_OK && ptr != NULL)
01259 {
01260 try
01261 {
01262 Url eurl( ptr);
01263 std::string path( eurl.getPathName());
01264 if( !path.empty() && path != "/" && *path.rbegin() == '/')
01265 {
01266 DBG << "Effective url ("
01267 << eurl
01268 << ") seems to provide the index of a directory"
01269 << endl;
01270 not_a_file = true;
01271 }
01272 }
01273 catch( ... )
01274 {}
01275 }
01276
01277 if( not_a_file)
01278 {
01279 ::fclose( file );
01280 filesystem::unlink( destNew );
01281 ZYPP_THROW(MediaNotAFileException(_url, filename));
01282 }
01283 }
01284 #endif // DETECT_DIR_INDEX
01285
01286 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
01287 {
01288 ERR << "Failed to chmod file " << destNew << endl;
01289 }
01290 ::fclose( file );
01291
01292 if ( rename( destNew, dest ) != 0 ) {
01293 ERR << "Rename failed" << endl;
01294 ZYPP_THROW(MediaWriteException(dest));
01295 }
01296 DBG << "done: " << PathInfo(dest) << endl;
01297 }
01298
01299
01301
01302
01303
01304
01305
01306
01307
01308 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
01309 {
01310 filesystem::DirContent content;
01311 getDirInfo( content, dirname, false );
01312
01313 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
01314 Pathname filename = dirname + it->name;
01315 int res = 0;
01316
01317 switch ( it->type ) {
01318 case filesystem::FT_NOT_AVAIL:
01319 case filesystem::FT_FILE:
01320 getFile( filename );
01321 break;
01322 case filesystem::FT_DIR:
01323 if ( recurse_r ) {
01324 getDir( filename, recurse_r );
01325 } else {
01326 res = assert_dir( localPath( filename ) );
01327 if ( res ) {
01328 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
01329 }
01330 }
01331 break;
01332 default:
01333
01334 break;
01335 }
01336 }
01337 }
01338
01340
01341
01342
01343
01344
01345
01346
01347 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01348 const Pathname & dirname, bool dots ) const
01349 {
01350 getDirectoryYast( retlist, dirname, dots );
01351 }
01352
01354
01355
01356
01357
01358
01359
01360
01361 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01362 const Pathname & dirname, bool dots ) const
01363 {
01364 getDirectoryYast( retlist, dirname, dots );
01365 }
01366
01368
01369
01370
01371
01372
01373
01374
01375 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
01376 double ultotal, double ulnow )
01377 {
01378 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01379 if( pdata)
01380 {
01381
01382 if( pdata->report)
01383 {
01384 if (! (*(pdata->report))->progress(int( dlnow * 100 / dltotal ), pdata->url))
01385 {
01386 return 1;
01387 }
01388 }
01389
01390
01391 if( pdata->timeout > 0)
01392 {
01393 time_t now = time(NULL);
01394 if( now > 0)
01395 {
01396 bool progress = false;
01397
01398
01399
01400 if( pdata->ltime <= 0 || pdata->ltime > now)
01401 pdata->ltime = now;
01402
01403
01404 if( dlnow != pdata->dload)
01405 {
01406 progress = true;
01407 pdata->dload = dlnow;
01408 pdata->ltime = now;
01409 }
01410
01411 if( ulnow != pdata->uload)
01412 {
01413 progress = true;
01414 pdata->uload = ulnow;
01415 pdata->ltime = now;
01416 }
01417
01418 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01419 {
01420 pdata->reached = true;
01421 return 1;
01422 }
01423 }
01424 }
01425 }
01426 return 0;
01427 }
01428
01429 string MediaCurl::getAuthHint() const
01430 {
01431 long auth_info = CURLAUTH_NONE;
01432
01433 CURLcode infoRet =
01434 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
01435
01436 if(infoRet == CURLE_OK)
01437 {
01438 return CurlAuthData::auth_type_long2str(auth_info);
01439 }
01440
01441 return "";
01442 }
01443
01444 }
01445 }
01446