1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaAria2c.cc
16 #include "zypp/base/Logger.h"
17 #include "zypp/ExternalProgram.h"
18 #include "zypp/ProgressData.h"
19 #include "zypp/base/String.h"
20 #include "zypp/base/Gettext.h"
21 #include "zypp/base/Sysconfig.h"
22 #include "zypp/base/Gettext.h"
23 #include "zypp/ZYppCallbacks.h"
25 #include "zypp/Target.h"
26 #include "zypp/ZYppFactory.h"
28 #include "zypp/media/MediaAria2c.h"
29 #include "zypp/media/proxyinfo/ProxyInfos.h"
30 #include "zypp/media/ProxyInfo.h"
31 #include "zypp/media/MediaUserAuth.h"
32 #include "zypp/thread/Once.h"
34 #include <sys/types.h>
36 #include <sys/mount.h>
40 #include <boost/format.hpp>
42 #define DETECT_DIR_INDEX 0
43 #define CONNECT_TIMEOUT 60
44 #define TRANSFER_TIMEOUT 60 * 3
45 #define TRANSFER_TIMEOUT_MAX 60 * 60
49 using namespace zypp::base;
56 Pathname MediaAria2c::_cookieFile = "/var/lib/YaST2/cookies";
57 Pathname MediaAria2c::_aria2cPath = "/usr/local/bin/aria2c";
58 std::string MediaAria2c::_aria2cVersion = "WE DON'T KNOW ARIA2C VERSION";
60 //check if aria2c is present in the system
62 MediaAria2c::existsAria2cmd()
72 ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
74 std::string ariaResponse( aria.receiveLine());
75 string::size_type pos = ariaResponse.find('/', 0 );
76 if( pos != string::npos )
82 static const char *const anonymousIdHeader()
84 // we need to add the release and identifier to the
86 // The target could be not initialized, and then this information
89 // FIXME this has to go away as soon as the target
90 // does not throw when not initialized.
92 target = zypp::getZYpp()->target();
94 catch ( const Exception &e )
99 static const std::string _value(
101 "X-Zypp-AnonymousId: %s",
102 target ? target->anonymousUniqueId().c_str() : "" )
104 return _value.c_str();
107 static const char *const distributionFlavorHeader()
109 // we need to add the release and identifier to the
111 // The target could be not initialized, and then this information
114 // FIXME this has to go away as soon as the target
115 // does not throw when not initialized.
117 target = zypp::getZYpp()->target();
119 catch ( const Exception &e )
124 static const std::string _value(
125 str::trim( str::form(
126 "X-ZYpp-DistributionFlavor: %s",
127 target ? target->distributionFlavor().c_str() : "" ) )
129 return _value.c_str();
132 const char *const MediaAria2c::agentString()
134 // we need to add the release and identifier to the
136 // The target could be not initialized, and then this information
139 // FIXME this has to go away as soon as the target
140 // does not throw when not initialized.
142 target = zypp::getZYpp()->target();
144 catch ( const Exception &e )
149 static const std::string _value(
153 , MediaAria2c::_aria2cVersion.c_str()
154 , target ? target->targetDistribution().c_str() : ""
157 return _value.c_str();
161 MediaAria2c::MediaAria2c( const Url & url_r,
162 const Pathname & attach_point_hint_r )
163 : MediaHandler( url_r, attach_point_hint_r,
164 "/", // urlpath at attachpoint
165 true ) // does_download
167 MIL << "MediaAria2c::MediaAria2c(" << url_r << ", " << attach_point_hint_r << ")" << endl;
169 if( !attachPoint().empty())
171 PathInfo ainfo(attachPoint());
172 Pathname apath(attachPoint() + "XXXXXX");
173 char *atemp = ::strdup( apath.asString().c_str());
175 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
176 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
178 WAR << "attach point " << ainfo.path()
179 << " is not useable for " << url_r.getScheme() << endl;
180 setAttachPoint("", true);
182 else if( atest != NULL)
189 //At this point, we initialize aria2c path
190 _aria2cPath = Pathname( whereisAria2c().asString() );
193 _aria2cVersion = getAria2cVersion();
196 void MediaAria2c::attachTo (bool next)
198 // clear last arguments
202 ZYPP_THROW(MediaNotSupportedException(_url));
204 if ( !_url.isValid() )
205 ZYPP_THROW(MediaBadUrlException(_url));
207 if( !isUseableAttachPoint(attachPoint()))
209 std::string mountpoint = createAttachPoint().asString();
211 if( mountpoint.empty())
212 ZYPP_THROW( MediaBadAttachPointException(url()));
214 setAttachPoint( mountpoint, true);
219 // Build the aria command.
220 _args.push_back(_aria2cPath.asString());
221 _args.push_back(str::form("--user-agent=%s", agentString()));
222 _args.push_back("--summary-interval=1");
223 _args.push_back("--follow-metalink=mem");
224 _args.push_back( "--check-integrity=true");
226 // add the anonymous id.
227 _args.push_back(str::form("--header=%s", anonymousIdHeader() ));
228 _args.push_back(str::form("--header=%s", distributionFlavorHeader() ));
229 // TODO add debug option
233 _xfer_timeout = TRANSFER_TIMEOUT;
235 std::string param(_url.getQueryParam("timeout"));
238 long num = str::strtonum<long>( param);
239 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
244 _args.push_back( str::form("--connect-timeout=%d", CONNECT_TIMEOUT));
246 // TODO limit redirections
247 // TODO Implement certificate validation
249 // FTP defaults to anonymous
252 if ( _url.getUsername().empty() )
254 if ( _url.getScheme() == "ftp" )
256 string id = "yast2@";
258 DBG << "Anonymous FTP identification: '" << id << "'" << endl;
259 _userpwd = "anonymous:" + id;
264 if ( _url.getScheme() == "ftp" )
266 _args.push_back(str::form("--ftp-user=%s", _url.getUsername().c_str() ));
268 else if ( _url.getScheme() == "http" ||
269 _url.getScheme() == "https" )
271 _args.push_back(str::form("--http-user=%s", _url.getUsername().c_str() ));
274 if ( _url.getPassword().size() )
276 if ( _url.getScheme() == "ftp" )
278 _args.push_back(str::form("--ftp-passwd=%s", _url.getPassword().c_str() ));
280 else if ( _url.getScheme() == "http" ||
281 _url.getScheme() == "https" )
283 _args.push_back(str::form("--http-passwd=%s", _url.getPassword().c_str() ));
288 // note, aria2c does not support setting the auth type with
289 // (basic, digest yet)
292 /*---------------------------------------------------------------*
293 CURLOPT_PROXY: host[:port]
295 Url::option(proxy and proxyport)
296 If not provided, /etc/sysconfig/proxy is evaluated
297 *---------------------------------------------------------------*/
299 _proxy = _url.getQueryParam( "proxy" );
301 if ( ! _proxy.empty() )
303 string proxyport( _url.getQueryParam( "proxyport" ) );
304 if ( ! proxyport.empty() ) {
305 _proxy += ":" + proxyport;
311 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
313 if ( proxy_info.enabled())
315 bool useproxy = true;
317 std::list<std::string> nope = proxy_info.noProxy();
318 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
319 it != proxy_info.noProxyEnd();
322 std::string host( str::toLower(_url.getHost()));
323 std::string temp( str::toLower(*it));
325 // no proxy if it points to a suffix
326 // preceeded by a '.', that maches
327 // the trailing portion of the host.
328 if( temp.size() > 1 && temp.at(0) == '.')
330 if(host.size() > temp.size() &&
331 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
333 DBG << "NO_PROXY: '" << *it << "' matches host '"
334 << host << "'" << endl;
340 // no proxy if we have an exact match
343 DBG << "NO_PROXY: '" << *it << "' matches host '"
344 << host << "'" << endl;
351 _proxy = proxy_info.proxy(_url.getScheme());
356 DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
358 if ( ! _proxy.empty() )
360 _args.push_back(str::form("--http-proxy=%s", _proxy.c_str() ));
362 /*---------------------------------------------------------------*
363 CURLOPT_PROXYUSERPWD: [user name]:[password]
365 Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
366 If not provided, $HOME/.curlrc is evaluated
367 *---------------------------------------------------------------*/
369 _proxyuserpwd = _url.getQueryParam( "proxyuser" );
371 if ( ! _proxyuserpwd.empty() ) {
372 _args.push_back(str::form("--http-proxy-user=%s", _proxyuserpwd.c_str() ));
374 string proxypassword( _url.getQueryParam( "proxypassword" ) );
375 if ( ! proxypassword.empty() ) {
376 _args.push_back(str::form("--http-proxy-passwd=%s", proxypassword.c_str() ));
381 //_currentCookieFile = _cookieFile.asString();
382 //_args.push_back(str::form("--load-cookies=%s", _currentCookieFile.c_str()));
385 // FIXME: need a derived class to propelly compare url's
386 MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
387 setMediaSource(media);
392 MediaAria2c::checkAttachPoint(const Pathname &apoint) const
394 return MediaHandler::checkAttachPoint( apoint, true, true);
397 void MediaAria2c::disconnectFrom()
401 void MediaAria2c::releaseFrom( const std::string & ejectDev )
406 static Url getFileUrl(const Url & url, const Pathname & filename)
409 string path = url.getPathName();
410 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
411 filename.absolute() )
413 // If url has a path with trailing slash, remove the leading slash from
414 // the absolute file name
415 path += filename.asString().substr( 1, filename.asString().size() - 1 );
417 else if ( filename.relative() )
419 // Add trailing slash to path, if not already there
420 if (path.empty()) path = "/";
421 else if (*path.rbegin() != '/' ) path += "/";
422 // Remove "./" from begin of relative file name
423 path += filename.asString().substr( 2, filename.asString().size() - 2 );
427 path += filename.asString();
430 newurl.setPathName(path);
434 void MediaAria2c::getFile( const Pathname & filename ) const
436 // Use absolute file name to prevent access of files outside of the
437 // hierarchy below the attach point.
438 getFileCopy(filename, localPath(filename).absolutename());
441 void MediaAria2c::getFileCopy( const Pathname & filename , const Pathname & target) const
443 callback::SendReport<DownloadProgressReport> report;
445 Url fileurl(getFileUrl(_url, filename));
449 ExternalProgram::Arguments args = _args;
450 args.push_back(str::form("--dir=%s", target.dirname().c_str()));
451 args.push_back(fileurl.asString());
457 report->start(_url, target.asString() );
459 ExternalProgram aria(args, ExternalProgram::Stderr_To_Stdout);
463 for(std::string ariaResponse( aria.receiveLine());
464 ariaResponse.length();
465 ariaResponse = aria.receiveLine())
467 //cout << ariaResponse;
469 if (!ariaResponse.substr(0,31).compare("Exception: Authorization failed") )
471 ZYPP_THROW(MediaUnauthorizedException(
472 _url, "Login failed.", "Login failed", "auth hint"
475 if (!ariaResponse.substr(0,29).compare("Exception: Resource not found") )
477 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
480 if (!ariaResponse.substr(0,9).compare("[#2 SIZE:")) {
484 size_t left_bound = ariaResponse.find('(',0) + 1;
485 size_t count = ariaResponse.find('%',left_bound) - left_bound;
486 //cout << ariaResponse.substr(left_bound, count) << endl;
487 //progressData.toMax();
488 report->progress ( std::atoi(ariaResponse.substr(left_bound, count).c_str()), _url, -1, -1 );
499 report->finish( _url , zypp::media::DownloadProgressReport::NO_ERROR, "");
503 // retry with proper authentication data
504 catch (MediaUnauthorizedException & ex_r)
506 if(authenticate(ex_r.hint(), !retry))
510 report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserString());
515 // unexpected exception
516 catch (MediaException & excpt_r)
518 // FIXME: error number fix
519 report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserString());
520 ZYPP_RETHROW(excpt_r);
525 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
528 bool MediaAria2c::getDoesFileExist( const Pathname & filename ) const
537 return doGetDoesFileExist( filename );
539 // authentication problem, retry with proper authentication data
540 catch (MediaUnauthorizedException & ex_r)
542 if(authenticate(ex_r.hint(), !retry))
547 // unexpected exception
548 catch (MediaException & excpt_r)
550 ZYPP_RETHROW(excpt_r);
558 bool MediaAria2c::doGetDoesFileExist( const Pathname & filename ) const
561 DBG << filename.asString() << endl;
565 void MediaAria2c::getDir( const Pathname & dirname, bool recurse_r ) const
567 filesystem::DirContent content;
568 getDirInfo( content, dirname, /*dots*/false );
570 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
571 Pathname filename = dirname + it->name;
574 switch ( it->type ) {
575 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
576 case filesystem::FT_FILE:
579 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
581 getDir( filename, recurse_r );
583 res = assert_dir( localPath( filename ) );
585 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
590 // don't provide devices, sockets, etc.
596 bool MediaAria2c::authenticate(const std::string & availAuthTypes, bool firstTry) const
602 void MediaAria2c::getDirInfo( std::list<std::string> & retlist,
603 const Pathname & dirname, bool dots ) const
605 getDirectoryYast( retlist, dirname, dots );
608 void MediaAria2c::getDirInfo( filesystem::DirContent & retlist,
609 const Pathname & dirname, bool dots ) const
611 getDirectoryYast( retlist, dirname, dots );
614 std::string MediaAria2c::getAria2cVersion()
623 ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
625 std::string vResponse = aria.receiveLine();
627 return str::trim(vResponse);
630 #define ARIA_DEFAULT_BINARY "/usr/bin/aria2c"
632 Pathname MediaAria2c::whereisAria2c()
634 Pathname aria2cPathr(ARIA_DEFAULT_BINARY);
644 ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
646 std::string ariaResponse( aria.receiveLine());
649 string::size_type pos = ariaResponse.find('/', 0 );
650 if( pos != string::npos )
652 aria2cPathr = ariaResponse;
653 string::size_type pose = ariaResponse.find(' ', pos + 1 );
654 aria2cPathr = ariaResponse.substr( pos , pose - pos );
655 MIL << "We will use aria2c located here: " << ariaResponse.substr( pos , pose - pos) << endl;
659 MIL << "We don't know were is ari2ac binary. We will use aria2c located here: " << aria2cPathr << endl;