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/Edition.h"
26 #include "zypp/Target.h"
27 #include "zypp/ZYppFactory.h"
29 #include "zypp/media/TransferProgram.h"
30 #include "zypp/media/MediaAria2c.h"
31 #include "zypp/media/proxyinfo/ProxyInfos.h"
32 #include "zypp/media/ProxyInfo.h"
33 #include "zypp/media/MediaUserAuth.h"
34 #include "zypp/media/MediaCurl.h"
35 #include "zypp/thread/Once.h"
37 #include <sys/types.h>
39 #include <sys/mount.h>
43 #include <boost/format.hpp>
45 #define DETECT_DIR_INDEX 0
46 #define CONNECT_TIMEOUT 60
47 #define TRANSFER_TIMEOUT 60 * 3
48 #define TRANSFER_TIMEOUT_MAX 60 * 60
52 using namespace zypp::base;
59 Pathname MediaAria2c::_cookieFile = "/var/lib/YaST2/cookies";
60 Pathname MediaAria2c::_aria2cPath = "/usr/local/bin/aria2c";
61 std::string MediaAria2c::_aria2cVersion = "WE DON'T KNOW ARIA2C VERSION";
63 //check if aria2c is present in the system
65 MediaAria2c::existsAria2cmd()
74 ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
75 return ( aria.close() == 0 );
78 void fillSettingsFromUrl( const Url &url, TransferSettings &s )
80 std::string param(url.getQueryParam("timeout"));
83 long num = str::strtonum<long>(param);
84 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
88 if ( ! url.getUsername().empty() )
90 s.setUsername(url.getUsername());
91 if ( url.getPassword().size() )
93 s.setPassword(url.getPassword());
97 string proxy = url.getQueryParam( "proxy" );
99 if ( ! proxy.empty() )
101 string proxyport( url.getQueryParam( "proxyport" ) );
102 if ( ! proxyport.empty() ) {
103 proxy += ":" + proxyport;
106 s.setProxyEnabled(true);
110 void fillSettingsSystemProxy( const Url&url, TransferSettings &s )
112 ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
114 if ( proxy_info.enabled())
116 s.setProxyEnabled(true);
117 std::list<std::string> nope = proxy_info.noProxy();
118 for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
119 it != proxy_info.noProxyEnd();
122 std::string host( str::toLower(url.getHost()));
123 std::string temp( str::toLower(*it));
125 // no proxy if it points to a suffix
126 // preceeded by a '.', that maches
127 // the trailing portion of the host.
128 if( temp.size() > 1 && temp.at(0) == '.')
130 if(host.size() > temp.size() &&
131 host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
133 DBG << "NO_PROXY: '" << *it << "' matches host '"
134 << host << "'" << endl;
135 s.setProxyEnabled(false);
140 // no proxy if we have an exact match
143 DBG << "NO_PROXY: '" << *it << "' matches host '"
144 << host << "'" << endl;
145 s.setProxyEnabled(false);
150 if ( s.proxyEnabled() )
151 s.setProxy(proxy_info.proxy(url.getScheme()));
157 * comannd line for aria.
158 * The argument list gets passed as reference
161 void fillAriaCmdLine( const Pathname &ariapath,
162 const string &ariaver,
163 const TransferSettings &s,
165 const Pathname &destination,
166 ExternalProgram::Arguments &args )
168 args.push_back(ariapath.c_str());
169 args.push_back(str::form("--user-agent=%s", s.userAgentString().c_str()));
170 args.push_back("--summary-interval=1");
171 args.push_back("--follow-metalink=mem");
172 args.push_back("--check-integrity=true");
174 // only present in recent aria lets find out the aria version
175 vector<string> fields;
176 // "aria2c version x.x"
177 str::split( ariaver, std::back_inserter(fields));
178 if ( fields.size() == 3 )
180 if ( Edition(fields[2]) >= Edition("1.1.2") )
181 args.push_back( "--use-head=false");
184 if ( s.maxDownloadSpeed() > 0 )
185 args.push_back(str::form("--max-download-limit=%ld", s.maxDownloadSpeed()));
186 if ( s.minDownloadSpeed() > 0 )
187 args.push_back(str::form("--lowest-speed-limit=%ld", s.minDownloadSpeed()));
189 args.push_back(str::form("--max-tries=%ld", s.maxSilentTries()));
191 if ( Edition(fields[2]) < Edition("1.2.0") )
192 WAR << "aria2c is older than 1.2.0, some features may be disabled" << endl;
194 // TODO make this one configurable
195 args.push_back(str::form("--max-concurrent-downloads=%ld", s.maxConcurrentConnections()));
197 // add the anonymous id.
198 for ( TransferSettings::Headers::const_iterator it = s.headersBegin();
199 it != s.headersEnd();
201 args.push_back(str::form("--header=%s", it->c_str() ));
203 args.push_back( str::form("--connect-timeout=%ld", s.timeout()));
205 if ( s.username().empty() )
207 if ( url.getScheme() == "ftp" )
210 args.push_back(str::form("--ftp-user=%s", "suseuser" ));
211 args.push_back(str::form("--ftp-passwd=%s", VERSION ));
215 DBG << "Anonymous FTP identification: '" << id << "'" << endl;
220 if ( url.getScheme() == "ftp" )
221 args.push_back(str::form("--ftp-user=%s", s.username().c_str() ));
222 else if ( url.getScheme() == "http" ||
223 url.getScheme() == "https" )
224 args.push_back(str::form("--http-user=%s", s.username().c_str() ));
226 if ( s.password().size() )
228 if ( url.getScheme() == "ftp" )
229 args.push_back(str::form("--ftp-passwd=%s", s.password().c_str() ));
230 else if ( url.getScheme() == "http" ||
231 url.getScheme() == "https" )
232 args.push_back(str::form("--http-passwd=%s", s.password().c_str() ));
236 if ( s.proxyEnabled() )
238 args.push_back(str::form("--http-proxy=%s", s.proxy().c_str() ));
239 if ( ! s.proxyUsername().empty() )
241 args.push_back(str::form("--http-proxy-user=%s", s.proxyUsername().c_str() ));
242 if ( ! s.proxyPassword().empty() )
243 args.push_back(str::form("--http-proxy-passwd=%s", s.proxyPassword().c_str() ));
247 if ( ! destination.empty() )
248 args.push_back(str::form("--dir=%s", destination.c_str()));
250 args.push_back(url.asString().c_str());
253 static const char *const anonymousIdHeader()
255 // we need to add the release and identifier to the
257 // The target could be not initialized, and then this information
259 Target_Ptr target = zypp::getZYpp()->getTarget();
261 static const std::string _value(
263 "X-Zypp-AnonymousId: %s",
264 target ? target->anonymousUniqueId().c_str() : "" )
266 return _value.c_str();
269 static const char *const distributionFlavorHeader()
271 // we need to add the release and identifier to the
273 // The target could be not initialized, and then this information
275 Target_Ptr target = zypp::getZYpp()->getTarget();
277 static const std::string _value(
278 str::trim( str::form(
279 "X-ZYpp-DistributionFlavor: %s",
280 target ? target->distributionFlavor().c_str() : "" ) )
282 return _value.c_str();
285 const char *const MediaAria2c::agentString()
287 // we need to add the release and identifier to the
289 // The target could be not initialized, and then this information
291 Target_Ptr target = zypp::getZYpp()->getTarget();
293 static const std::string _value(
297 , MediaAria2c::_aria2cVersion.c_str()
298 , target ? target->targetDistribution().c_str() : ""
301 return _value.c_str();
306 MediaAria2c::MediaAria2c( const Url & url_r,
307 const Pathname & attach_point_hint_r )
308 : MediaCurl( url_r, attach_point_hint_r )
310 MIL << "MediaAria2c::MediaAria2c(" << url_r << ", " << attach_point_hint_r << ")" << endl;
313 if( !attachPoint().empty())
315 PathInfo ainfo(attachPoint());
316 Pathname apath(attachPoint() + "XXXXXX");
317 char *atemp = ::strdup( apath.asString().c_str());
319 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
320 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
322 WAR << "attach point " << ainfo.path()
323 << " is not useable for " << url_r.getScheme() << endl;
324 setAttachPoint("", true);
326 else if( atest != NULL)
334 //At this point, we initialize aria2c path
335 _aria2cPath = Pathname( whereisAria2c().asString() );
338 _aria2cVersion = getAria2cVersion();
341 void MediaAria2c::attachTo (bool next)
343 MediaCurl::attachTo(next);
345 _settings.setUserAgentString(agentString());
346 _settings.addHeader(anonymousIdHeader());
347 _settings.addHeader(distributionFlavorHeader());
349 _settings.setTimeout(TRANSFER_TIMEOUT);
350 _settings.setConnectTimeout(CONNECT_TIMEOUT);
352 // fill some settings from url query parameters
353 fillSettingsFromUrl(_url, _settings);
355 // if the proxy was not set by url, then look
356 if ( _settings.proxy().empty() )
358 // at the system proxy settings
359 fillSettingsSystemProxy(_url, _settings);
362 DBG << "Proxy: " << (_settings.proxy().empty() ? "-none-" : _settings.proxy()) << endl;
366 MediaAria2c::checkAttachPoint(const Pathname &apoint) const
368 return MediaCurl::checkAttachPoint( apoint );
371 void MediaAria2c::disconnectFrom()
373 MediaCurl::disconnectFrom();
377 void MediaAria2c::releaseFrom( const std::string & ejectDev )
379 MediaCurl::releaseFrom(ejectDev);
382 static Url getFileUrl(const Url & url, const Pathname & filename)
385 string path = url.getPathName();
386 if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
387 filename.absolute() )
389 // If url has a path with trailing slash, remove the leading slash from
390 // the absolute file name
391 path += filename.asString().substr( 1, filename.asString().size() - 1 );
393 else if ( filename.relative() )
395 // Add trailing slash to path, if not already there
396 if (path.empty()) path = "/";
397 else if (*path.rbegin() != '/' ) path += "/";
398 // Remove "./" from begin of relative file name
399 path += filename.asString().substr( 2, filename.asString().size() - 2 );
403 path += filename.asString();
406 newurl.setPathName(path);
410 void MediaAria2c::getFile( const Pathname & filename ) const
412 // Use absolute file name to prevent access of files outside of the
413 // hierarchy below the attach point.
414 getFileCopy(filename, localPath(filename).absolutename());
417 void MediaAria2c::getFileCopy( const Pathname & filename , const Pathname & target) const
419 callback::SendReport<DownloadProgressReport> report;
421 Url fileurl(getFileUrl(_url, filename));
425 ExternalProgram::Arguments args;
427 fillAriaCmdLine(_aria2cPath, _aria2cVersion, _settings, fileurl, target.dirname(), args);
433 report->start(_url, target.asString() );
435 ExternalProgram aria(args, ExternalProgram::Stderr_To_Stdout);
439 for(std::string ariaResponse( aria.receiveLine());
440 ariaResponse.length();
441 ariaResponse = aria.receiveLine())
443 //cout << ariaResponse;
445 if (!ariaResponse.substr(0,31).compare("Exception: Authorization failed") )
447 ZYPP_THROW(MediaUnauthorizedException(
448 _url, "Login failed.", "Login failed", "auth hint"
451 if (!ariaResponse.substr(0,29).compare("Exception: Resource not found") )
453 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
456 if (!ariaResponse.substr(0,9).compare("[#2 SIZE:"))
460 size_t left_bound = ariaResponse.find('(',0) + 1;
461 size_t count = ariaResponse.find('%',left_bound) - left_bound;
462 //cout << ariaResponse.substr(left_bound, count) << endl;
463 //progressData.toMax();
464 report->progress ( std::atoi(ariaResponse.substr(left_bound, count).c_str()), _url, -1, -1 );
476 report->finish( _url , zypp::media::DownloadProgressReport::NO_ERROR, "");
480 // retry with proper authentication data
481 catch (MediaUnauthorizedException & ex_r)
483 if(authenticate(ex_r.hint(), !retry))
487 report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
492 // unexpected exception
493 catch (MediaException & excpt_r)
495 // FIXME: error number fix
496 report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserHistory());
497 ZYPP_RETHROW(excpt_r);
502 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
505 bool MediaAria2c::getDoesFileExist( const Pathname & filename ) const
507 return MediaCurl::getDoesFileExist(filename);
510 bool MediaAria2c::doGetDoesFileExist( const Pathname & filename ) const
512 return MediaCurl::doGetDoesFileExist(filename);
515 void MediaAria2c::getDir( const Pathname & dirname, bool recurse_r ) const
517 MediaCurl::getDir(dirname, recurse_r);
520 bool MediaAria2c::authenticate(const std::string & availAuthTypes, bool firstTry) const
526 void MediaAria2c::getDirInfo( std::list<std::string> & retlist,
527 const Pathname & dirname, bool dots ) const
529 getDirectoryYast( retlist, dirname, dots );
532 void MediaAria2c::getDirInfo( filesystem::DirContent & retlist,
533 const Pathname & dirname, bool dots ) const
535 getDirectoryYast( retlist, dirname, dots );
538 std::string MediaAria2c::getAria2cVersion()
547 ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
549 std::string vResponse = aria.receiveLine();
551 return str::trim(vResponse);
554 #define ARIA_DEFAULT_BINARY "/usr/bin/aria2c"
556 Pathname MediaAria2c::whereisAria2c()
558 Pathname aria2cPathr(ARIA_DEFAULT_BINARY);
567 ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
569 std::string ariaResponse( aria.receiveLine());
570 int code = aria.close();
574 aria2cPathr = str::trim(ariaResponse);
575 MIL << "We will use aria2c located here: " << aria2cPathr << endl;
579 MIL << "We don't know were is ari2ac binary. We will use aria2c located here: " << aria2cPathr << endl;