20#include <zypp-curl/parser/MetaLinkParser>
59 struct MirrorListRefused :
public zypp::Exception
61 MirrorListRefused(
const std::string & msg_r ) : zypp::Exception( msg_r ) {}
71 struct RepoMirrorListTempProvider
73 RepoMirrorListTempProvider()
76 RepoMirrorListTempProvider( Pathname localfile_r )
77 : _localfile(std::move( localfile_r ))
80 RepoMirrorListTempProvider(
const Url & url_r )
82 if ( url_r.schemeIsDownloading()
84 && url_r.getQueryStringMap().count(
"mirrorlist") > 0 ) {
87 const auto &authCb = [&](
const zypp::Url &, media::TransferSettings &settings,
const std::string & availAuthTypes,
bool firstTry,
bool &canContinue ) {
88 media::CredentialManager cm(media::CredManagerOptions(
ZConfig::instance().repoManagerRoot()));
96 internal::MediaNetworkRequestExecutor executor;
97 executor.sigAuthRequired ().connect(authCb);
99 _tmpfile = filesystem::TmpFile();
100 _localfile = _tmpfile->path();
104 auto tSettings = media::TransferSettings();
107 auto req = std::make_shared<zyppng::NetworkRequest>( url_r, _localfile );
108 req->transferSettings () = tSettings;
110 executor.executeRequest ( req,
nullptr );
112 catch (
const zypp::Exception & ) {
113 switch ( req->error().type() ) {
119 ZYPP_THROW( MirrorListRefused( req->error().toString() ) );
130 ERR <<
"Failed to chmod file " << _localfile << endl;
137 Url abs_url( url_r );
138 abs_url.setPathName(
"/" );
139 _access.reset(
new MediaSetAccess( zypp::MirroredOrigin{abs_url} ) );
140 _localfile = _access->provideFile( url_r.getPathName() );
144 const Pathname & localfile()
const
145 {
return _localfile; }
147 shared_ptr<MediaSetAccess> _access;
149 std::optional<filesystem::TmpFile> _tmpfile;
152 enum class RepoMirrorListFormat {
160 static RepoMirrorListFormat detectRepoMirrorListFormat(
const Pathname &localfile ) {
164 MIL <<
"Detecting RepoMirrorlist Format based on file content" << std::endl;
166 if ( localfile.empty () )
167 return RepoMirrorListFormat::Empty;
169 InputStream tmpfstream (localfile);
170 auto &str = tmpfstream.stream();
174 while ( !str.eof () && !str.bad() && ( c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r') )
178 ERR <<
"Failed to read RepoMirrorList file, stream hit EOF early." << std::endl;
179 return RepoMirrorListFormat::Empty;
183 ERR <<
"Failed to read RepoMirrorList file, stream became bad." << std::endl;
184 return RepoMirrorListFormat::Error;
189 MIL <<
"Detected Metalink, file starts with <" << std::endl;
190 return RepoMirrorListFormat::MetaLink;
193 MIL <<
"Detected JSON, file starts with [" << std::endl;
194 return RepoMirrorListFormat::MirrorListJson;
197 MIL <<
"Detected TXT, file starts with " << c << std::endl;
198 return RepoMirrorListFormat::MirrorListTxt;
203 inline std::vector<Url> RepoMirrorListParseXML(
const Pathname &tmpfile )
206 media::MetaLinkParser metalink;
207 metalink.parse(tmpfile);
208 return metalink.getUrls();
211 zypp::parser::ParseException ex(
"Invalid repo metalink format.");
212 ex.remember ( std::current_exception () );
217 inline std::vector<Url> RepoMirrorListParseJSON(
const Pathname &tmpfile )
219 InputStream tmpfstream (tmpfile);
222 using namespace zyppng::operators;
223 using zyppng::operators::operator|;
225 auto res = json::parseDocumentExpected( tmpfstream )
226 |
and_then([&]( json::Value data ) {
228 std::vector<Url> urls;
230 MIL <<
"Empty mirrorlist received, no mirrors available." << std::endl;
235 MIL <<
"Unexpected JSON format, top level element must be an array." << std::endl;
236 return zyppng::expected<std::vector<Url>>::error(
ZYPP_EXCPT_PTR( zypp::Exception(
"Unexpected JSON format, top level element must be an array.") ));
238 const auto &topArray = data.
asArray ();
239 for (
const auto &val : topArray ) {
241 MIL <<
"Unexpected JSON element, array must contain only objects. Ignoring current element" << std::endl;
245 const auto &obj = val.asObject();
246 for (
const auto &key : obj ) {
247 if ( key.first ==
"url" ) {
248 const auto &elemValue = key.second;
250 MIL <<
"Unexpected JSON element, element \"url\" must contain a string. Ignoring current element" << std::endl;
254 MIL <<
"Trying to parse URL: " << std::string(elemValue.asString()) << std::endl;
255 urls.push_back (
Url( elemValue.asString() ) );
256 }
catch (
const url::UrlException &e ) {
258 MIL <<
"Invalid URL in mirrors file: "<< elemValue.asString() <<
", ignoring" << std::endl;
267 using zypp::operator<<;
268 MIL <<
"Error while parsing mirrorlist: (" << res.error() <<
"), no mirrors available" << std::endl;
276 MIL <<
"Caught exception while parsing json" << std::endl;
278 zypp::parser::ParseException ex(
"Invalid repo mirror list format, valid JSON was expected.");
279 ex.remember ( std::current_exception () );
285 inline std::vector<Url> RepoMirrorListParseTXT(
const Pathname &tmpfile )
287 InputStream tmpfstream (tmpfile);
288 std::vector<Url> my_urls;
290 while (
getline(tmpfstream.stream(), tmpurl))
292 if ( tmpurl[0] ==
'#' )
295 Url mirrUrl( tmpurl );
296 if ( !mirrUrl.schemeIsDownloading( ) ) {
297 MIL <<
"Ignoring non downloading URL " << tmpurl << std::endl;
299 my_urls.push_back(
Url(tmpurl));
306 ERR <<
"Invalid URL in mirrorlist file." << std::endl;
308 zypp::parser::ParseException ex(
"Invalid repo mirror list format, all Urls must be valid in a mirrorlist txt file.");
309 ex.remember ( std::current_exception () );
317 inline std::vector<Url> RepoMirrorListParse(
const Url & url_r,
const Pathname & listfile_r )
319 MIL <<
"Parsing mirrorlist file: " << listfile_r <<
" originally received from " << url_r << endl;
321 std::vector<Url> mirrorurls;
322 switch( detectRepoMirrorListFormat (listfile_r) ) {
323 case RepoMirrorListFormat::Error:
325 ZYPP_THROW( zypp::parser::ParseException( str::Format(
"Unable to detect metalink file format for: %1%") % listfile_r ));
326 case RepoMirrorListFormat::Empty:
329 case RepoMirrorListFormat::MetaLink:
330 mirrorurls = RepoMirrorListParseXML( listfile_r );
332 case RepoMirrorListFormat::MirrorListJson:
333 mirrorurls = RepoMirrorListParseJSON( listfile_r );
335 case RepoMirrorListFormat::MirrorListTxt:
336 mirrorurls = RepoMirrorListParseTXT( listfile_r );
340 std::vector<Url> ret;
341 for (
auto & murl : mirrorurls )
343 if ( murl.getScheme() !=
"rsync" )
345 std::string pName = murl.getPathName();
346 size_t delpos = pName.find(
"repodata/repomd.xml");
347 if( delpos != std::string::npos )
349 murl.setPathName( pName.erase(delpos) );
351 ret.push_back( murl );
362 PathInfo metaPathInfo( metadatapath_r);
363 std::exception_ptr errors;
370 else if ( !metaPathInfo.
isDir() )
373 RepoMirrorListTempProvider provider( url_r );
374 _urls = RepoMirrorListParse( url_r, provider.localfile() );
383 bool needRefresh = ( !cacheinfo.
isFile()
391 if ( !needRefresh ) {
392 MIL <<
"Mirror cachefile cookie valid and cache is not too old, skipping download (" << cachefile <<
")" << std::endl;
394 _urls = RepoMirrorListParse( url_r, cachefile );
401 errors = std::make_exception_ptr(ex);
402 MIL <<
"Invalid mirrorlist cachefile, deleting it and trying to fetch a new one" << std::endl;
407 if( cacheinfo.
isFile() ) {
415 MIL <<
"Getting MirrorList from URL: " << url_r << endl;
417 RepoMirrorListTempProvider provider( url_r );
418 _urls = RepoMirrorListParse( url_r, provider.localfile() );
424 DBG <<
"Copy MirrorList file to " << cachefile << endl;
438 catch (
const MirrorListRefused & e ) {
448 if ( cacheFailure_r && metaPathInfo.
userMayRWX() ) {
449 MIL <<
"No mirrorlist for this repo, remembering that in " << cachefile << endl;
469 static const std::vector<std::string> hosts{
470 "download.opensuse.org",
473 return ( std::find( hosts.begin(), hosts.end(),
str::toLower(
url.getHost() )) != hosts.end() );
478 std::ifstream file( path_r.
c_str() );
480 WAR <<
"No cookie file " << path_r << endl;
497 std::ofstream file(path_r.
c_str());
502 MIL <<
"Saving mirrorlist cookie file " << path_r << std::endl;
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
std::string checksum() const
static CheckSum sha256FromString(const std::string &input_r)
Base class for Exception.
void remember(const Exception &old_r)
Store an other Exception as history.
std::string getScheme() const
Returns the scheme name of the URL.
std::string asCompleteString() const
Returns a complete string representation of the Url object.
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
static ZConfig & instance()
Singleton ctor.
Wrapper class for stat/lstat.
const char * c_str() const
String representation.
const std::string & asString() const
String representation.
const Array & asArray() const
static std::string makeCookie(const zypp::Url &url_r)
Generates the cookie value, currently this is only derived from the Url.
static std::string readCookieFile(const Pathname &path_r)
RepoMirrorList(const Url &url_r, const Pathname &metadatapath_r, bool cacheFailure_r=false)
static void saveToCookieFile(const Pathname &path_r, const zypp::Url &url_r)
static bool urlSupportsMirrorLink(const zypp::Url &url)
static constexpr const char * cookieFileName()
static constexpr const char * cacheFileName()
void prepareSettingsAndUrl(zypp::Url &url_r, zypp::media::TransferSettings &s)
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
int hardlinkCopy(const Pathname &oldpath, const Pathname &newpath)
Create newpath as hardlink or copy of oldpath.
int unlink(const Pathname &path)
Like 'unlink'.
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
std::string getline(std::istream &str)
Read one line from stream.
std::string toLower(const std::string &s)
Return lowercase version of s.
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Easy-to use interface to the ZYPP dependency resolver.
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
ResultType and_then(const expected< T, E > &exp, Function &&f)
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...