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