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 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
00531 if ( proxy_info.useProxyFor( _url ) )
00532 _proxy = proxy_info.proxy( _url.getScheme() );
00533 }
00534
00535 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
00536
00537 if ( ! _proxy.empty() ) {
00538
00539 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
00540 if ( ret != 0 ) {
00541 disconnectFrom();
00542 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00543 }
00544
00545
00546
00547
00548
00549
00550
00551
00552 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
00553
00554 if ( ! _proxyuserpwd.empty() ) {
00555 string proxypassword( _url.getQueryParam( "proxypassword" ) );
00556 if ( ! proxypassword.empty() ) {
00557 _proxyuserpwd += ":" + proxypassword;
00558 }
00559 } else {
00560 if (curlconf.proxyuserpwd.empty())
00561 DBG << "~/.curlrc does not contain the proxy-user option" << endl;
00562 else
00563 {
00564 _proxyuserpwd = curlconf.proxyuserpwd;
00565 DBG << "using proxy-user from ~/.curlrc" << endl;
00566 }
00567 }
00568
00569 _proxyuserpwd = unEscape( _proxyuserpwd );
00570 ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
00571 if ( ret != 0 ) {
00572 disconnectFrom();
00573 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00574 }
00575 }
00576
00577
00578
00579
00580 _currentCookieFile = _cookieFile.asString();
00581
00582 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
00583 _currentCookieFile.c_str() );
00584 if ( ret != 0 ) {
00585 disconnectFrom();
00586 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00587 }
00588
00589 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
00590 _currentCookieFile.c_str() );
00591 if ( ret != 0 ) {
00592 disconnectFrom();
00593 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00594 }
00595
00596 ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
00597 &progressCallback );
00598 if ( ret != 0 ) {
00599 disconnectFrom();
00600 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00601 }
00602
00603 ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
00604 if ( ret != 0 ) {
00605 disconnectFrom();
00606 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00607 }
00608
00609
00610 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
00611 setMediaSource(media);
00612 }
00613
00614 bool
00615 MediaCurl::checkAttachPoint(const Pathname &apoint) const
00616 {
00617 return MediaHandler::checkAttachPoint( apoint, true, true);
00618 }
00619
00621
00622
00623
00624
00625
00626 void MediaCurl::disconnectFrom()
00627 {
00628 if ( _curl )
00629 {
00630 curl_easy_cleanup( _curl );
00631 _curl = NULL;
00632 }
00633 }
00634
00636
00637
00638
00639
00640
00641
00642
00643 void MediaCurl::releaseFrom( bool eject )
00644 {
00645 disconnect();
00646 }
00647
00648
00650
00651
00652
00653
00654
00655 void MediaCurl::getFile( const Pathname & filename ) const
00656 {
00657
00658
00659 getFileCopy(filename, localPath(filename).absolutename());
00660 }
00661
00662
00663 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
00664 {
00665 callback::SendReport<DownloadProgressReport> report;
00666
00667 Url url( _url );
00668
00669 try {
00670 doGetFileCopy(filename, target, report);
00671 }
00672 catch (MediaException & excpt_r)
00673 {
00674
00675
00676 report->finish(url, zypp::media::DownloadProgressReport::NOT_FOUND, excpt_r.msg());
00677 ZYPP_RETHROW(excpt_r);
00678 }
00679 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
00680 }
00681
00682 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
00683 {
00684 DBG << filename.asString() << endl;
00685
00686 if(!_url.isValid())
00687 ZYPP_THROW(MediaBadUrlException(_url));
00688
00689 if(_url.getHost().empty())
00690 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00691
00692 string path = _url.getPathName();
00693 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00694 filename.absolute() ) {
00695
00696
00697 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00698 } else if ( filename.relative() ) {
00699
00700 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00701
00702 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00703 } else {
00704 path += filename.asString();
00705 }
00706
00707 Url url( _url );
00708 url.setPathName( path );
00709
00710 DBG << "URL: " << url.asString() << endl;
00711
00712
00713
00714
00715 Url curlUrl( url );
00716
00717
00718 curlUrl.setUsername( "" );
00719 curlUrl.setPassword( "" );
00720 curlUrl.setPathParams( "" );
00721 curlUrl.setQueryString( "" );
00722 curlUrl.setFragment( "" );
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732 string urlBuffer( curlUrl.asString());
00733 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00734 urlBuffer.c_str() );
00735 if ( ret != 0 ) {
00736 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00737 }
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
00750 if ( ret != 0 ) {
00751 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00752 }
00753
00754 FILE *file = ::fopen( "/dev/null", "w" );
00755 if ( !file ) {
00756 ::fclose(file);
00757 ERR << "fopen failed for /dev/null" << endl;
00758 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00759 if ( ret != 0 ) {
00760 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00761 }
00762 ZYPP_THROW(MediaWriteException("/dev/null"));
00763 }
00764
00765 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00766 if ( ret != 0 ) {
00767 ::fclose(file);
00768 std::string err( _curlError);
00769 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00770 if ( ret != 0 ) {
00771 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00772 }
00773 ZYPP_THROW(MediaCurlSetOptException(url, err));
00774 }
00775
00776
00777
00778
00779
00780
00781
00782 CURLcode ok = curl_easy_perform( _curl );
00783 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
00784
00785
00786 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00787 if ( ret != 0 )
00788 {
00789 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00790 }
00791
00792 if ( ok != 0 )
00793 {
00794 ::fclose( file );
00795
00796 std::string err;
00797 try
00798 {
00799 bool err_file_not_found = false;
00800 switch ( ok )
00801 {
00802 case CURLE_FTP_COULDNT_RETR_FILE:
00803 case CURLE_FTP_ACCESS_DENIED:
00804 err_file_not_found = true;
00805 break;
00806 case CURLE_HTTP_RETURNED_ERROR:
00807 {
00808 long httpReturnCode = 0;
00809 CURLcode infoRet = curl_easy_getinfo( _curl,
00810 CURLINFO_RESPONSE_CODE,
00811 &httpReturnCode );
00812 if ( infoRet == CURLE_OK )
00813 {
00814 string msg = "HTTP response: " +
00815 str::numstring( httpReturnCode );
00816 if ( httpReturnCode == 401 )
00817 {
00818 err = " Login failed";
00819 }
00820 else
00821 if ( httpReturnCode == 403)
00822 {
00823 string msg403;
00824 if (url.asString().find("novell.com") != string::npos)
00825 msg403 = str::form(_(
00826 "Permission to access '%s' denied.\n\n"
00827 "Visit the Novell Customer Center to check whether"
00828 " your registration is valid and has not expired."),
00829 url.asString().c_str());
00830
00831 ZYPP_THROW(MediaForbiddenException(url, msg403));
00832 }
00833 else
00834 if ( httpReturnCode == 404)
00835 {
00836 err_file_not_found = true;
00837 break;
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_UNSUPPORTED_PROTOCOL:
00854 case CURLE_URL_MALFORMAT:
00855 case CURLE_URL_MALFORMAT_USER:
00856 case CURLE_BAD_PASSWORD_ENTERED:
00857 case CURLE_FTP_USER_PASSWORD_INCORRECT:
00858 case CURLE_COULDNT_RESOLVE_PROXY:
00859 case CURLE_COULDNT_RESOLVE_HOST:
00860 case CURLE_COULDNT_CONNECT:
00861 case CURLE_FTP_CANT_GET_HOST:
00862 case CURLE_WRITE_ERROR:
00863 case CURLE_ABORTED_BY_CALLBACK:
00864 case CURLE_SSL_PEER_CERTIFICATE:
00865 default:
00866 err = curl_easy_strerror(ok);
00867 if (err.empty())
00868 err = "Unrecognized error";
00869 break;
00870 }
00871
00872 if( err_file_not_found)
00873 {
00874
00875 return false;
00876 }
00877 else
00878 {
00879
00880 ZYPP_THROW(MediaCurlException(url, string(), _curlError));
00881 }
00882 }
00883 catch (const MediaException & excpt_r)
00884 {
00885 ZYPP_RETHROW(excpt_r);
00886 }
00887 }
00888
00889
00890 return ( ok == CURLE_OK );
00891
00892
00893
00894 }
00895
00896 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
00897 {
00898 DBG << filename.asString() << endl;
00899
00900 if(!_url.isValid())
00901 ZYPP_THROW(MediaBadUrlException(_url));
00902
00903 if(_url.getHost().empty())
00904 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00905
00906 string path = _url.getPathName();
00907 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00908 filename.absolute() ) {
00909
00910
00911 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00912 } else if ( filename.relative() ) {
00913
00914 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00915
00916 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00917 } else {
00918 path += filename.asString();
00919 }
00920
00921 Url url( _url );
00922 url.setPathName( path );
00923
00924 Pathname dest = target.absolutename();
00925 if( assert_dir( dest.dirname() ) )
00926 {
00927 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
00928 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
00929 }
00930
00931 DBG << "URL: " << url.asString() << endl;
00932
00933
00934
00935
00936 Url curlUrl( url );
00937
00938
00939 curlUrl.setUsername( "" );
00940 curlUrl.setPassword( "" );
00941 curlUrl.setPathParams( "" );
00942 curlUrl.setQueryString( "" );
00943 curlUrl.setFragment( "" );
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953 string urlBuffer( curlUrl.asString());
00954 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00955 urlBuffer.c_str() );
00956 if ( ret != 0 ) {
00957 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00958 }
00959
00960 string destNew = target.asString() + ".new.zypp.XXXXXX";
00961 char *buf = ::strdup( destNew.c_str());
00962 if( !buf)
00963 {
00964 ERR << "out of memory for temp file name" << endl;
00965 ZYPP_THROW(MediaSystemException(
00966 url, "out of memory for temp file name"
00967 ));
00968 }
00969
00970 int tmp_fd = ::mkstemp( buf );
00971 if( tmp_fd == -1)
00972 {
00973 free( buf);
00974 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
00975 ZYPP_THROW(MediaWriteException(destNew));
00976 }
00977 destNew = buf;
00978 free( buf);
00979
00980 FILE *file = ::fdopen( tmp_fd, "w" );
00981 if ( !file ) {
00982 ::close( tmp_fd);
00983 filesystem::unlink( destNew );
00984 ERR << "fopen failed for file '" << destNew << "'" << endl;
00985 ZYPP_THROW(MediaWriteException(destNew));
00986 }
00987
00988 DBG << "dest: " << dest << endl;
00989 DBG << "temp: " << destNew << endl;
00990
00991 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00992 if ( ret != 0 ) {
00993 ::fclose( file );
00994 filesystem::unlink( destNew );
00995 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00996 }
00997
00998
00999 ProgressData progressData(_xfer_timeout, url, &report);
01000 report->start(url, dest);
01001 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
01002 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01003 }
01004
01005 ret = curl_easy_perform( _curl );
01006
01007 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
01008 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01009 }
01010
01011 if ( ret != 0 ) {
01012 ERR << "curl error: " << ret << ": " << _curlError
01013 << ", temp file size " << PathInfo(destNew).size()
01014 << " byte." << endl;
01015
01016 ::fclose( file );
01017 filesystem::unlink( destNew );
01018
01019 std::string err;
01020 try {
01021 bool err_file_not_found = false;
01022 switch ( ret ) {
01023 case CURLE_UNSUPPORTED_PROTOCOL:
01024 case CURLE_URL_MALFORMAT:
01025 case CURLE_URL_MALFORMAT_USER:
01026 err = " Bad URL";
01027 case CURLE_HTTP_RETURNED_ERROR:
01028 {
01029 long httpReturnCode = 0;
01030 CURLcode infoRet = curl_easy_getinfo( _curl,
01031 CURLINFO_RESPONSE_CODE,
01032 &httpReturnCode );
01033 if ( infoRet == CURLE_OK ) {
01034 string msg = "HTTP response: " +
01035 str::numstring( httpReturnCode );
01036 if ( httpReturnCode == 401 )
01037 {
01038 err = " Login failed";
01039 }
01040 else
01041 if ( httpReturnCode == 403)
01042 {
01043 string msg403;
01044 if (url.asString().find("novell.com") != string::npos)
01045 msg403 = str::form(_(
01046 "Permission to access '%s' denied.\n\n"
01047 "Visit the Novell Customer Center to check whether"
01048 " your registration is valid and has not expired."),
01049 url.asString().c_str());
01050
01051 ZYPP_THROW(MediaForbiddenException(url, msg403));
01052 }
01053 else
01054 if ( httpReturnCode == 404)
01055 {
01056 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01057 }
01058
01059 msg += err;
01060 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01061 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01062 }
01063 else
01064 {
01065 string msg = "Unable to retrieve HTTP response:";
01066 msg += err;
01067 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01068 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01069 }
01070 }
01071 break;
01072 case CURLE_FTP_COULDNT_RETR_FILE:
01073 case CURLE_FTP_ACCESS_DENIED:
01074 err = "File not found";
01075 err_file_not_found = true;
01076 break;
01077 case CURLE_BAD_PASSWORD_ENTERED:
01078 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01079 err = "Login failed";
01080 break;
01081 case CURLE_COULDNT_RESOLVE_PROXY:
01082 case CURLE_COULDNT_RESOLVE_HOST:
01083 case CURLE_COULDNT_CONNECT:
01084 case CURLE_FTP_CANT_GET_HOST:
01085 err = "Connection failed";
01086 break;
01087 case CURLE_WRITE_ERROR:
01088 err = "Write error";
01089 break;
01090 case CURLE_ABORTED_BY_CALLBACK:
01091 if( progressData.reached)
01092 {
01093 err = "Timeout reached";
01094 }
01095 else
01096 {
01097 err = "User abort";
01098 }
01099 break;
01100 case CURLE_SSL_PEER_CERTIFICATE:
01101 default:
01102 err = "Unrecognized error";
01103 break;
01104 }
01105 if( err_file_not_found)
01106 {
01107 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01108 }
01109 else
01110 {
01111 ZYPP_THROW(MediaCurlException(url, err, _curlError));
01112 }
01113 }
01114 catch (const MediaException & excpt_r)
01115 {
01116 ZYPP_RETHROW(excpt_r);
01117 }
01118 }
01119 #if DETECT_DIR_INDEX
01120 else
01121 if(curlUrl.getScheme() == "http" ||
01122 curlUrl.getScheme() == "https")
01123 {
01124
01125
01126
01127
01128
01129
01130
01131
01132 bool not_a_file = false;
01133 char *ptr = NULL;
01134 CURLcode ret = curl_easy_getinfo( _curl,
01135 CURLINFO_EFFECTIVE_URL,
01136 &ptr);
01137 if ( ret == CURLE_OK && ptr != NULL)
01138 {
01139 try
01140 {
01141 Url eurl( ptr);
01142 std::string path( eurl.getPathName());
01143 if( !path.empty() && path != "/" && *path.rbegin() == '/')
01144 {
01145 DBG << "Effective url ("
01146 << eurl
01147 << ") seems to provide the index of a directory"
01148 << endl;
01149 not_a_file = true;
01150 }
01151 }
01152 catch( ... )
01153 {}
01154 }
01155
01156 if( not_a_file)
01157 {
01158 ::fclose( file );
01159 filesystem::unlink( destNew );
01160 ZYPP_THROW(MediaNotAFileException(_url, filename));
01161 }
01162 }
01163 #endif // DETECT_DIR_INDEX
01164
01165 mode_t mask;
01166
01167
01168 mask = ::umask(0022); ::umask(mask);
01169 if ( ::fchmod( ::fileno(file), 0644 & ~mask))
01170 {
01171 ERR << "Failed to chmod file " << destNew << endl;
01172 }
01173 ::fclose( file );
01174
01175 if ( rename( destNew, dest ) != 0 ) {
01176 ERR << "Rename failed" << endl;
01177 ZYPP_THROW(MediaWriteException(dest));
01178 }
01179 DBG << "done: " << PathInfo(dest) << endl;
01180 }
01181
01182
01184
01185
01186
01187
01188
01189
01190
01191 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
01192 {
01193 filesystem::DirContent content;
01194 getDirInfo( content, dirname, false );
01195
01196 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
01197 Pathname filename = dirname + it->name;
01198 int res = 0;
01199
01200 switch ( it->type ) {
01201 case filesystem::FT_NOT_AVAIL:
01202 case filesystem::FT_FILE:
01203 getFile( filename );
01204 break;
01205 case filesystem::FT_DIR:
01206 if ( recurse_r ) {
01207 getDir( filename, recurse_r );
01208 } else {
01209 res = assert_dir( localPath( filename ) );
01210 if ( res ) {
01211 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
01212 }
01213 }
01214 break;
01215 default:
01216
01217 break;
01218 }
01219 }
01220 }
01221
01223
01224
01225
01226
01227
01228
01229
01230 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01231 const Pathname & dirname, bool dots ) const
01232 {
01233 getDirectoryYast( retlist, dirname, dots );
01234 }
01235
01237
01238
01239
01240
01241
01242
01243
01244 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01245 const Pathname & dirname, bool dots ) const
01246 {
01247 getDirectoryYast( retlist, dirname, dots );
01248 }
01249
01251
01252
01253
01254
01255
01256
01257
01258 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
01259 double ultotal, double ulnow )
01260 {
01261 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01262 if( pdata)
01263 {
01264
01265 if( pdata->report)
01266 {
01267 if (! (*(pdata->report))->progress(int( dlnow * 100 / dltotal ), pdata->url))
01268 {
01269 return 1;
01270 }
01271 }
01272
01273
01274 if( pdata->timeout > 0)
01275 {
01276 time_t now = time(NULL);
01277 if( now > 0)
01278 {
01279 bool progress = false;
01280
01281
01282
01283 if( pdata->ltime <= 0 || pdata->ltime > now)
01284 pdata->ltime = now;
01285
01286
01287 if( dlnow != pdata->dload)
01288 {
01289 progress = true;
01290 pdata->dload = dlnow;
01291 pdata->ltime = now;
01292 }
01293
01294 if( ulnow != pdata->uload)
01295 {
01296 progress = true;
01297 pdata->uload = ulnow;
01298 pdata->ltime = now;
01299 }
01300
01301 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01302 {
01303 pdata->reached = true;
01304 return 1;
01305 }
01306 }
01307 }
01308 }
01309 return 0;
01310 }
01311
01312
01313 }
01314 }