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