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 #include "zypp/base/Gettext.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/MediaUserAuth.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 #include <boost/format.hpp>
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
00446 if(_url.getScheme() == "http" || _url.getScheme() == "https")
00447 {
00448 string use_auth = _url.getQueryParam("auth");
00449 if( use_auth.empty())
00450 use_auth = "digest,basic";
00451
00452 try
00453 {
00454 long auth = CurlAuthData::auth_type_str2long(use_auth);
00455 if( auth != CURLAUTH_NONE)
00456 {
00457 DBG << "Enabling HTTP authentication methods: " << use_auth
00458 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
00459
00460 ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
00461 if ( ret != 0 ) {
00462 disconnectFrom();
00463 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00464 }
00465 }
00466 }
00467 catch (MediaException & ex_r)
00468 {
00469 string auth_hint = getAuthHint();
00470
00471 DBG << "Rethrowing as MediaUnauthorizedException. auth hint: '"
00472 << auth_hint << "'" << endl;
00473
00474 ZYPP_THROW(MediaUnauthorizedException(
00475 _url, ex_r.msg(), _curlError, auth_hint
00476 ));
00477 }
00478 }
00479 }
00480
00481
00482
00483
00484
00485
00486
00487
00488 _proxy = _url.getQueryParam( "proxy" );
00489
00490 if ( ! _proxy.empty() ) {
00491 string proxyport( _url.getQueryParam( "proxyport" ) );
00492 if ( ! proxyport.empty() ) {
00493 _proxy += ":" + proxyport;
00494 }
00495 } else {
00496
00497 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
00498
00499 if ( proxy_info.enabled())
00500 {
00501 bool useproxy = true;
00502
00503 std::list<std::string> nope = proxy_info.noProxy();
00504 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
00505 it != proxy_info.noProxyEnd();
00506 it++)
00507 {
00508 std::string host( str::toLower(_url.getHost()));
00509 std::string temp( str::toLower(*it));
00510
00511
00512
00513
00514 if( temp.size() > 1 && temp.at(0) == '.')
00515 {
00516 if(host.size() > temp.size() &&
00517 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
00518 {
00519 DBG << "NO_PROXY: '" << *it << "' matches host '"
00520 << host << "'" << endl;
00521 useproxy = false;
00522 break;
00523 }
00524 }
00525 else
00526
00527 if( host == temp)
00528 {
00529 DBG << "NO_PROXY: '" << *it << "' matches host '"
00530 << host << "'" << endl;
00531 useproxy = false;
00532 break;
00533 }
00534 }
00535
00536 if ( useproxy ) {
00537 _proxy = proxy_info.proxy(_url.getScheme());
00538 }
00539 }
00540 }
00541
00542
00543 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
00544
00545 if ( ! _proxy.empty() ) {
00546
00547 ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
00548 if ( ret != 0 ) {
00549 disconnectFrom();
00550 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00551 }
00552
00553
00554
00555
00556
00557
00558
00559
00560 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
00561
00562 if ( ! _proxyuserpwd.empty() ) {
00563
00564 string proxypassword( _url.getQueryParam( "proxypassword" ) );
00565 if ( ! proxypassword.empty() ) {
00566 _proxyuserpwd += ":" + proxypassword;
00567 }
00568
00569 } else {
00570 char *home = getenv("HOME");
00571 if( home && *home)
00572 {
00573 Pathname curlrcFile = string( home ) + string( "/.curlrc" );
00574
00575 PathInfo h_info(string(home), PathInfo::LSTAT);
00576 PathInfo c_info(curlrcFile, PathInfo::LSTAT);
00577
00578 if( h_info.isDir() && h_info.owner() == getuid() &&
00579 c_info.isFile() && c_info.owner() == getuid())
00580 {
00581 map<string,string> rc_data = base::sysconfig::read( curlrcFile );
00582
00583 map<string,string>::const_iterator it = rc_data.find("--proxy-user");
00584 if (it != rc_data.end())
00585 _proxyuserpwd = it->second;
00586
00587 DBG << "got proxy userpwd (--proxy-user) from ~/culrc" << endl;
00588 }
00589 else
00590 {
00591 WAR << "Not allowed to parse '" << curlrcFile
00592 << "': bad file owner" << std::endl;
00593 }
00594 }
00595 }
00596
00597 _proxyuserpwd = unEscape( _proxyuserpwd );
00598 ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
00599 if ( ret != 0 ) {
00600 disconnectFrom();
00601 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00602 }
00603 }
00604
00605
00606
00607
00608 _currentCookieFile = _cookieFile.asString();
00609
00610 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
00611 _currentCookieFile.c_str() );
00612 if ( ret != 0 ) {
00613 disconnectFrom();
00614 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00615 }
00616
00617 ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
00618 _currentCookieFile.c_str() );
00619 if ( ret != 0 ) {
00620 disconnectFrom();
00621 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00622 }
00623
00624 ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
00625 &progressCallback );
00626 if ( ret != 0 ) {
00627 disconnectFrom();
00628 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00629 }
00630
00631 ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
00632 if ( ret != 0 ) {
00633 disconnectFrom();
00634 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00635 }
00636
00637
00638 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
00639 setMediaSource(media);
00640 }
00641
00642 bool
00643 MediaCurl::checkAttachPoint(const Pathname &apoint) const
00644 {
00645 return MediaHandler::checkAttachPoint( apoint, true, true);
00646 }
00647
00649
00650
00651
00652
00653
00654 void MediaCurl::disconnectFrom()
00655 {
00656 if ( _curl )
00657 {
00658 curl_easy_cleanup( _curl );
00659 _curl = NULL;
00660 }
00661 }
00662
00664
00665
00666
00667
00668
00669
00670
00671 void MediaCurl::releaseFrom( bool eject )
00672 {
00673 disconnect();
00674 }
00675
00676
00678
00679
00680
00681
00682
00683 void MediaCurl::getFile( const Pathname & filename ) const
00684 {
00685
00686
00687 getFileCopy(filename, localPath(filename).absolutename());
00688 }
00689
00690
00691 void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
00692 {
00693 callback::SendReport<DownloadProgressReport> report;
00694
00695 Url url( _url );
00696
00697 bool retry = false;
00698 CurlAuthData auth_data;
00699
00700 do
00701 {
00702 try
00703 {
00704 doGetFileCopy(filename, target, report);
00705 retry = false;
00706 }
00707
00708 catch (MediaUnauthorizedException & ex_r)
00709 {
00710 callback::SendReport<AuthenticationReport> auth_report;
00711
00712 if (!_url.getUsername().empty() && !retry)
00713 auth_data.setUserName(_url.getUsername());
00714
00715 string prompt_msg;
00716 if (retry || !_url.getUsername().empty())
00717 prompt_msg = _("Invalid user name or password.");
00718 else
00719 prompt_msg = boost::str(boost::format(
00720 _("Authentication required for '%s'")) % _url.asString());
00721
00722
00723 auth_data.setAuthType(ex_r.hint());
00724
00725 if (auth_report->prompt(_url, prompt_msg, auth_data))
00726 {
00727 DBG << "callback answer: retry" << endl
00728 << "CurlAuthData: " << auth_data << endl;
00729
00730 if (auth_data.valid()) {
00731 _userpwd = auth_data.getUserPwd();
00732
00733
00734 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
00735 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00736
00737
00738 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
00739 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00740 }
00741
00742 retry = true;
00743 }
00744 else
00745 {
00746 DBG << "callback answer: cancel" << endl;
00747 report->finish(url, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserString());
00748 ZYPP_RETHROW(ex_r);
00749 }
00750 }
00751
00752 catch (MediaException & excpt_r)
00753 {
00754
00755
00756 report->finish(url, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserString());
00757 ZYPP_RETHROW(excpt_r);
00758 }
00759 }
00760 while (retry);
00761
00762 report->finish(url, zypp::media::DownloadProgressReport::NO_ERROR, "");
00763 }
00764
00765 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
00766 {
00767 bool retry = false;
00768 CurlAuthData auth_data;
00769
00770 do
00771 {
00772 try
00773 {
00774 return doGetDoesFileExist( filename );
00775 }
00776
00777 catch (MediaUnauthorizedException & ex_r)
00778 {
00779 callback::SendReport<AuthenticationReport> auth_report;
00780
00781 if (!_url.getUsername().empty() && !retry)
00782 auth_data.setUserName(_url.getUsername());
00783
00784 string prompt_msg;
00785 if (retry || !_url.getUsername().empty())
00786 prompt_msg = _("Invalid user name or password.");
00787 else
00788 prompt_msg = boost::str(boost::format(
00789 _("Authentication required for '%s'")) % _url.asString());
00790
00791
00792 auth_data.setAuthType(ex_r.hint());
00793
00794 if (auth_report->prompt(_url, prompt_msg, auth_data))
00795 {
00796 DBG << "callback answer: retry" << endl
00797 << "CurlAuthData: " << auth_data << endl;
00798
00799 if (auth_data.valid()) {
00800 _userpwd = auth_data.getUserPwd();
00801
00802
00803 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
00804 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00805
00806
00807 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
00808 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
00809 }
00810
00811 retry = true;
00812 }
00813 else
00814 {
00815 DBG << "callback answer: cancel" << endl;
00816 ZYPP_RETHROW(ex_r);
00817 }
00818 }
00819
00820 catch (MediaException & excpt_r)
00821 {
00822 ZYPP_RETHROW(excpt_r);
00823 }
00824 }
00825 while (retry);
00826
00827 return false;
00828 }
00829
00830 bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
00831 {
00832 DBG << filename.asString() << endl;
00833
00834 if(!_url.isValid())
00835 ZYPP_THROW(MediaBadUrlException(_url));
00836
00837 if(_url.getHost().empty())
00838 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00839
00840 string path = _url.getPathName();
00841 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
00842 filename.absolute() ) {
00843
00844
00845 path += filename.asString().substr( 1, filename.asString().size() - 1 );
00846 } else if ( filename.relative() ) {
00847
00848 if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
00849
00850 path += filename.asString().substr( 2, filename.asString().size() - 2 );
00851 } else {
00852 path += filename.asString();
00853 }
00854
00855 Url url( _url );
00856 url.setPathName( path );
00857
00858 DBG << "URL: " << url.asString() << endl;
00859
00860
00861
00862
00863 Url curlUrl( url );
00864
00865
00866 curlUrl.setUsername( "" );
00867 curlUrl.setPassword( "" );
00868 curlUrl.setPathParams( "" );
00869 curlUrl.setQueryString( "" );
00870 curlUrl.setFragment( "" );
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880 string urlBuffer( curlUrl.asString());
00881 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
00882 urlBuffer.c_str() );
00883 if ( ret != 0 ) {
00884 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00885 }
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
00898 if ( ret != 0 ) {
00899 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00900 }
00901
00902 FILE *file = ::fopen( "/dev/null", "w" );
00903 if ( !file ) {
00904 ::fclose(file);
00905 ERR << "fopen failed for /dev/null" << endl;
00906 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00907 if ( ret != 0 ) {
00908 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00909 }
00910 ZYPP_THROW(MediaWriteException("/dev/null"));
00911 }
00912
00913 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
00914 if ( ret != 0 ) {
00915 ::fclose(file);
00916 std::string err( _curlError);
00917 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00918 if ( ret != 0 ) {
00919 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00920 }
00921 ZYPP_THROW(MediaCurlSetOptException(url, err));
00922 }
00923
00924
00925
00926
00927
00928
00929
00930 CURLcode ok = curl_easy_perform( _curl );
00931 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
00932
00933
00934 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
00935 if ( ret != 0 )
00936 {
00937 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
00938 }
00939
00940 if ( ok != 0 )
00941 {
00942 ::fclose( file );
00943
00944 std::string err;
00945 try
00946 {
00947 bool err_file_not_found = false;
00948 switch ( ok )
00949 {
00950 case CURLE_FTP_COULDNT_RETR_FILE:
00951 case CURLE_FTP_ACCESS_DENIED:
00952 err_file_not_found = true;
00953 break;
00954 case CURLE_HTTP_RETURNED_ERROR:
00955 {
00956 long httpReturnCode = 0;
00957 CURLcode infoRet = curl_easy_getinfo( _curl,
00958 CURLINFO_RESPONSE_CODE,
00959 &httpReturnCode );
00960 if ( infoRet == CURLE_OK )
00961 {
00962 string msg = "HTTP response: " +
00963 str::numstring( httpReturnCode );
00964 if ( httpReturnCode == 401 )
00965 {
00966 std::string auth_hint = getAuthHint();
00967
00968 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
00969 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
00970
00971 ZYPP_THROW(MediaUnauthorizedException(
00972 url, "Login failed.", _curlError, auth_hint
00973 ));
00974 }
00975 else
00976 if ( httpReturnCode == 404)
00977 {
00978 err_file_not_found = true;
00979 break;
00980 }
00981
00982 msg += err;
00983 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00984 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00985 }
00986 else
00987 {
00988 string msg = "Unable to retrieve HTTP response:";
00989 msg += err;
00990 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
00991 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
00992 }
00993 }
00994 break;
00995 case CURLE_UNSUPPORTED_PROTOCOL:
00996 case CURLE_URL_MALFORMAT:
00997 case CURLE_URL_MALFORMAT_USER:
00998 case CURLE_BAD_PASSWORD_ENTERED:
00999 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01000 case CURLE_COULDNT_RESOLVE_PROXY:
01001 case CURLE_COULDNT_RESOLVE_HOST:
01002 case CURLE_COULDNT_CONNECT:
01003 case CURLE_FTP_CANT_GET_HOST:
01004 case CURLE_WRITE_ERROR:
01005 case CURLE_ABORTED_BY_CALLBACK:
01006 case CURLE_SSL_PEER_CERTIFICATE:
01007 default:
01008 err = curl_easy_strerror(ok);
01009 if (err.empty())
01010 err = "Unrecognized error";
01011 break;
01012 }
01013
01014 if( err_file_not_found)
01015 {
01016
01017 return false;
01018 }
01019 else
01020 {
01021
01022 ZYPP_THROW(MediaCurlException(url, string(), _curlError));
01023 }
01024 }
01025 catch (const MediaException & excpt_r)
01026 {
01027 ZYPP_RETHROW(excpt_r);
01028 }
01029 }
01030
01031
01032 return ( ok == CURLE_OK );
01033
01034
01035
01036 }
01037
01038 void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
01039 {
01040 DBG << filename.asString() << endl;
01041
01042 if(!_url.isValid())
01043 ZYPP_THROW(MediaBadUrlException(_url));
01044
01045 if(_url.getHost().empty())
01046 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
01047
01048 string path = _url.getPathName();
01049 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
01050 filename.absolute() ) {
01051
01052
01053 path += filename.asString().substr( 1, filename.asString().size() - 1 );
01054 } else if ( filename.relative() ) {
01055
01056 if (path.empty()) path = "/";
01057 else if (*path.rbegin() != '/' ) path += "/";
01058
01059 path += filename.asString().substr( 2, filename.asString().size() - 2 );
01060 } else {
01061 path += filename.asString();
01062 }
01063
01064 Url url( _url );
01065 url.setPathName( path );
01066
01067 Pathname dest = target.absolutename();
01068 if( assert_dir( dest.dirname() ) )
01069 {
01070 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
01071 ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
01072 }
01073
01074 DBG << "URL: " << url.asString() << endl;
01075
01076
01077
01078
01079 Url curlUrl( url );
01080
01081
01082 curlUrl.setUsername( "" );
01083 curlUrl.setPassword( "" );
01084 curlUrl.setPathParams( "" );
01085 curlUrl.setQueryString( "" );
01086 curlUrl.setFragment( "" );
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096 string urlBuffer( curlUrl.asString());
01097 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
01098 urlBuffer.c_str() );
01099 if ( ret != 0 ) {
01100 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01101 }
01102
01103 string destNew = target.asString() + ".new.zypp.XXXXXX";
01104 char *buf = ::strdup( destNew.c_str());
01105 if( !buf)
01106 {
01107 ERR << "out of memory for temp file name" << endl;
01108 ZYPP_THROW(MediaSystemException(
01109 url, "out of memory for temp file name"
01110 ));
01111 }
01112
01113 int tmp_fd = ::mkstemp( buf );
01114 if( tmp_fd == -1)
01115 {
01116 free( buf);
01117 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
01118 ZYPP_THROW(MediaWriteException(destNew));
01119 }
01120 destNew = buf;
01121 free( buf);
01122
01123 FILE *file = ::fdopen( tmp_fd, "w" );
01124 if ( !file ) {
01125 ::close( tmp_fd);
01126 filesystem::unlink( destNew );
01127 ERR << "fopen failed for file '" << destNew << "'" << endl;
01128 ZYPP_THROW(MediaWriteException(destNew));
01129 }
01130
01131 DBG << "dest: " << dest << endl;
01132 DBG << "temp: " << destNew << endl;
01133
01134 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
01135 if ( ret != 0 ) {
01136 ::fclose( file );
01137 filesystem::unlink( destNew );
01138 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
01139 }
01140
01141
01142 ProgressData progressData(_xfer_timeout, url, &report);
01143 report->start(url, dest);
01144 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
01145 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01146 }
01147
01148 ret = curl_easy_perform( _curl );
01149
01150 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
01151 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
01152 }
01153
01154 if ( ret != 0 ) {
01155 ERR << "curl error: " << ret << ": " << _curlError
01156 << ", temp file size " << PathInfo(destNew).size()
01157 << " byte." << endl;
01158
01159 ::fclose( file );
01160 filesystem::unlink( destNew );
01161
01162 std::string err;
01163 try {
01164 bool err_file_not_found = false;
01165 switch ( ret ) {
01166 case CURLE_UNSUPPORTED_PROTOCOL:
01167 case CURLE_URL_MALFORMAT:
01168 case CURLE_URL_MALFORMAT_USER:
01169 err = " Bad URL";
01170 case CURLE_HTTP_RETURNED_ERROR:
01171 {
01172 long httpReturnCode = 0;
01173 CURLcode infoRet = curl_easy_getinfo( _curl,
01174 CURLINFO_RESPONSE_CODE,
01175 &httpReturnCode );
01176 if ( infoRet == CURLE_OK ) {
01177 string msg = "HTTP response: " +
01178 str::numstring( httpReturnCode );
01179 if ( httpReturnCode == 401 )
01180 {
01181 std::string auth_hint = getAuthHint();
01182
01183 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
01184 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
01185
01186 ZYPP_THROW(MediaUnauthorizedException(
01187 url, "Login failed.", _curlError, auth_hint
01188 ));
01189 }
01190 else
01191 if ( httpReturnCode == 404)
01192 {
01193 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01194 }
01195
01196 msg += err;
01197 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01198 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01199 }
01200 else
01201 {
01202 string msg = "Unable to retrieve HTTP response:";
01203 msg += err;
01204 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
01205 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
01206 }
01207 }
01208 break;
01209 case CURLE_FTP_COULDNT_RETR_FILE:
01210 case CURLE_FTP_ACCESS_DENIED:
01211 err = "File not found";
01212 err_file_not_found = true;
01213 break;
01214 case CURLE_BAD_PASSWORD_ENTERED:
01215 case CURLE_FTP_USER_PASSWORD_INCORRECT:
01216 err = "Login failed";
01217 break;
01218 case CURLE_COULDNT_RESOLVE_PROXY:
01219 case CURLE_COULDNT_RESOLVE_HOST:
01220 case CURLE_COULDNT_CONNECT:
01221 case CURLE_FTP_CANT_GET_HOST:
01222 err = "Connection failed";
01223 break;
01224 case CURLE_WRITE_ERROR:
01225 err = "Write error";
01226 break;
01227 case CURLE_ABORTED_BY_CALLBACK:
01228 if( progressData.reached)
01229 {
01230 err = "Timeout reached";
01231 }
01232 else
01233 {
01234 err = "User abort";
01235 }
01236 break;
01237 case CURLE_SSL_PEER_CERTIFICATE:
01238 default:
01239 err = "Unrecognized error";
01240 break;
01241 }
01242 if( err_file_not_found)
01243 {
01244 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
01245 }
01246 else
01247 {
01248 ZYPP_THROW(MediaCurlException(url, err, _curlError));
01249 }
01250 }
01251 catch (const MediaException & excpt_r)
01252 {
01253 ZYPP_RETHROW(excpt_r);
01254 }
01255 }
01256 #if DETECT_DIR_INDEX
01257 else
01258 if(curlUrl.getScheme() == "http" ||
01259 curlUrl.getScheme() == "https")
01260 {
01261
01262
01263
01264
01265
01266
01267
01268
01269 bool not_a_file = false;
01270 char *ptr = NULL;
01271 CURLcode ret = curl_easy_getinfo( _curl,
01272 CURLINFO_EFFECTIVE_URL,
01273 &ptr);
01274 if ( ret == CURLE_OK && ptr != NULL)
01275 {
01276 try
01277 {
01278 Url eurl( ptr);
01279 std::string path( eurl.getPathName());
01280 if( !path.empty() && path != "/" && *path.rbegin() == '/')
01281 {
01282 DBG << "Effective url ("
01283 << eurl
01284 << ") seems to provide the index of a directory"
01285 << endl;
01286 not_a_file = true;
01287 }
01288 }
01289 catch( ... )
01290 {}
01291 }
01292
01293 if( not_a_file)
01294 {
01295 ::fclose( file );
01296 filesystem::unlink( destNew );
01297 ZYPP_THROW(MediaNotAFileException(_url, filename));
01298 }
01299 }
01300 #endif // DETECT_DIR_INDEX
01301
01302 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
01303 {
01304 ERR << "Failed to chmod file " << destNew << endl;
01305 }
01306 ::fclose( file );
01307
01308 if ( rename( destNew, dest ) != 0 ) {
01309 ERR << "Rename failed" << endl;
01310 ZYPP_THROW(MediaWriteException(dest));
01311 }
01312 DBG << "done: " << PathInfo(dest) << endl;
01313 }
01314
01315
01317
01318
01319
01320
01321
01322
01323
01324 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
01325 {
01326 filesystem::DirContent content;
01327 getDirInfo( content, dirname, false );
01328
01329 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
01330 Pathname filename = dirname + it->name;
01331 int res = 0;
01332
01333 switch ( it->type ) {
01334 case filesystem::FT_NOT_AVAIL:
01335 case filesystem::FT_FILE:
01336 getFile( filename );
01337 break;
01338 case filesystem::FT_DIR:
01339 if ( recurse_r ) {
01340 getDir( filename, recurse_r );
01341 } else {
01342 res = assert_dir( localPath( filename ) );
01343 if ( res ) {
01344 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
01345 }
01346 }
01347 break;
01348 default:
01349
01350 break;
01351 }
01352 }
01353 }
01354
01356
01357
01358
01359
01360
01361
01362
01363 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
01364 const Pathname & dirname, bool dots ) const
01365 {
01366 getDirectoryYast( retlist, dirname, dots );
01367 }
01368
01370
01371
01372
01373
01374
01375
01376
01377 void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
01378 const Pathname & dirname, bool dots ) const
01379 {
01380 getDirectoryYast( retlist, dirname, dots );
01381 }
01382
01384
01385
01386
01387
01388
01389
01390
01391 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow,
01392 double ultotal, double ulnow )
01393 {
01394 ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
01395 if( pdata)
01396 {
01397
01398 if( pdata->report)
01399 {
01400 if (! (*(pdata->report))->progress(int( dlnow * 100 / dltotal ), pdata->url))
01401 {
01402 return 1;
01403 }
01404 }
01405
01406
01407 if( pdata->timeout > 0)
01408 {
01409 time_t now = time(NULL);
01410 if( now > 0)
01411 {
01412 bool progress = false;
01413
01414
01415
01416 if( pdata->ltime <= 0 || pdata->ltime > now)
01417 pdata->ltime = now;
01418
01419
01420 if( dlnow != pdata->dload)
01421 {
01422 progress = true;
01423 pdata->dload = dlnow;
01424 pdata->ltime = now;
01425 }
01426
01427 if( ulnow != pdata->uload)
01428 {
01429 progress = true;
01430 pdata->uload = ulnow;
01431 pdata->ltime = now;
01432 }
01433
01434 if( !progress && (now >= (pdata->ltime + pdata->timeout)))
01435 {
01436 pdata->reached = true;
01437 return 1;
01438 }
01439 }
01440 }
01441 }
01442 return 0;
01443 }
01444
01445 string MediaCurl::getAuthHint() const
01446 {
01447 long auth_info = CURLAUTH_NONE;
01448
01449 CURLcode infoRet =
01450 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
01451
01452 if(infoRet == CURLE_OK)
01453 {
01454 return CurlAuthData::auth_type_long2str(auth_info);
01455 }
01456
01457 return "";
01458 }
01459
01460 }
01461 }
01462