00001
00002
00003
00004
00005
00006
00007
00008
00013 #include <iostream>
00014
00015 #include "zypp/base/Logger.h"
00016 #include "zypp/ExternalProgram.h"
00017 #include "zypp/base/String.h"
00018 #include "zypp/base/Sysconfig.h"
00019
00020 #include "zypp/media/MediaCurl.h"
00021 #include "zypp/media/proxyinfo/ProxyInfos.h"
00022 #include "zypp/media/ProxyInfo.h"
00023 #include "zypp/thread/Once.h"
00024 #include <cstdlib>
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <sys/mount.h>
00028 #include <errno.h>
00029 #include <dirent.h>
00030 #include <unistd.h>
00031
00032 #include "config.h"
00033
00034 #define DETECT_DIR_INDEX 0
00035 #define CONNECT_TIMEOUT 60
00036 #define TRANSFER_TIMEOUT 60 * 3
00037 #define TRANSFER_TIMEOUT_MAX 60 * 60
00038
00039
00040 using namespace std;
00041 using namespace zypp::base;
00042
00043 namespace
00044 {
00045 zypp::thread::OnceFlag g_InitOnceFlag = PTHREAD_ONCE_INIT;
00046 zypp::thread::OnceFlag g_FreeOnceFlag = PTHREAD_ONCE_INIT;
00047
00048 extern "C" void _do_free_once()
00049 {
00050 curl_global_cleanup();
00051 }
00052
00053 extern "C" void globalFreeOnce()
00054 {
00055 zypp::thread::callOnce(g_FreeOnceFlag, _do_free_once);
00056 }
00057
00058 extern "C" void _do_init_once()
00059 {
00060 CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
00061 if ( ret != 0 )
00062 {
00063 WAR << "curl global init failed" << endl;
00064 }
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 }
00075
00076 inline void globalInitOnce()
00077 {
00078 zypp::thread::callOnce(g_InitOnceFlag, _do_init_once);
00079 }
00080 }
00081
00082 namespace zypp {
00083 namespace media {
00084
00085 namespace {
00086 struct ProgressData
00087 {
00088 ProgressData(const long _timeout, const zypp::Url &_url = zypp::Url(),
00089 callback::SendReport<DownloadProgressReport> *_report=NULL)
00090 : timeout(_timeout)
00091 , reached(false)
00092 , report(_report)
00093 , ltime( time(NULL))
00094 , dload( 0)
00095 , uload( 0)
00096 , url(_url)
00097 {}
00098 long timeout;
00099 bool reached;
00100 callback::SendReport<DownloadProgressReport> *report;
00101 time_t ltime;
00102 double dload;
00103 double uload;
00104 zypp::Url url;
00105 };
00106 }
00107
00108 Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
00109 std::string MediaCurl::_agent = "Novell ZYPP Installer";
00110
00112
00113 static inline void escape( string & str_r,
00114 const char char_r, const string & escaped_r ) {
00115 for ( string::size_type pos = str_r.find( char_r );
00116 pos != string::npos; pos = str_r.find( char_r, pos ) ) {
00117 str_r.replace( pos, 1, escaped_r );
00118 }
00119 }
00120
00121 static inline string escapedPath( string path_r ) {
00122 escape( path_r, ' ', "%20" );
00123 return path_r;
00124 }
00125
00126 static inline string unEscape( string text_r ) {
00127 char * tmp = curl_unescape( text_r.c_str(), 0 );
00128 string ret( tmp );
00129 curl_free( tmp );
00130 return ret;
00131 }
00132
00134
00135
00136
00138
00139 MediaCurl::MediaCurl( const Url & url_r,
00140 const Pathname & attach_point_hint_r )
00141 : MediaHandler( url_r, attach_point_hint_r,
00142 "/",
00143 true ),
00144 _curl( NULL )
00145 {
00146 _curlError[0] = '\0';
00147
00148 MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00149
00150 globalInitOnce();
00151
00152 if( !attachPoint().empty())
00153 {
00154 PathInfo ainfo(attachPoint());
00155 Pathname apath(attachPoint() + "XXXXXX");
00156 char *atemp = ::strdup( apath.asString().c_str());
00157 char *atest = NULL;
00158 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
00159 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
00160 {
00161 WAR << "attach point " << ainfo.path()
00162 << " is not useable for " << url_r.getScheme() << endl;
00163 setAttachPoint("", true);
00164 }
00165 else if( atest != NULL)
00166 ::rmdir(atest);
00167
00168 if( atemp != NULL)
00169 ::free(atemp);
00170 }
00171 }
00172
00173 void MediaCurl::setCookieFile( const Pathname &fileName )
00174 {
00175 _cookieFile = fileName;
00176 }
00177
00179
00180
00181
00182
00183
00184
00185
00186 void MediaCurl::attachTo (bool next)
00187 {
00188 if ( next )
00189 ZYPP_THROW(MediaNotSupportedException(_url));
00190
00191 if ( !_url.isValid() )
00192 ZYPP_THROW(MediaBadUrlException(_url));
00193
00194 curl_version_info_data *curl_info = NULL;
00195 curl_info = curl_version_info(CURLVERSION_NOW);
00196
00197 if (curl_info->protocols)
00198 {
00199 const char * const *proto;
00200 std::string scheme( _url.getScheme());
00201 bool found = false;
00202 for(proto=curl_info->protocols; !found && *proto; ++proto)
00203 {
00204 if( scheme == std::string((const char *)*proto))
00205 found = true;
00206 }
00207 if( !found)
00208 {
00209 std::string msg("Unsupported protocol '");
00210 msg += scheme;
00211 msg += "'";
00212 ZYPP_THROW(MediaBadUrlException(_url, msg));
00213 }
00214 }
00215
00216 if( !isUseableAttachPoint(attachPoint()))
00217 {
00218 std::string mountpoint = createAttachPoint().asString();
00219
00220 if( mountpoint.empty())
00221 ZYPP_THROW( MediaBadAttachPointException(url()));
00222
00223 setAttachPoint( mountpoint, true);
00224 }
00225
00226 disconnectFrom();
00227 _curl = curl_easy_init();
00228 if ( !_curl ) {
00229 ZYPP_THROW(MediaCurlInitException(_url));
00230 }
00231
00232 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
00233 if ( ret != 0 ) {
00234 disconnectFrom();
00235 ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
00236 }
00237
00238 ret = curl_easy_setopt( _curl, CURLOPT_FAILONERROR, true );
00239 if ( ret != 0 ) {
00240 disconnectFrom();
00241 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00242 }
00243
00244 ret = curl_easy_setopt( _curl, CURLOPT_NOSIGNAL, 1 );
00245 if ( ret != 0 ) {
00246 disconnectFrom();
00247 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00248 }
00249
00253 {
00254 _xfer_timeout = TRANSFER_TIMEOUT;
00255
00256 std::string param(_url.getQueryParam("timeout"));
00257 if( !param.empty())
00258 {
00259 long num = str::strtonum<long>( param);
00260 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
00261 _xfer_timeout = num;
00262 }
00263 }
00264
00265
00266
00267
00268 ret = curl_easy_setopt( _curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
00269 if ( ret != 0 ) {
00270 disconnectFrom();
00271 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00272 }
00273
00274 if ( _url.getScheme() == "http" ) {
00275
00276
00277 ret = curl_easy_setopt ( _curl, CURLOPT_FOLLOWLOCATION, true );
00278 if ( ret != 0) {
00279 disconnectFrom();
00280 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00281 }
00282 ret = curl_easy_setopt ( _curl, CURLOPT_MAXREDIRS, 3L );
00283 if ( ret != 0) {
00284 disconnectFrom();
00285 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00286 }
00287 ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, _agent.c_str() );
00288 if ( ret != 0) {
00289 disconnectFrom();
00290 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00291 }
00292 }
00293
00294 if ( _url.getScheme() == "https" )
00295 {
00296 bool verify_peer = false;
00297 bool verify_host = false;
00298
00299 std::string verify( _url.getQueryParam("ssl_verify"));
00300 if( verify.empty() ||
00301 verify == "yes")
00302 {
00303 verify_peer = true;
00304 verify_host = true;
00305 }
00306 else
00307 if( verify == "no")
00308 {
00309 verify_peer = false;
00310 verify_host = false;
00311 }
00312 else
00313 {
00314 std::vector<std::string> flags;
00315 std::vector<std::string>::const_iterator flag;
00316 str::split( verify, std::back_inserter(flags), ",");
00317 for(flag = flags.begin(); flag != flags.end(); ++flag)
00318 {
00319 if( *flag == "host")
00320 {
00321 verify_host = true;
00322 }
00323 else
00324 if( *flag == "peer")
00325 {
00326 verify_peer = true;
00327 }
00328 else
00329 {
00330 disconnectFrom();
00331 ZYPP_THROW(MediaBadUrlException(_url, "Unknown ssl_verify flag"));
00332 }
00333 }
00334 }
00335
00336 _ca_path = Pathname(_url.getQueryParam("ssl_capath")).asString();
00337 if( _ca_path.empty())
00338 {
00339 _ca_path = "/etc/ssl/certs/";
00340 }
00341 else
00342 if( !PathInfo(_ca_path).isDir() || !Pathname(_ca_path).absolute())
00343 {
00344 disconnectFrom();
00345 ZYPP_THROW(MediaBadUrlException(_url, "Invalid ssl_capath path"));
00346 }
00347
00348 if( verify_peer || verify_host)
00349 {
00350 ret = curl_easy_setopt( _curl, CURLOPT_CAPATH, _ca_path.c_str());
00351 if ( ret != 0 ) {
00352 disconnectFrom();
00353 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00354 }
00355 }
00356
00357 ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L);
00358 if ( ret != 0 ) {
00359 disconnectFrom();
00360 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00361 }
00362 ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L);
00363 if ( ret != 0 ) {
00364 disconnectFrom();
00365 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00366 }
00367
00368 ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, _agent.c_str() );
00369 if ( ret != 0) {
00370 disconnectFrom();
00371 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00372 }
00373 }
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383 if ( _url.getUsername().empty() ) {
00384 if ( _url.getScheme() == "ftp" ) {
00385 string id = "yast2@";
00386 id += VERSION;
00387 DBG << "Anonymous FTP identification: '" << id << "'" << endl;
00388 _userpwd = "anonymous:" + id;
00389 }
00390 } else {
00391 _userpwd = _url.getUsername();
00392 if ( _url.getPassword().size() ) {
00393 _userpwd += ":" + _url.getPassword();
00394 }
00395 }
00396
00397 if ( _userpwd.size() ) {
00398 _userpwd = unEscape( _userpwd );
00399 ret = curl_easy_setopt( _curl, CURLOPT_USERPWD, _userpwd.c_str() );
00400 if ( ret != 0 ) {
00401 disconnectFrom();
00402 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00403 }
00404
00405 if( !_url.getQueryParam("auth").empty() &&
00406 (_url.getScheme() == "http" || _url.getScheme() == "https"))
00407 {
00408 std::vector<std::string> list;
00409 std::vector<std::string>::const_iterator it;
00410 str::split(_url.getQueryParam("auth"), std::back_inserter(list), ",");
00411
00412 long auth = CURLAUTH_NONE;
00413 for(it = list.begin(); it != list.end(); ++it)
00414 {
00415 if(*it == "basic")
00416 {
00417 auth |= CURLAUTH_BASIC;
00418 }
00419 else
00420 if(*it == "digest")
00421 {
00422 auth |= CURLAUTH_DIGEST;
00423 }
00424 else
00425 if((curl_info && (curl_info->features & CURL_VERSION_NTLM)) &&
00426 (*it == "ntlm"))
00427 {
00428 auth |= CURLAUTH_NTLM;
00429 }
00430 else
00431 if((curl_info && (curl_info->features & CURL_VERSION_SPNEGO)) &&
00432 (*it == "spnego" || *it == "negotiate"))
00433 {
00434
00435 auth |= CURLAUTH_GSSNEGOTIATE;
00436 }
00437 else
00438 if((curl_info && (curl_info->features & CURL_VERSION_GSSNEGOTIATE)) &&
00439 (*it == "gssnego" || *it == "negotiate"))
00440 {
00441 auth |= CURLAUTH_GSSNEGOTIATE;
00442 }
00443 else
00444 {
00445 std::string msg("Unsupported HTTP authentication method '");
00446 msg += *it;
00447 msg += "'";
00448 disconnectFrom();
00449 ZYPP_THROW(MediaBadUrlException(_url, msg));
00450 }
00451 }
00452
00453 if( auth != CURLAUTH_NONE)
00454 {
00455 DBG << "Enabling HTTP authentication methods: "
00456 << _url.getQueryParam("auth") << std::endl;
00457
00458 ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
00459 if ( ret != 0 ) {
00460 disconnectFrom();
00461 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00462 }
00463 }
00464 }
00465 }
00466
00467
00468
00469
00470
00471
00472
00473
00474 _proxy = _url.getQueryParam( "proxy" );
00475
00476 if ( ! _proxy.empty() ) {
00477 string proxyport( _url.getQueryParam( "proxyport" ) );
00478 if ( ! proxyport.empty() ) {
00479 _proxy += ":" + proxyport;
00480 }
00481 } else {
00482
00483 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
00484
00485 if ( proxy_info.enabled())
00486 {
00487 bool useproxy = true;
00488
00489 std::list<std::string> nope = proxy_info.noProxy();
00490 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
00491 it != proxy_info.noProxyEnd();
00492 it++)
00493 {
00494 std::string host( str::toLower(_url.getHost()));
00495 std::string temp( str::toLower(*it));
00496
00497
00498
00499
00500 if( temp.size() > 1 && temp.at(0) == '.')
00501 {
00502 if(host.size() > temp.size() &&
00503 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
00504 {
00505 DBG << "NO_PROXY: '" << *it << "' matches host '"
00506 << host << "'" << endl;
00507 useproxy = false;
00508 break;
00509 }
00510 }
00511 else
00512
00513 if( host == temp)
00514 {
00515 DBG << "NO_PROXY: '" << *it << "' matches host '"
00516 << host << "'" << endl;
00517 useproxy = false;
00518 break;
00519 }
00520 }
00521
00522 if ( useproxy ) {
00523 _proxy = proxy_info.proxy(_url.getScheme());
00524 }
00525 }
00526 }
00527
00528
00529 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
00530
00531 if ( ! _proxy.empty() ) {
00532
00533 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
00534 if ( ret != 0 ) {
00535 disconnectFrom();
00536 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00537 }
00538
00539
00540
00541
00542
00543
00544
00545
00546 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
00547
00548 if ( ! _proxyuserpwd.empty() ) {
00549
00550 string proxypassword( _url.getQueryParam( "proxypassword" ) );
00551 if ( ! proxypassword.empty() ) {
00552 _proxyuserpwd += ":" + proxypassword;
00553 }
00554
00555 } else {
00556 char *home = getenv("HOME");
00557 if( home && *home)
00558 {
00559 Pathname curlrcFile = string( home ) + string( "/.curlrc" );
00560
00561 PathInfo h_info(string(home), PathInfo::LSTAT);
00562 PathInfo c_info(curlrcFile, PathInfo::LSTAT);
00563
00564 if( h_info.isDir() && h_info.owner() == getuid() &&
00565 c_info.isFile() && c_info.owner() == getuid())
00566 {
00567 map<string,string> rc_data = base::sysconfig::read( curlrcFile );
00568
00569 map<string,string>::const_iterator it = rc_data.find("proxy-user");
00570 if (it != rc_data.end())
00571 _proxyuserpwd = it->second;
00572 }
00573 else
00574 {
00575 WAR << "Not allowed to parse '" << curlrcFile
00576 << "': bad file owner" << std::endl;
00577 }
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 try {
00682 doGetFileCopy(filename, target, report);
00683 }
00684 catch (MediaException & excpt_r)
00685 {
00686
00687
00688 report->finish(url, zypp::media::DownloadProgressReport::NOT_FOUND, excpt_r.msg());
00689 ZYPP_RETHROW(excpt_r);
00690 }
00691 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
00692 }
00693
00694 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
00695 {
00696 DBG << filename.asString() << endl;
00697
00698 if(!_url.isValid())
00699 ZYPP_THROW(MediaBadUrlException(_url));
00700
00701 if(_url.getHost().empty())
00702 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00703
00704 string path = _url.getPathName();
00705 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00706 filename.absolute() ) {
00707
00708
00709 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00710 } else if ( filename.relative() ) {
00711
00712 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00713
00714 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00715 } else {
00716 path += filename.asString();
00717 }
00718
00719 Url url( _url );
00720 url.setPathName( path );
00721
00722 Pathname dest = target.absolutename();
00723 if( assert_dir( dest.dirname() ) )
00724 {
00725 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
00726 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
00727 }
00728
00729 DBG << "URL: " << url.asString() << endl;
00730
00731
00732
00733
00734 Url curlUrl( url );
00735
00736
00737 curlUrl.setUsername( "" );
00738 curlUrl.setPassword( "" );
00739 curlUrl.setPathParams( "" );
00740 curlUrl.setQueryString( "" );
00741 curlUrl.setFragment( "" );
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751 string urlBuffer( curlUrl.asString());
00752 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00753 urlBuffer.c_str() );
00754 if ( ret != 0 ) {
00755 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00756 }
00757
00758 string destNew = target.asString() + ".new.zypp.XXXXXX";
00759 char *buf = ::strdup( destNew.c_str());
00760 if( !buf)
00761 {
00762 ERR << "out of memory for temp file name" << endl;
00763 ZYPP_THROW(MediaSystemException(
00764 url, "out of memory for temp file name"
00765 ));
00766 }
00767
00768 int tmp_fd = ::mkstemp( buf );
00769 if( tmp_fd == -1)
00770 {
00771 free( buf);
00772 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
00773 ZYPP_THROW(MediaWriteException(destNew));
00774 }
00775 destNew = buf;
00776 free( buf);
00777
00778 FILE *file = ::fdopen( tmp_fd, "w" );
00779 if ( !file ) {
00780 ::close( tmp_fd);
00781 filesystem::unlink( destNew );
00782 ERR << "fopen failed for file '" << destNew << "'" << endl;
00783 ZYPP_THROW(MediaWriteException(destNew));
00784 }
00785
00786 DBG << "dest: " << dest << endl;
00787 DBG << "temp: " << destNew << endl;
00788
00789 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00790 if ( ret != 0 ) {
00791 ::fclose( file );
00792 filesystem::unlink( destNew );
00793 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00794 }
00795
00796
00797 ProgressData progressData(_xfer_timeout, url, &report);
00798 report->start(url, dest);
00799 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
00800 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
00801 }
00802
00803 ret = curl_easy_perform( _curl );
00804
00805 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
00806 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
00807 }
00808
00809 if ( ret != 0 ) {
00810 ::fclose( file );
00811 filesystem::unlink( destNew );
00812 ERR << "curl error: " << ret << ": " << _curlError << endl;
00813 std::string err;
00814 try {
00815 bool err_file_not_found = false;
00816 switch ( ret ) {
00817 case CURLE_UNSUPPORTED_PROTOCOL:
00818 case CURLE_URL_MALFORMAT:
00819 case CURLE_URL_MALFORMAT_USER:
00820 err = " Bad URL";
00821 case CURLE_HTTP_RETURNED_ERROR:
00822 {
00823 long httpReturnCode = 0;
00824 CURLcode infoRet = curl_easy_getinfo( _curl,
00825 CURLINFO_RESPONSE_CODE,
00826 &httpReturnCode );
00827 if ( infoRet == CURLE_OK ) {
00828 string msg = "HTTP response: " +
00829 str::numstring( httpReturnCode );
00830 if ( httpReturnCode == 401 )
00831 {
00832 err = " Login failed";
00833 }
00834 else
00835 if ( httpReturnCode == 404)
00836 {
00837 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
00838 }
00839
00840 msg += err;
00841 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00842 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00843 }
00844 else
00845 {
00846 string msg = "Unable to retrieve HTTP response:";
00847 msg += err;
00848 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00849 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00850 }
00851 }
00852 break;
00853 case CURLE_FTP_COULDNT_RETR_FILE:
00854 case CURLE_FTP_ACCESS_DENIED:
00855 err = "File not found";
00856 err_file_not_found = true;
00857 break;
00858 case CURLE_BAD_PASSWORD_ENTERED:
00859 case CURLE_FTP_USER_PASSWORD_INCORRECT:
00860 err = "Login failed";
00861 break;
00862 case CURLE_COULDNT_RESOLVE_PROXY:
00863 case CURLE_COULDNT_RESOLVE_HOST:
00864 case CURLE_COULDNT_CONNECT:
00865 case CURLE_FTP_CANT_GET_HOST:
00866 err = "Connection failed";
00867 break;
00868 case CURLE_WRITE_ERROR:
00869 err = "Write error";
00870 break;
00871 case CURLE_ABORTED_BY_CALLBACK:
00872 if( progressData.reached)
00873 {
00874 err = "Timeout reached";
00875 }
00876 else
00877 {
00878 err = "User abort";
00879 }
00880 break;
00881 case CURLE_SSL_PEER_CERTIFICATE:
00882 default:
00883 err = "Unrecognized error";
00884 break;
00885 }
00886 if( err_file_not_found)
00887 {
00888 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
00889 }
00890 else
00891 {
00892 ZYPP_THROW(MediaCurlException(url, err, _curlError));
00893 }
00894 }
00895 catch (const MediaException & excpt_r)
00896 {
00897 ZYPP_RETHROW(excpt_r);
00898 }
00899 }
00900 #if DETECT_DIR_INDEX
00901 else
00902 if(curlUrl.getScheme() == "http" ||
00903 curlUrl.getScheme() == "https")
00904 {
00905
00906
00907
00908
00909
00910
00911
00912
00913 bool not_a_file = false;
00914 char *ptr = NULL;
00915 CURLcode ret = curl_easy_getinfo( _curl,
00916 CURLINFO_EFFECTIVE_URL,
00917 &ptr);
00918 if ( ret == CURLE_OK && ptr != NULL)
00919 {
00920 try
00921 {
00922 Url eurl( ptr);
00923 std::string path( eurl.getPathName());
00924 if( !path.empty() && path != "/" && *path.rbegin() == '/')
00925 {
00926 DBG << "Effective url ("
00927 << eurl
00928 << ") seems to provide the index of a directory"
00929 << endl;
00930 not_a_file = true;
00931 }
00932 }
00933 catch( ... )
00934 {}
00935 }
00936
00937 if( not_a_file)
00938 {
00939 ::fclose( file );
00940 filesystem::unlink( destNew );
00941 ZYPP_THROW(MediaNotAFileException(_url, filename));
00942 }
00943 }
00944 #endif // DETECT_DIR_INDEX
00945
00946 mode_t mask;
00947
00948
00949 mask = ::umask(0022); ::umask(mask);
00950 if ( ::fchmod( ::fileno(file), 0644 & ~mask))
00951 {
00952 ERR << "Failed to chmod file " << destNew << endl;
00953 }
00954 ::fclose( file );
00955
00956 if ( rename( destNew, dest ) != 0 ) {
00957 ERR << "Rename failed" << endl;
00958 ZYPP_THROW(MediaWriteException(dest));
00959 }
00960 }
00961
00962
00964
00965
00966
00967
00968
00969
00970
00971 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
00972 {
00973 filesystem::DirContent content;
00974 getDirInfo( content, dirname, false );
00975
00976 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
00977 Pathname filename = dirname + it->name;
00978 int res = 0;
00979
00980 switch ( it->type ) {
00981 case filesystem::FT_NOT_AVAIL:
00982 case filesystem::FT_FILE:
00983 getFile( filename );
00984 break;
00985 case filesystem::FT_DIR:
00986 if ( recurse_r ) {
00987 getDir( filename, recurse_r );
00988 } else {
00989 res = assert_dir( localPath( filename ) );
00990 if ( res ) {
00991 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
00992 }
00993 }
00994 break;
00995 default:
00996
00997 break;
00998 }
00999 }
01000 }
01001
01003
01004
01005
01006
01007
01008
01009
01010 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01011 const Pathname & dirname, bool dots ) const
01012 {
01013 getDirectoryYast( retlist, dirname, dots );
01014 }
01015
01017
01018
01019
01020
01021
01022
01023
01024 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01025 const Pathname & dirname, bool dots ) const
01026 {
01027 getDirectoryYast( retlist, dirname, dots );
01028 }
01029
01031
01032
01033
01034
01035
01036
01037
01038 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
01039 double ultotal, double ulnow )
01040 {
01041 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01042 if( pdata)
01043 {
01044
01045 if( pdata->report)
01046 {
01047 if (! (*(pdata->report))->progress(int( dlnow * 100 / dltotal ), pdata->url))
01048 {
01049 return 1;
01050 }
01051 }
01052
01053
01054 if( pdata->timeout > 0)
01055 {
01056 time_t now = time(NULL);
01057 if( now > 0)
01058 {
01059 bool progress = false;
01060
01061
01062
01063 if( pdata->ltime <= 0 || pdata->ltime > now)
01064 pdata->ltime = now;
01065
01066
01067 if( dlnow != pdata->dload)
01068 {
01069 progress = true;
01070 pdata->dload = dlnow;
01071 pdata->ltime = now;
01072 }
01073
01074 if( ulnow != pdata->uload)
01075 {
01076 progress = true;
01077 pdata->uload = ulnow;
01078 pdata->ltime = now;
01079 }
01080
01081 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01082 {
01083 pdata->reached = true;
01084 return 1;
01085 }
01086 }
01087 }
01088 }
01089 return 0;
01090 }
01091
01092
01093 }
01094 }