00001
00002
00003
00004
00005
00006
00007
00008
00013 #include <iostream>
00014 #include <list>
00015
00016 #include "zypp/base/Logger.h"
00017 #include "zypp/ExternalProgram.h"
00018 #include "zypp/base/String.h"
00019 #include "zypp/base/Gettext.h"
00020 #include "zypp/base/Sysconfig.h"
00021
00022 #include "zypp/media/MediaCurl.h"
00023 #include "zypp/media/proxyinfo/ProxyInfos.h"
00024 #include "zypp/media/ProxyInfo.h"
00025 #include "zypp/media/CurlConfig.h"
00026 #include "zypp/thread/Once.h"
00027 #include <cstdlib>
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 #include <sys/mount.h>
00031 #include <errno.h>
00032 #include <dirent.h>
00033 #include <unistd.h>
00034
00035 #include "config.h"
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 if(_url.getScheme() == "http" || _url.getScheme() == "https")
00450 {
00451 std::vector<std::string> list;
00452 std::vector<std::string>::const_iterator it;
00453
00454 string use_auth = _url.getQueryParam("auth");
00455 if( use_auth.empty())
00456 use_auth = "digest,basic";
00457
00458 str::split(use_auth, std::back_inserter(list), ",");
00459
00460 long auth = CURLAUTH_NONE;
00461 for(it = list.begin(); it != list.end(); ++it)
00462 {
00463 if(*it == "basic")
00464 {
00465 auth |= CURLAUTH_BASIC;
00466 }
00467 else
00468 if(*it == "digest")
00469 {
00470 auth |= CURLAUTH_DIGEST;
00471 }
00472 else
00473 if((curl_info && (curl_info->features & CURL_VERSION_NTLM)) &&
00474 (*it == "ntlm"))
00475 {
00476 auth |= CURLAUTH_NTLM;
00477 }
00478 else
00479 if((curl_info && (curl_info->features & CURL_VERSION_SPNEGO)) &&
00480 (*it == "spnego" || *it == "negotiate"))
00481 {
00482
00483 auth |= CURLAUTH_GSSNEGOTIATE;
00484 }
00485 else
00486 if((curl_info && (curl_info->features & CURL_VERSION_GSSNEGOTIATE)) &&
00487 (*it == "gssnego" || *it == "negotiate"))
00488 {
00489 auth |= CURLAUTH_GSSNEGOTIATE;
00490 }
00491 else
00492 {
00493 std::string msg("Unsupported HTTP authentication method '");
00494 msg += *it;
00495 msg += "'";
00496 disconnectFrom();
00497 ZYPP_THROW(MediaBadUrlException(_url, msg));
00498 }
00499 }
00500
00501 if( auth != CURLAUTH_NONE)
00502 {
00503 DBG << "Enabling HTTP authentication methods: " << use_auth
00504 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
00505
00506 ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
00507 if ( ret != 0 ) {
00508 disconnectFrom();
00509 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00510 }
00511 }
00512 }
00513 }
00514
00515
00516
00517
00518
00519
00520
00521
00522 _proxy = _url.getQueryParam( "proxy" );
00523
00524 if ( ! _proxy.empty() ) {
00525 string proxyport( _url.getQueryParam( "proxyport" ) );
00526 if ( ! proxyport.empty() ) {
00527 _proxy += ":" + proxyport;
00528 }
00529 } else {
00530
00531 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
00532
00533 if ( proxy_info.enabled())
00534 {
00535 bool useproxy = true;
00536
00537 std::list<std::string> nope = proxy_info.noProxy();
00538 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
00539 it != proxy_info.noProxyEnd();
00540 it++)
00541 {
00542 std::string host( str::toLower(_url.getHost()));
00543 std::string temp( str::toLower(*it));
00544
00545
00546
00547
00548 if( temp.size() > 1 && temp.at(0) == '.')
00549 {
00550 if(host.size() > temp.size() &&
00551 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
00552 {
00553 DBG << "NO_PROXY: '" << *it << "' matches host '"
00554 << host << "'" << endl;
00555 useproxy = false;
00556 break;
00557 }
00558 }
00559 else
00560
00561 if( host == temp)
00562 {
00563 DBG << "NO_PROXY: '" << *it << "' matches host '"
00564 << host << "'" << endl;
00565 useproxy = false;
00566 break;
00567 }
00568 }
00569
00570 if ( useproxy ) {
00571 _proxy = proxy_info.proxy(_url.getScheme());
00572 }
00573 }
00574 }
00575
00576
00577 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
00578
00579 if ( ! _proxy.empty() ) {
00580
00581 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
00582 if ( ret != 0 ) {
00583 disconnectFrom();
00584 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00585 }
00586
00587
00588
00589
00590
00591
00592
00593
00594 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
00595
00596 if ( ! _proxyuserpwd.empty() ) {
00597 string proxypassword( _url.getQueryParam( "proxypassword" ) );
00598 if ( ! proxypassword.empty() ) {
00599 _proxyuserpwd += ":" + proxypassword;
00600 }
00601 } else {
00602 if (curlconf.proxyuserpwd.empty())
00603 DBG << "~/.curlrc does not contain the proxy-user option" << endl;
00604 else
00605 {
00606 _proxyuserpwd = curlconf.proxyuserpwd;
00607 DBG << "using proxy-user from ~/.curlrc" << endl;
00608 }
00609 }
00610
00611 _proxyuserpwd = unEscape( _proxyuserpwd );
00612 ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
00613 if ( ret != 0 ) {
00614 disconnectFrom();
00615 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00616 }
00617 }
00618
00619
00620
00621
00622 _currentCookieFile = _cookieFile.asString();
00623
00624 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
00625 _currentCookieFile.c_str() );
00626 if ( ret != 0 ) {
00627 disconnectFrom();
00628 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00629 }
00630
00631 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
00632 _currentCookieFile.c_str() );
00633 if ( ret != 0 ) {
00634 disconnectFrom();
00635 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00636 }
00637
00638 ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
00639 &progressCallback );
00640 if ( ret != 0 ) {
00641 disconnectFrom();
00642 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00643 }
00644
00645 ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
00646 if ( ret != 0 ) {
00647 disconnectFrom();
00648 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00649 }
00650
00651
00652 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
00653 setMediaSource(media);
00654 }
00655
00656 bool
00657 MediaCurl::checkAttachPoint(const Pathname &apoint) const
00658 {
00659 return MediaHandler::checkAttachPoint( apoint, true, true);
00660 }
00661
00663
00664
00665
00666
00667
00668 void MediaCurl::disconnectFrom()
00669 {
00670 if ( _curl )
00671 {
00672 curl_easy_cleanup( _curl );
00673 _curl = NULL;
00674 }
00675 }
00676
00678
00679
00680
00681
00682
00683
00684
00685 void MediaCurl::releaseFrom( bool eject )
00686 {
00687 disconnect();
00688 }
00689
00690
00692
00693
00694
00695
00696
00697 void MediaCurl::getFile( const Pathname & filename ) const
00698 {
00699
00700
00701 getFileCopy(filename, localPath(filename).absolutename());
00702 }
00703
00704
00705 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
00706 {
00707 callback::SendReport<DownloadProgressReport> report;
00708
00709 Url url( _url );
00710
00711 try {
00712 doGetFileCopy(filename, target, report);
00713 }
00714 catch (MediaException & excpt_r)
00715 {
00716
00717
00718 report->finish(url, zypp::media::DownloadProgressReport::NOT_FOUND, excpt_r.msg());
00719 ZYPP_RETHROW(excpt_r);
00720 }
00721 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
00722 }
00723
00724 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
00725 {
00726 DBG << filename.asString() << endl;
00727
00728 if(!_url.isValid())
00729 ZYPP_THROW(MediaBadUrlException(_url));
00730
00731 if(_url.getHost().empty())
00732 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00733
00734 string path = _url.getPathName();
00735 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00736 filename.absolute() ) {
00737
00738
00739 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00740 } else if ( filename.relative() ) {
00741
00742 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00743
00744 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00745 } else {
00746 path += filename.asString();
00747 }
00748
00749 Url url( _url );
00750 url.setPathName( path );
00751
00752 DBG << "URL: " << url.asString() << endl;
00753
00754
00755
00756
00757 Url curlUrl( url );
00758
00759
00760 curlUrl.setUsername( "" );
00761 curlUrl.setPassword( "" );
00762 curlUrl.setPathParams( "" );
00763 curlUrl.setQueryString( "" );
00764 curlUrl.setFragment( "" );
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774 string urlBuffer( curlUrl.asString());
00775 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00776 urlBuffer.c_str() );
00777 if ( ret != 0 ) {
00778 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00779 }
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
00792 if ( ret != 0 ) {
00793 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00794 }
00795
00796 FILE *file = ::fopen( "/dev/null", "w" );
00797 if ( !file ) {
00798 ::fclose(file);
00799 ERR << "fopen failed for /dev/null" << endl;
00800 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00801 if ( ret != 0 ) {
00802 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00803 }
00804 ZYPP_THROW(MediaWriteException("/dev/null"));
00805 }
00806
00807 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00808 if ( ret != 0 ) {
00809 ::fclose(file);
00810 std::string err( _curlError);
00811 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00812 if ( ret != 0 ) {
00813 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00814 }
00815 ZYPP_THROW(MediaCurlSetOptException(url, err));
00816 }
00817
00818
00819
00820
00821
00822
00823
00824 CURLcode ok = curl_easy_perform( _curl );
00825 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
00826
00827
00828 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00829 if ( ret != 0 )
00830 {
00831 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00832 }
00833
00834 if ( ok != 0 )
00835 {
00836 ::fclose( file );
00837
00838 std::string err;
00839 try
00840 {
00841 bool err_file_not_found = false;
00842 switch ( ok )
00843 {
00844 case CURLE_FTP_COULDNT_RETR_FILE:
00845 case CURLE_FTP_ACCESS_DENIED:
00846 err_file_not_found = true;
00847 break;
00848 case CURLE_HTTP_RETURNED_ERROR:
00849 {
00850 long httpReturnCode = 0;
00851 CURLcode infoRet = curl_easy_getinfo( _curl,
00852 CURLINFO_RESPONSE_CODE,
00853 &httpReturnCode );
00854 if ( infoRet == CURLE_OK )
00855 {
00856 string msg = "HTTP response: " +
00857 str::numstring( httpReturnCode );
00858 if ( httpReturnCode == 401 )
00859 {
00860 err = " Login failed";
00861 }
00862 else
00863 if ( httpReturnCode == 403)
00864 {
00865 string msg403;
00866 if (url.asString().find("novell.com") != string::npos)
00867 msg403 = str::form(_(
00868 "Permission to access '%s' denied.\n\n"
00869 "Visit the Novell Customer Center to check whether"
00870 " your registration is valid and has not expired."),
00871 url.asString().c_str());
00872
00873 ZYPP_THROW(MediaForbiddenException(url, msg403));
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 == 403)
01084 {
01085 string msg403;
01086 if (url.asString().find("novell.com") != string::npos)
01087 msg403 = str::form(_(
01088 "Permission to access '%s' denied.\n\n"
01089 "Visit the Novell Customer Center to check whether"
01090 " your registration is valid and has not expired."),
01091 url.asString().c_str());
01092
01093 ZYPP_THROW(MediaForbiddenException(url, msg403));
01094 }
01095 else
01096 if ( httpReturnCode == 404)
01097 {
01098 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01099 }
01100
01101 msg += err;
01102 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01103 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01104 }
01105 else
01106 {
01107 string msg = "Unable to retrieve HTTP response:";
01108 msg += err;
01109 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01110 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01111 }
01112 }
01113 break;
01114 case CURLE_FTP_COULDNT_RETR_FILE:
01115 case CURLE_FTP_ACCESS_DENIED:
01116 err = "File not found";
01117 err_file_not_found = true;
01118 break;
01119 case CURLE_BAD_PASSWORD_ENTERED:
01120 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01121 err = "Login failed";
01122 break;
01123 case CURLE_COULDNT_RESOLVE_PROXY:
01124 case CURLE_COULDNT_RESOLVE_HOST:
01125 case CURLE_COULDNT_CONNECT:
01126 case CURLE_FTP_CANT_GET_HOST:
01127 err = "Connection failed";
01128 break;
01129 case CURLE_WRITE_ERROR:
01130 err = "Write error";
01131 break;
01132 case CURLE_ABORTED_BY_CALLBACK:
01133 if( progressData.reached)
01134 {
01135 err = "Timeout reached";
01136 }
01137 else
01138 {
01139 err = "User abort";
01140 }
01141 break;
01142 case CURLE_SSL_PEER_CERTIFICATE:
01143 default:
01144 err = "Unrecognized error";
01145 break;
01146 }
01147 if( err_file_not_found)
01148 {
01149 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01150 }
01151 else
01152 {
01153 ZYPP_THROW(MediaCurlException(url, err, _curlError));
01154 }
01155 }
01156 catch (const MediaException & excpt_r)
01157 {
01158 ZYPP_RETHROW(excpt_r);
01159 }
01160 }
01161 #if DETECT_DIR_INDEX
01162 else
01163 if(curlUrl.getScheme() == "http" ||
01164 curlUrl.getScheme() == "https")
01165 {
01166
01167
01168
01169
01170
01171
01172
01173
01174 bool not_a_file = false;
01175 char *ptr = NULL;
01176 CURLcode ret = curl_easy_getinfo( _curl,
01177 CURLINFO_EFFECTIVE_URL,
01178 &ptr);
01179 if ( ret == CURLE_OK && ptr != NULL)
01180 {
01181 try
01182 {
01183 Url eurl( ptr);
01184 std::string path( eurl.getPathName());
01185 if( !path.empty() && path != "/" && *path.rbegin() == '/')
01186 {
01187 DBG << "Effective url ("
01188 << eurl
01189 << ") seems to provide the index of a directory"
01190 << endl;
01191 not_a_file = true;
01192 }
01193 }
01194 catch( ... )
01195 {}
01196 }
01197
01198 if( not_a_file)
01199 {
01200 ::fclose( file );
01201 filesystem::unlink( destNew );
01202 ZYPP_THROW(MediaNotAFileException(_url, filename));
01203 }
01204 }
01205 #endif // DETECT_DIR_INDEX
01206
01207 mode_t mask;
01208
01209
01210 mask = ::umask(0022); ::umask(mask);
01211 if ( ::fchmod( ::fileno(file), 0644 & ~mask))
01212 {
01213 ERR << "Failed to chmod file " << destNew << endl;
01214 }
01215 ::fclose( file );
01216
01217 if ( rename( destNew, dest ) != 0 ) {
01218 ERR << "Rename failed" << endl;
01219 ZYPP_THROW(MediaWriteException(dest));
01220 }
01221 DBG << "done: " << PathInfo(dest) << endl;
01222 }
01223
01224
01226
01227
01228
01229
01230
01231
01232
01233 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
01234 {
01235 filesystem::DirContent content;
01236 getDirInfo( content, dirname, false );
01237
01238 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
01239 Pathname filename = dirname + it->name;
01240 int res = 0;
01241
01242 switch ( it->type ) {
01243 case filesystem::FT_NOT_AVAIL:
01244 case filesystem::FT_FILE:
01245 getFile( filename );
01246 break;
01247 case filesystem::FT_DIR:
01248 if ( recurse_r ) {
01249 getDir( filename, recurse_r );
01250 } else {
01251 res = assert_dir( localPath( filename ) );
01252 if ( res ) {
01253 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
01254 }
01255 }
01256 break;
01257 default:
01258
01259 break;
01260 }
01261 }
01262 }
01263
01265
01266
01267
01268
01269
01270
01271
01272 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01273 const Pathname & dirname, bool dots ) const
01274 {
01275 getDirectoryYast( retlist, dirname, dots );
01276 }
01277
01279
01280
01281
01282
01283
01284
01285
01286 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01287 const Pathname & dirname, bool dots ) const
01288 {
01289 getDirectoryYast( retlist, dirname, dots );
01290 }
01291
01293
01294
01295
01296
01297
01298
01299
01300 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
01301 double ultotal, double ulnow )
01302 {
01303 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01304 if( pdata)
01305 {
01306
01307 if( pdata->report)
01308 {
01309 if (! (*(pdata->report))->progress(int( dlnow * 100 / dltotal ), pdata->url))
01310 {
01311 return 1;
01312 }
01313 }
01314
01315
01316 if( pdata->timeout > 0)
01317 {
01318 time_t now = time(NULL);
01319 if( now > 0)
01320 {
01321 bool progress = false;
01322
01323
01324
01325 if( pdata->ltime <= 0 || pdata->ltime > now)
01326 pdata->ltime = now;
01327
01328
01329 if( dlnow != pdata->dload)
01330 {
01331 progress = true;
01332 pdata->dload = dlnow;
01333 pdata->ltime = now;
01334 }
01335
01336 if( ulnow != pdata->uload)
01337 {
01338 progress = true;
01339 pdata->uload = ulnow;
01340 pdata->ltime = now;
01341 }
01342
01343 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01344 {
01345 pdata->reached = true;
01346 return 1;
01347 }
01348 }
01349 }
01350 }
01351 return 0;
01352 }
01353
01354
01355 }
01356 }