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.getQueryParam("auth").empty() &&
00445 (_url.getScheme() == "http" || _url.getScheme() == "https"))
00446 {
00447 std::vector<std::string> list;
00448 std::vector<std::string>::const_iterator it;
00449 str::split(_url.getQueryParam("auth"), std::back_inserter(list), ",");
00450
00451 long auth = CURLAUTH_NONE;
00452 for(it = list.begin(); it != list.end(); ++it)
00453 {
00454 if(*it == "basic")
00455 {
00456 auth |= CURLAUTH_BASIC;
00457 }
00458 else
00459 if(*it == "digest")
00460 {
00461 auth |= CURLAUTH_DIGEST;
00462 }
00463 else
00464 if((curl_info && (curl_info->features & CURL_VERSION_NTLM)) &&
00465 (*it == "ntlm"))
00466 {
00467 auth |= CURLAUTH_NTLM;
00468 }
00469 else
00470 if((curl_info && (curl_info->features & CURL_VERSION_SPNEGO)) &&
00471 (*it == "spnego" || *it == "negotiate"))
00472 {
00473
00474 auth |= CURLAUTH_GSSNEGOTIATE;
00475 }
00476 else
00477 if((curl_info && (curl_info->features & CURL_VERSION_GSSNEGOTIATE)) &&
00478 (*it == "gssnego" || *it == "negotiate"))
00479 {
00480 auth |= CURLAUTH_GSSNEGOTIATE;
00481 }
00482 else
00483 {
00484 std::string msg("Unsupported HTTP authentication method '");
00485 msg += *it;
00486 msg += "'";
00487 disconnectFrom();
00488 ZYPP_THROW(MediaBadUrlException(_url, msg));
00489 }
00490 }
00491
00492 if( auth != CURLAUTH_NONE)
00493 {
00494 DBG << "Enabling HTTP authentication methods: "
00495 << _url.getQueryParam("auth") << std::endl;
00496
00497 ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
00498 if ( ret != 0 ) {
00499 disconnectFrom();
00500 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00501 }
00502 }
00503 }
00504 }
00505
00506
00507
00508
00509
00510
00511
00512
00513 _proxy = _url.getQueryParam( "proxy" );
00514
00515 if ( ! _proxy.empty() ) {
00516 string proxyport( _url.getQueryParam( "proxyport" ) );
00517 if ( ! proxyport.empty() ) {
00518 _proxy += ":" + proxyport;
00519 }
00520 } else {
00521
00522 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
00523
00524 if ( proxy_info.enabled())
00525 {
00526 bool useproxy = true;
00527
00528 std::list<std::string> nope = proxy_info.noProxy();
00529 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
00530 it != proxy_info.noProxyEnd();
00531 it++)
00532 {
00533 std::string host( str::toLower(_url.getHost()));
00534 std::string temp( str::toLower(*it));
00535
00536
00537
00538
00539 if( temp.size() > 1 && temp.at(0) == '.')
00540 {
00541 if(host.size() > temp.size() &&
00542 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
00543 {
00544 DBG << "NO_PROXY: '" << *it << "' matches host '"
00545 << host << "'" << endl;
00546 useproxy = false;
00547 break;
00548 }
00549 }
00550 else
00551
00552 if( host == temp)
00553 {
00554 DBG << "NO_PROXY: '" << *it << "' matches host '"
00555 << host << "'" << endl;
00556 useproxy = false;
00557 break;
00558 }
00559 }
00560
00561 if ( useproxy ) {
00562 _proxy = proxy_info.proxy(_url.getScheme());
00563 }
00564 }
00565 }
00566
00567
00568 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
00569
00570 if ( ! _proxy.empty() ) {
00571
00572 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
00573 if ( ret != 0 ) {
00574 disconnectFrom();
00575 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00576 }
00577
00578
00579
00580
00581
00582
00583
00584
00585 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
00586
00587 if ( ! _proxyuserpwd.empty() ) {
00588
00589 string proxypassword( _url.getQueryParam( "proxypassword" ) );
00590 if ( ! proxypassword.empty() ) {
00591 _proxyuserpwd += ":" + proxypassword;
00592 }
00593
00594 } else {
00595 char *home = getenv("HOME");
00596 if( home && *home)
00597 {
00598 Pathname curlrcFile = string( home ) + string( "/.curlrc" );
00599
00600 PathInfo h_info(string(home), PathInfo::LSTAT);
00601 PathInfo c_info(curlrcFile, PathInfo::LSTAT);
00602
00603 if( h_info.isDir() && h_info.owner() == getuid() &&
00604 c_info.isFile() && c_info.owner() == getuid())
00605 {
00606 map<string,string> rc_data = base::sysconfig::read( curlrcFile );
00607
00608 map<string,string>::const_iterator it = rc_data.find("proxy-user");
00609 if (it != rc_data.end())
00610 _proxyuserpwd = it->second;
00611 }
00612 else
00613 {
00614 WAR << "Not allowed to parse '" << curlrcFile
00615 << "': bad file owner" << std::endl;
00616 }
00617 }
00618 }
00619
00620 _proxyuserpwd = unEscape( _proxyuserpwd );
00621 ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
00622 if ( ret != 0 ) {
00623 disconnectFrom();
00624 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00625 }
00626 }
00627
00628
00629
00630
00631 _currentCookieFile = _cookieFile.asString();
00632
00633 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
00634 _currentCookieFile.c_str() );
00635 if ( ret != 0 ) {
00636 disconnectFrom();
00637 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00638 }
00639
00640 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
00641 _currentCookieFile.c_str() );
00642 if ( ret != 0 ) {
00643 disconnectFrom();
00644 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00645 }
00646
00647 ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
00648 &progressCallback );
00649 if ( ret != 0 ) {
00650 disconnectFrom();
00651 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00652 }
00653
00654 ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
00655 if ( ret != 0 ) {
00656 disconnectFrom();
00657 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00658 }
00659
00660
00661 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
00662 setMediaSource(media);
00663 }
00664
00665 bool
00666 MediaCurl::checkAttachPoint(const Pathname &apoint) const
00667 {
00668 return MediaHandler::checkAttachPoint( apoint, true, true);
00669 }
00670
00672
00673
00674
00675
00676
00677 void MediaCurl::disconnectFrom()
00678 {
00679 if ( _curl )
00680 {
00681 curl_easy_cleanup( _curl );
00682 _curl = NULL;
00683 }
00684 }
00685
00687
00688
00689
00690
00691
00692
00693
00694 void MediaCurl::releaseFrom( bool eject )
00695 {
00696 disconnect();
00697 }
00698
00699
00701
00702
00703
00704
00705
00706 void MediaCurl::getFile( const Pathname & filename ) const
00707 {
00708
00709
00710 getFileCopy(filename, localPath(filename).absolutename());
00711 }
00712
00713
00714 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
00715 {
00716 callback::SendReport<DownloadProgressReport> report;
00717
00718 Url url( _url );
00719
00720 try {
00721 doGetFileCopy(filename, target, report);
00722 }
00723 catch (MediaException & excpt_r)
00724 {
00725
00726
00727 report->finish(url, zypp::media::DownloadProgressReport::NOT_FOUND, excpt_r.msg());
00728 ZYPP_RETHROW(excpt_r);
00729 }
00730 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
00731 }
00732
00733 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
00734 {
00735 DBG << filename.asString() << endl;
00736
00737 if(!_url.isValid())
00738 ZYPP_THROW(MediaBadUrlException(_url));
00739
00740 if(_url.getHost().empty())
00741 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00742
00743 string path = _url.getPathName();
00744 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00745 filename.absolute() ) {
00746
00747
00748 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00749 } else if ( filename.relative() ) {
00750
00751 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00752
00753 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00754 } else {
00755 path += filename.asString();
00756 }
00757
00758 Url url( _url );
00759 url.setPathName( path );
00760
00761 DBG << "URL: " << url.asString() << endl;
00762
00763
00764
00765
00766 Url curlUrl( url );
00767
00768
00769 curlUrl.setUsername( "" );
00770 curlUrl.setPassword( "" );
00771 curlUrl.setPathParams( "" );
00772 curlUrl.setQueryString( "" );
00773 curlUrl.setFragment( "" );
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783 string urlBuffer( curlUrl.asString());
00784 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00785 urlBuffer.c_str() );
00786 if ( ret != 0 ) {
00787 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00788 }
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
00801 if ( ret != 0 ) {
00802 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00803 }
00804
00805 FILE *file = ::fopen( "/dev/null", "w" );
00806 if ( !file ) {
00807 ::fclose(file);
00808 ERR << "fopen failed for /dev/null" << endl;
00809 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00810 if ( ret != 0 ) {
00811 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00812 }
00813 ZYPP_THROW(MediaWriteException("/dev/null"));
00814 }
00815
00816 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00817 if ( ret != 0 ) {
00818 ::fclose(file);
00819 std::string err( _curlError);
00820 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00821 if ( ret != 0 ) {
00822 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00823 }
00824 ZYPP_THROW(MediaCurlSetOptException(url, err));
00825 }
00826
00827
00828
00829
00830
00831
00832
00833 CURLcode ok = curl_easy_perform( _curl );
00834 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
00835
00836 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00837 if ( ret != 0 ) {
00838 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00839 }
00840
00841 return ( ok == CURLE_OK );
00842
00843
00844
00845 }
00846
00847 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
00848 {
00849 DBG << filename.asString() << endl;
00850
00851 if(!_url.isValid())
00852 ZYPP_THROW(MediaBadUrlException(_url));
00853
00854 if(_url.getHost().empty())
00855 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00856
00857 string path = _url.getPathName();
00858 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00859 filename.absolute() ) {
00860
00861
00862 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00863 } else if ( filename.relative() ) {
00864
00865 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00866
00867 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00868 } else {
00869 path += filename.asString();
00870 }
00871
00872 Url url( _url );
00873 url.setPathName( path );
00874
00875 Pathname dest = target.absolutename();
00876 if( assert_dir( dest.dirname() ) )
00877 {
00878 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
00879 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
00880 }
00881
00882 DBG << "URL: " << url.asString() << endl;
00883
00884
00885
00886
00887 Url curlUrl( url );
00888
00889
00890 curlUrl.setUsername( "" );
00891 curlUrl.setPassword( "" );
00892 curlUrl.setPathParams( "" );
00893 curlUrl.setQueryString( "" );
00894 curlUrl.setFragment( "" );
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904 string urlBuffer( curlUrl.asString());
00905 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00906 urlBuffer.c_str() );
00907 if ( ret != 0 ) {
00908 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00909 }
00910
00911 string destNew = target.asString() + ".new.zypp.XXXXXX";
00912 char *buf = ::strdup( destNew.c_str());
00913 if( !buf)
00914 {
00915 ERR << "out of memory for temp file name" << endl;
00916 ZYPP_THROW(MediaSystemException(
00917 url, "out of memory for temp file name"
00918 ));
00919 }
00920
00921 int tmp_fd = ::mkstemp( buf );
00922 if( tmp_fd == -1)
00923 {
00924 free( buf);
00925 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
00926 ZYPP_THROW(MediaWriteException(destNew));
00927 }
00928 destNew = buf;
00929 free( buf);
00930
00931 FILE *file = ::fdopen( tmp_fd, "w" );
00932 if ( !file ) {
00933 ::close( tmp_fd);
00934 filesystem::unlink( destNew );
00935 ERR << "fopen failed for file '" << destNew << "'" << endl;
00936 ZYPP_THROW(MediaWriteException(destNew));
00937 }
00938
00939 DBG << "dest: " << dest << endl;
00940 DBG << "temp: " << destNew << endl;
00941
00942 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00943 if ( ret != 0 ) {
00944 ::fclose( file );
00945 filesystem::unlink( destNew );
00946 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00947 }
00948
00949
00950 ProgressData progressData(_xfer_timeout, url, &report);
00951 report->start(url, dest);
00952 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
00953 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
00954 }
00955
00956 ret = curl_easy_perform( _curl );
00957
00958 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
00959 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
00960 }
00961
00962 if ( ret != 0 ) {
00963 ERR << "curl error: " << ret << ": " << _curlError
00964 << ", temp file size " << PathInfo(destNew).size()
00965 << " byte." << endl;
00966
00967 ::fclose( file );
00968 filesystem::unlink( destNew );
00969
00970 std::string err;
00971 try {
00972 bool err_file_not_found = false;
00973 switch ( ret ) {
00974 case CURLE_UNSUPPORTED_PROTOCOL:
00975 case CURLE_URL_MALFORMAT:
00976 case CURLE_URL_MALFORMAT_USER:
00977 err = " Bad URL";
00978 case CURLE_HTTP_RETURNED_ERROR:
00979 {
00980 long httpReturnCode = 0;
00981 CURLcode infoRet = curl_easy_getinfo( _curl,
00982 CURLINFO_RESPONSE_CODE,
00983 &httpReturnCode );
00984 if ( infoRet == CURLE_OK ) {
00985 string msg = "HTTP response: " +
00986 str::numstring( httpReturnCode );
00987 if ( httpReturnCode == 401 )
00988 {
00989 err = " Login failed";
00990 }
00991 else
00992 if ( httpReturnCode == 404)
00993 {
00994 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
00995 }
00996
00997 msg += err;
00998 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00999 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01000 }
01001 else
01002 {
01003 string msg = "Unable to retrieve HTTP response:";
01004 msg += err;
01005 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01006 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01007 }
01008 }
01009 break;
01010 case CURLE_FTP_COULDNT_RETR_FILE:
01011 case CURLE_FTP_ACCESS_DENIED:
01012 err = "File not found";
01013 err_file_not_found = true;
01014 break;
01015 case CURLE_BAD_PASSWORD_ENTERED:
01016 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01017 err = "Login failed";
01018 break;
01019 case CURLE_COULDNT_RESOLVE_PROXY:
01020 case CURLE_COULDNT_RESOLVE_HOST:
01021 case CURLE_COULDNT_CONNECT:
01022 case CURLE_FTP_CANT_GET_HOST:
01023 err = "Connection failed";
01024 break;
01025 case CURLE_WRITE_ERROR:
01026 err = "Write error";
01027 break;
01028 case CURLE_ABORTED_BY_CALLBACK:
01029 if( progressData.reached)
01030 {
01031 err = "Timeout reached";
01032 }
01033 else
01034 {
01035 err = "User abort";
01036 }
01037 break;
01038 case CURLE_SSL_PEER_CERTIFICATE:
01039 default:
01040 err = "Unrecognized error";
01041 break;
01042 }
01043 if( err_file_not_found)
01044 {
01045 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01046 }
01047 else
01048 {
01049 ZYPP_THROW(MediaCurlException(url, err, _curlError));
01050 }
01051 }
01052 catch (const MediaException & excpt_r)
01053 {
01054 ZYPP_RETHROW(excpt_r);
01055 }
01056 }
01057 #if DETECT_DIR_INDEX
01058 else
01059 if(curlUrl.getScheme() == "http" ||
01060 curlUrl.getScheme() == "https")
01061 {
01062
01063
01064
01065
01066
01067
01068
01069
01070 bool not_a_file = false;
01071 char *ptr = NULL;
01072 CURLcode ret = curl_easy_getinfo( _curl,
01073 CURLINFO_EFFECTIVE_URL,
01074 &ptr);
01075 if ( ret == CURLE_OK && ptr != NULL)
01076 {
01077 try
01078 {
01079 Url eurl( ptr);
01080 std::string path( eurl.getPathName());
01081 if( !path.empty() && path != "/" && *path.rbegin() == '/')
01082 {
01083 DBG << "Effective url ("
01084 << eurl
01085 << ") seems to provide the index of a directory"
01086 << endl;
01087 not_a_file = true;
01088 }
01089 }
01090 catch( ... )
01091 {}
01092 }
01093
01094 if( not_a_file)
01095 {
01096 ::fclose( file );
01097 filesystem::unlink( destNew );
01098 ZYPP_THROW(MediaNotAFileException(_url, filename));
01099 }
01100 }
01101 #endif // DETECT_DIR_INDEX
01102
01103 mode_t mask;
01104
01105
01106 mask = ::umask(0022); ::umask(mask);
01107 if ( ::fchmod( ::fileno(file), 0644 & ~mask))
01108 {
01109 ERR << "Failed to chmod file " << destNew << endl;
01110 }
01111 ::fclose( file );
01112
01113 if ( rename( destNew, dest ) != 0 ) {
01114 ERR << "Rename failed" << endl;
01115 ZYPP_THROW(MediaWriteException(dest));
01116 }
01117 DBG << "done: " << PathInfo(dest) << endl;
01118 }
01119
01120
01122
01123
01124
01125
01126
01127
01128
01129 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
01130 {
01131 filesystem::DirContent content;
01132 getDirInfo( content, dirname, false );
01133
01134 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
01135 Pathname filename = dirname + it->name;
01136 int res = 0;
01137
01138 switch ( it->type ) {
01139 case filesystem::FT_NOT_AVAIL:
01140 case filesystem::FT_FILE:
01141 getFile( filename );
01142 break;
01143 case filesystem::FT_DIR:
01144 if ( recurse_r ) {
01145 getDir( filename, recurse_r );
01146 } else {
01147 res = assert_dir( localPath( filename ) );
01148 if ( res ) {
01149 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
01150 }
01151 }
01152 break;
01153 default:
01154
01155 break;
01156 }
01157 }
01158 }
01159
01161
01162
01163
01164
01165
01166
01167
01168 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01169 const Pathname & dirname, bool dots ) const
01170 {
01171 getDirectoryYast( retlist, dirname, dots );
01172 }
01173
01175
01176
01177
01178
01179
01180
01181
01182 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01183 const Pathname & dirname, bool dots ) const
01184 {
01185 getDirectoryYast( retlist, dirname, dots );
01186 }
01187
01189
01190
01191
01192
01193
01194
01195
01196 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
01197 double ultotal, double ulnow )
01198 {
01199 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01200 if( pdata)
01201 {
01202
01203 if( pdata->report)
01204 {
01205 if (! (*(pdata->report))->progress(int( dlnow * 100 / dltotal ), pdata->url))
01206 {
01207 return 1;
01208 }
01209 }
01210
01211
01212 if( pdata->timeout > 0)
01213 {
01214 time_t now = time(NULL);
01215 if( now > 0)
01216 {
01217 bool progress = false;
01218
01219
01220
01221 if( pdata->ltime <= 0 || pdata->ltime > now)
01222 pdata->ltime = now;
01223
01224
01225 if( dlnow != pdata->dload)
01226 {
01227 progress = true;
01228 pdata->dload = dlnow;
01229 pdata->ltime = now;
01230 }
01231
01232 if( ulnow != pdata->uload)
01233 {
01234 progress = true;
01235 pdata->uload = ulnow;
01236 pdata->ltime = now;
01237 }
01238
01239 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01240 {
01241 pdata->reached = true;
01242 return 1;
01243 }
01244 }
01245 }
01246 }
01247 return 0;
01248 }
01249
01250
01251 }
01252 }