00001
00002
00003
00004
00005
00006
00007
00008
00013 #include <iostream>
00014 #include <fstream>
00015 #include <list>
00016 #include <algorithm>
00017 #include "zypp/base/InputStream.h"
00018 #include "zypp/base/Logger.h"
00019 #include "zypp/base/Gettext.h"
00020 #include "zypp/base/Function.h"
00021 #include "zypp/PathInfo.h"
00022 #include "zypp/TmpPath.h"
00023
00024 #include "zypp/repo/RepoException.h"
00025 #include "zypp/RepoManager.h"
00026
00027 #include "zypp/cache/CacheStore.h"
00028 #include "zypp/repo/cached/RepoImpl.h"
00029 #include "zypp/media/MediaManager.h"
00030 #include "zypp/MediaSetAccess.h"
00031
00032 #include "zypp/parser/RepoFileReader.h"
00033 #include "zypp/repo/yum/Downloader.h"
00034 #include "zypp/parser/yum/RepoParser.h"
00035 #include "zypp/parser/plaindir/RepoParser.h"
00036 #include "zypp/repo/susetags/Downloader.h"
00037 #include "zypp/parser/susetags/RepoParser.h"
00038
00039 #include "zypp/ZYppCallbacks.h"
00040
00041 using namespace std;
00042 using namespace zypp;
00043 using namespace zypp::repo;
00044 using namespace zypp::filesystem;
00045
00046 using namespace zypp::repo;
00047
00049 namespace zypp
00050 {
00051
00053
00054
00055
00057
00058 RepoManagerOptions::RepoManagerOptions()
00059 {
00060 repoCachePath = ZConfig::instance().repoCachePath();
00061 repoRawCachePath = ZConfig::instance().repoMetadataPath();
00062 knownReposPath = ZConfig::instance().knownReposPath();
00063 }
00064
00066
00077 struct RepoCollector
00078 {
00079 RepoCollector()
00080 {
00081 MIL << endl;
00082 }
00083
00084 ~RepoCollector()
00085 {
00086 MIL << endl;
00087 }
00088
00089 bool collect( const RepoInfo &repo )
00090 {
00091
00092 repos.push_back(repo);
00093
00094 return true;
00095 }
00096
00097 RepoInfoList repos;
00098 };
00099
00101
00108 static void cleanCacheInternal( cache::CacheStore &store,
00109 const RepoInfo &info,
00110 const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() )
00111 {
00112 ProgressData progress;
00113 callback::SendReport<ProgressReport> report;
00114 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
00115 progress.name(str::form(_("Cleaning repository '%s' cache"), info.name().c_str()));
00116
00117 if ( !store.isCached(info.alias()) )
00118 return;
00119
00120 MIL << info.alias() << " cleaning cache..." << endl;
00121 data::RecordId id = store.lookupRepository(info.alias());
00122
00123 CombinedProgressData subprogrcv(progress);
00124
00125 store.cleanRepository(id, subprogrcv);
00126 }
00127
00129
00135 static std::list<RepoInfo> repositories_in_file( const Pathname & file )
00136 {
00137 MIL << "repo file: " << file << endl;
00138 RepoCollector collector;
00139 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
00140 return collector.repos;
00141 }
00142
00144
00145 std::list<RepoInfo> readRepoFile(const Url & repo_file)
00146 {
00147
00149 Url url(repo_file);
00150 Pathname path(url.getPathName());
00151 url.setPathName ("/");
00152 MediaSetAccess access(url);
00153 Pathname local = access.provideFile(path);
00154
00155 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
00156
00157 return repositories_in_file(local);
00158 }
00159
00161
00170 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
00171 {
00172 MIL << "directory " << dir << endl;
00173 list<RepoInfo> repos;
00174 list<Pathname> entries;
00175 if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
00176 ZYPP_THROW(Exception("failed to read directory"));
00177
00178 for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
00179 {
00180 list<RepoInfo> tmp = repositories_in_file( *it );
00181 repos.insert( repos.end(), tmp.begin(), tmp.end() );
00182
00183
00184
00185 }
00186 return repos;
00187 }
00188
00190
00191 static void assert_alias( const RepoInfo &info )
00192 {
00193 if (info.alias().empty())
00194 ZYPP_THROW(RepoNoAliasException());
00195 }
00196
00198
00199 static void assert_urls( const RepoInfo &info )
00200 {
00201 if (info.baseUrlsEmpty())
00202 ZYPP_THROW(RepoNoUrlException());
00203 }
00204
00206
00210 static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
00211 {
00212 assert_alias(info);
00213 return opt.repoRawCachePath + info.alias();
00214 }
00215
00217
00218
00219
00221
00225 struct RepoManager::Impl
00226 {
00227 Impl( const RepoManagerOptions &opt )
00228 : options(opt)
00229 {
00230
00231 }
00232
00233 Impl()
00234 {
00235
00236 }
00237
00238 RepoManagerOptions options;
00239
00240 public:
00242 static shared_ptr<Impl> nullimpl()
00243 {
00244 static shared_ptr<Impl> _nullimpl( new Impl );
00245 return _nullimpl;
00246 }
00247
00248 private:
00249 friend Impl * rwcowClone<Impl>( const Impl * rhs );
00251 Impl * clone() const
00252 { return new Impl( *this ); }
00253 };
00255
00257 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
00258 {
00259 return str << "RepoManager::Impl";
00260 }
00261
00263
00264
00265
00267
00268 RepoManager::RepoManager( const RepoManagerOptions &opt )
00269 : _pimpl( new Impl(opt) )
00270 {}
00271
00273
00274 RepoManager::~RepoManager()
00275 {}
00276
00278
00279 std::list<RepoInfo> RepoManager::knownRepositories() const
00280 {
00281 MIL << endl;
00282
00283 if ( PathInfo(_pimpl->options.knownReposPath).isExist() )
00284 {
00285 RepoInfoList repos = repositories_in_dir(_pimpl->options.knownReposPath);
00286 for ( RepoInfoList::iterator it = repos.begin();
00287 it != repos.end();
00288 ++it )
00289 {
00290
00291 Pathname metadata_path = rawcache_path_for_repoinfo(_pimpl->options, (*it));
00292 (*it).setMetadataPath(metadata_path);
00293 }
00294 return repos;
00295 }
00296 else
00297 return std::list<RepoInfo>();
00298
00299 MIL << endl;
00300 }
00301
00303
00304 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
00305 {
00306 return rawcache_path_for_repoinfo(_pimpl->options, info );
00307 }
00308
00310
00311 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
00312 {
00313 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00314 RepoType repokind = info.type();
00315 RepoStatus status;
00316
00317 switch ( repokind.toEnum() )
00318 {
00319 case RepoType::NONE_e:
00320
00321 repokind = probe(rawpath.asUrl());
00322 break;
00323 default:
00324 break;
00325 }
00326
00327 switch ( repokind.toEnum() )
00328 {
00329 case RepoType::RPMMD_e :
00330 {
00331 status = RepoStatus( rawpath + "/repodata/repomd.xml");
00332 }
00333 break;
00334
00335 case RepoType::YAST2_e :
00336 {
00337
00338 status = RepoStatus( rawpath + "/content") && (RepoStatus( rawpath + "/media.1/media"));
00339 }
00340 break;
00341
00342 case RepoType::RPMPLAINDIR_e :
00343 {
00344 if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
00345 status = RepoStatus( rawpath + "/cookie");
00346 }
00347 break;
00348
00349 case RepoType::NONE_e :
00350
00351
00352
00353 break;
00354 }
00355 return status;
00356 }
00357
00358 void RepoManager::touchIndexFile(const RepoInfo & info)
00359 {
00360 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00361
00362 RepoType repokind = info.type();
00363 if ( repokind.toEnum() == RepoType::NONE_e )
00364
00365 repokind = probe(rawpath.asUrl());
00366
00367 if (repokind == RepoType::NONE_e)
00368 return;
00369
00370 Pathname p;
00371 switch ( repokind.toEnum() )
00372 {
00373 case RepoType::RPMMD_e :
00374 p = Pathname(rawpath + "/repodata/repomd.xml");
00375 break;
00376
00377 case RepoType::YAST2_e :
00378 p = Pathname(rawpath + "/content");
00379 break;
00380
00381 case RepoType::RPMPLAINDIR_e :
00382 p = Pathname(rawpath + "/cookie");
00383 break;
00384
00385 case RepoType::NONE_e :
00386 default:
00387 break;
00388 }
00389
00390
00391 filesystem::touch(p);
00392 }
00393
00394 bool RepoManager::checkIfToRefreshMetadata( const RepoInfo &info,
00395 const Url &url,
00396 RawMetadataRefreshPolicy policy )
00397 {
00398 assert_alias(info);
00399
00400 RepoStatus oldstatus;
00401 RepoStatus newstatus;
00402
00403 try
00404 {
00405 MIL << "Going to try to check whether refresh is needed for " << url << endl;
00406
00407 repo::RepoType repokind = info.type();
00408
00409
00410 switch ( repokind.toEnum() )
00411 {
00412 case RepoType::NONE_e:
00413
00414 repokind = probe(url);
00415 break;
00416 default:
00417 break;
00418 }
00419
00420 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00421 filesystem::assert_dir(rawpath);
00422 oldstatus = metadataStatus(info);
00423
00424
00425 if (policy != RefreshForced)
00426 {
00427
00428 double diff = difftime(
00429 (Date::ValueType)Date::now(),
00430 (Date::ValueType)oldstatus.timestamp()) / 60;
00431
00432 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
00433 DBG << "current time: " << (Date::ValueType)Date::now() << endl;
00434 DBG << "last refresh = " << diff << " minutes ago" << endl;
00435
00436 if (diff < ZConfig::instance().repo_refresh_delay())
00437 {
00438 MIL << "Repository '" << info.alias()
00439 << "' has been refreshed less than repo.refresh.delay ("
00440 << ZConfig::instance().repo_refresh_delay()
00441 << ") minutes ago. Advising to skip refresh" << endl;
00442 return false;
00443 }
00444 }
00445
00446
00447 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
00448
00449 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
00450 ( repokind.toEnum() == RepoType::YAST2_e ) )
00451 {
00452 MediaSetAccess media(url);
00453 shared_ptr<repo::Downloader> downloader_ptr;
00454
00455 if ( repokind.toEnum() == RepoType::RPMMD_e )
00456 downloader_ptr.reset(new yum::Downloader(info.path()));
00457 else
00458 downloader_ptr.reset( new susetags::Downloader(info.path()));
00459
00460 RepoStatus newstatus = downloader_ptr->status(media);
00461 bool refresh = false;
00462 if ( oldstatus.checksum() == newstatus.checksum() )
00463 {
00464 MIL << "repo has not changed" << endl;
00465 if ( policy == RefreshForced )
00466 {
00467 MIL << "refresh set to forced" << endl;
00468 refresh = true;
00469 }
00470 }
00471 else
00472 {
00473 MIL << "repo has changed, going to refresh" << endl;
00474 refresh = true;
00475 }
00476
00477 if (!refresh)
00478 touchIndexFile(info);
00479
00480 return refresh;
00481 }
00482 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
00483 {
00484 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
00485 bool refresh = false;
00486 if ( oldstatus.checksum() == newstatus.checksum() )
00487 {
00488 MIL << "repo has not changed" << endl;
00489 if ( policy == RefreshForced )
00490 {
00491 MIL << "refresh set to forced" << endl;
00492 refresh = true;
00493 }
00494 }
00495 else
00496 {
00497 MIL << "repo has changed, going to refresh" << endl;
00498 refresh = true;
00499 }
00500
00501 if (!refresh)
00502 touchIndexFile(info);
00503
00504 return refresh;
00505 }
00506 else
00507 {
00508 ZYPP_THROW(RepoUnknownTypeException());
00509 }
00510 }
00511 catch ( const Exception &e )
00512 {
00513 ZYPP_CAUGHT(e);
00514 ERR << "refresh check failed for " << url << endl;
00515 ZYPP_RETHROW(e);
00516 }
00517
00518 return true;
00519 }
00520
00521 void RepoManager::refreshMetadata( const RepoInfo &info,
00522 RawMetadataRefreshPolicy policy,
00523 const ProgressData::ReceiverFnc & progress )
00524 {
00525 assert_alias(info);
00526 assert_urls(info);
00527
00528
00529 RepoException rexception(_("Valid metadata not found at specified URL(s)"));
00530
00531
00532 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
00533 {
00534 try
00535 {
00536 Url url(*it);
00537
00538
00539
00540 if (!checkIfToRefreshMetadata(info, url, policy))
00541 return;
00542
00543 MIL << "Going to refresh metadata from " << url << endl;
00544
00545 repo::RepoType repokind = info.type();
00546
00547
00548 switch ( repokind.toEnum() )
00549 {
00550 case RepoType::NONE_e:
00551
00552 repokind = probe(*it);
00553 break;
00554 default:
00555 break;
00556 }
00557
00558 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00559 filesystem::assert_dir(rawpath);
00560
00561
00562 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
00563
00564 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
00565 ( repokind.toEnum() == RepoType::YAST2_e ) )
00566 {
00567 MediaSetAccess media(url);
00568 shared_ptr<repo::Downloader> downloader_ptr;
00569
00570 if ( repokind.toEnum() == RepoType::RPMMD_e )
00571 downloader_ptr.reset(new yum::Downloader(info.path()));
00572 else
00573 downloader_ptr.reset( new susetags::Downloader(info.path()));
00574
00581 std::list<RepoInfo> repos = knownRepositories();
00582 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
00583 it != repos.end();
00584 ++it )
00585 {
00586 downloader_ptr->addCachePath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
00587 }
00588
00589 downloader_ptr->download( media, tmpdir.path());
00590 }
00591 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
00592 {
00593 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
00594
00595 std::ofstream file(( tmpdir.path() + "/cookie").c_str());
00596 if (!file) {
00597 ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
00598 }
00599 file << url << endl;
00600 file << newstatus.checksum() << endl;
00601
00602 file.close();
00603 }
00604 else
00605 {
00606 ZYPP_THROW(RepoUnknownTypeException());
00607 }
00608
00609
00610
00611 TmpDir oldmetadata( TmpDir::makeSibling( rawpath ) );
00612 filesystem::rename( rawpath, oldmetadata.path() );
00613
00614 filesystem::rename( tmpdir.path(), rawpath );
00615
00616 return;
00617 }
00618 catch ( const Exception &e )
00619 {
00620 ZYPP_CAUGHT(e);
00621 ERR << "Trying another url..." << endl;
00622
00623
00624
00625
00626 if (it == info.baseUrlsBegin())
00627 rexception.remember(e);
00628 }
00629 }
00630 ERR << "No more urls..." << endl;
00631 ZYPP_THROW(rexception);
00632 }
00633
00635
00636 void RepoManager::cleanMetadata( const RepoInfo &info,
00637 const ProgressData::ReceiverFnc & progressfnc )
00638 {
00639 ProgressData progress(100);
00640 progress.sendTo(progressfnc);
00641
00642 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
00643 progress.toMax();
00644 }
00645
00646 void RepoManager::buildCache( const RepoInfo &info,
00647 CacheBuildPolicy policy,
00648 const ProgressData::ReceiverFnc & progressrcv )
00649 {
00650 assert_alias(info);
00651 Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
00652
00653 cache::CacheStore store(_pimpl->options.repoCachePath);
00654
00655 RepoStatus raw_metadata_status = metadataStatus(info);
00656 if ( raw_metadata_status.empty() )
00657 {
00658 ZYPP_THROW(RepoMetadataException(info));
00659 }
00660
00661 bool needs_cleaning = false;
00662 if ( store.isCached( info.alias() ) )
00663 {
00664 MIL << info.alias() << " is already cached." << endl;
00665 data::RecordId id = store.lookupRepository(info.alias());
00666 RepoStatus cache_status = store.repositoryStatus(id);
00667
00668 if ( cache_status.checksum() == raw_metadata_status.checksum() )
00669 {
00670 MIL << info.alias() << " cache is up to date with metadata." << endl;
00671 if ( policy == BuildIfNeeded ) {
00672 return;
00673 }
00674 else {
00675 MIL << info.alias() << " cache rebuild is forced" << endl;
00676 }
00677 }
00678
00679 needs_cleaning = true;
00680 }
00681
00682 ProgressData progress(100);
00683 callback::SendReport<ProgressReport> report;
00684 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
00685 progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
00686 progress.toMin();
00687
00688 if (needs_cleaning)
00689 cleanCacheInternal( store, info);
00690
00691 MIL << info.alias() << " building cache..." << endl;
00692 data::RecordId id = store.lookupOrAppendRepository(info.alias());
00693
00694 repo::RepoType repokind = info.type();
00695
00696
00697 switch ( repokind.toEnum() )
00698 {
00699 case RepoType::NONE_e:
00700
00701 repokind = probe(rawpath.asUrl());
00702 break;
00703 default:
00704 break;
00705 }
00706
00707
00708 switch ( repokind.toEnum() )
00709 {
00710 case RepoType::RPMMD_e :
00711 {
00712 CombinedProgressData subprogrcv( progress, 100);
00713 parser::yum::RepoParser parser(id, store, parser::yum::RepoParserOpts(), subprogrcv);
00714 parser.parse(rawpath);
00715
00716 }
00717 break;
00718 case RepoType::YAST2_e :
00719 {
00720 CombinedProgressData subprogrcv( progress, 100);
00721 parser::susetags::RepoParser parser(id, store, subprogrcv);
00722 parser.parse(rawpath);
00723
00724 }
00725 break;
00726 case RepoType::RPMPLAINDIR_e :
00727 {
00728 CombinedProgressData subprogrcv( progress, 100);
00729 InputStream is(rawpath + "cookie");
00730 string buffer;
00731 getline( is.stream(), buffer);
00732 Url url(buffer);
00733 parser::plaindir::RepoParser parser(id, store, subprogrcv);
00734 parser.parse(url.getPathName());
00735 }
00736 break;
00737 default:
00738 ZYPP_THROW(RepoUnknownTypeException());
00739 }
00740
00741
00742 store.updateRepositoryStatus(id, raw_metadata_status);
00743
00744 MIL << "Commit cache.." << endl;
00745 store.commit();
00746
00747 }
00748
00750
00751 repo::RepoType RepoManager::probe( const Url &url ) const
00752 {
00753 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
00754 {
00755
00756
00757 return repo::RepoType::NONE;
00758 }
00759
00760 try
00761 {
00762 MediaSetAccess access(url);
00763 if ( access.doesFileExist("/repodata/repomd.xml") )
00764 return repo::RepoType::RPMMD;
00765 if ( access.doesFileExist("/content") )
00766 return repo::RepoType::YAST2;
00767
00768
00769 if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
00770 {
00771 Pathname path = Pathname(url.getPathName());
00772 if ( PathInfo(path).isDir() )
00773 {
00774
00775 return repo::RepoType::RPMPLAINDIR;
00776 }
00777 }
00778 }
00779 catch ( const media::MediaException &e )
00780 {
00781 ZYPP_CAUGHT(e);
00782 RepoException enew("Error trying to read from " + url.asString());
00783 enew.remember(e);
00784 ZYPP_THROW(enew);
00785 }
00786 catch ( const Exception &e )
00787 {
00788 ZYPP_CAUGHT(e);
00789 Exception enew("Unknown error reading from " + url.asString());
00790 enew.remember(e);
00791 ZYPP_THROW(enew);
00792 }
00793
00794 return repo::RepoType::NONE;
00795 }
00796
00798
00799 void RepoManager::cleanCache( const RepoInfo &info,
00800 const ProgressData::ReceiverFnc & progressrcv )
00801 {
00802 cache::CacheStore store(_pimpl->options.repoCachePath);
00803 cleanCacheInternal( store, info, progressrcv );
00804 store.commit();
00805 }
00806
00808
00809 bool RepoManager::isCached( const RepoInfo &info ) const
00810 {
00811 cache::CacheStore store(_pimpl->options.repoCachePath);
00812 return store.isCached(info.alias());
00813 }
00814
00815 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
00816 {
00817 cache::CacheStore store(_pimpl->options.repoCachePath);
00818 data::RecordId id = store.lookupRepository(info.alias());
00819 RepoStatus cache_status = store.repositoryStatus(id);
00820 return cache_status;
00821 }
00822
00823 Repository RepoManager::createFromCache( const RepoInfo &info,
00824 const ProgressData::ReceiverFnc & progressrcv )
00825 {
00826 callback::SendReport<ProgressReport> report;
00827 ProgressData progress;
00828 progress.sendTo(ProgressReportAdaptor( progressrcv, report ));
00829
00830 progress.name(str::form(_("Reading repository '%s' cache"), info.name().c_str()));
00831
00832 cache::CacheStore store(_pimpl->options.repoCachePath);
00833
00834 if ( ! store.isCached( info.alias() ) )
00835 ZYPP_THROW(RepoNotCachedException());
00836
00837 MIL << "Repository " << info.alias() << " is cached" << endl;
00838
00839 data::RecordId id = store.lookupRepository(info.alias());
00840
00841 CombinedProgressData subprogrcv(progress);
00842
00843 repo::cached::RepoOptions opts( info, _pimpl->options.repoCachePath, id );
00844 opts.readingResolvablesProgress = subprogrcv;
00845 repo::cached::RepoImpl::Ptr repoimpl =
00846 new repo::cached::RepoImpl( opts );
00847
00848 repoimpl->resolvables();
00849
00850 return Repository(repoimpl);
00851 }
00852
00854
00870 static Pathname generate_non_existing_name( const Pathname &dir,
00871 const std::string &basefilename )
00872 {
00873 string final_filename = basefilename;
00874 int counter = 1;
00875 while ( PathInfo(dir + final_filename).isExist() )
00876 {
00877 final_filename = basefilename + "_" + str::numstring(counter);
00878 counter++;
00879 }
00880 return dir + Pathname(final_filename);
00881 }
00882
00884
00892 static std::string generate_filename( const RepoInfo &info )
00893 {
00894 std::string fnd="/";
00895 std::string rep="_";
00896 std::string filename = info.alias();
00897
00898 size_t pos = filename.find(fnd);
00899 while(pos!=string::npos)
00900 {
00901 filename.replace(pos,fnd.length(),rep);
00902 pos = filename.find(fnd,pos+rep.length());
00903 }
00904 filename = Pathname(filename).extend(".repo").asString();
00905 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
00906 return filename;
00907 }
00908
00909
00911
00912 void RepoManager::addRepository( const RepoInfo &info,
00913 const ProgressData::ReceiverFnc & progressrcv )
00914 {
00915 assert_alias(info);
00916
00917 ProgressData progress(100);
00918 callback::SendReport<ProgressReport> report;
00919 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
00920 progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
00921 progress.toMin();
00922
00923 std::list<RepoInfo> repos = knownRepositories();
00924 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
00925 it != repos.end();
00926 ++it )
00927 {
00928 if ( info.alias() == (*it).alias() )
00929 ZYPP_THROW(RepoAlreadyExistsException(info.alias()));
00930 }
00931
00932 RepoInfo tosave = info;
00933
00934
00935 if ( ZConfig::instance().repo_add_probe()
00936 || ( tosave.type() == RepoType::NONE && tosave.enabled()) )
00937 {
00938 DBG << "unknown repository type, probing" << endl;
00939
00940 RepoType probedtype;
00941 probedtype = probe(*tosave.baseUrlsBegin());
00942 if ( tosave.baseUrlsSize() > 0 )
00943 {
00944 if ( probedtype == RepoType::NONE )
00945 ZYPP_THROW(RepoUnknownTypeException());
00946 else
00947 tosave.setType(probedtype);
00948 }
00949 }
00950
00951 progress.set(50);
00952
00953
00954 filesystem::assert_dir(_pimpl->options.knownReposPath);
00955
00956 Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath,
00957 generate_filename(tosave));
00958
00959 MIL << "Saving repo in " << repofile << endl;
00960
00961 std::ofstream file(repofile.c_str());
00962 if (!file) {
00963 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
00964 }
00965
00966 tosave.dumpRepoOn(file);
00967 progress.toMax();
00968 MIL << "done" << endl;
00969 }
00970
00971 void RepoManager::addRepositories( const Url &url,
00972 const ProgressData::ReceiverFnc & progressrcv )
00973 {
00974 std::list<RepoInfo> knownrepos = knownRepositories();
00975 std::list<RepoInfo> repos = readRepoFile(url);
00976 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
00977 it != repos.end();
00978 ++it )
00979 {
00980
00981 for ( std::list<RepoInfo>::const_iterator kit = knownrepos.begin();
00982 kit != knownrepos.end();
00983 ++kit )
00984 {
00985 if ( (*it).alias() == (*kit).alias() )
00986 {
00987 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
00988 ZYPP_THROW(RepoAlreadyExistsException((*it).alias()));
00989 }
00990 }
00991 }
00992
00993 string filename = Pathname(url.getPathName()).basename();
00994
00995 if ( filename == Pathname() )
00996 ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
00997
00998
00999 filesystem::assert_dir(_pimpl->options.knownReposPath);
01000
01001 Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath, filename);
01002
01003 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
01004
01005 std::ofstream file(repofile.c_str());
01006 if (!file) {
01007 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
01008 }
01009
01010 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
01011 it != repos.end();
01012 ++it )
01013 {
01014 MIL << "Saving " << (*it).alias() << endl;
01015 (*it).dumpRepoOn(file);
01016 }
01017 MIL << "done" << endl;
01018 }
01019
01021
01022 void RepoManager::removeRepository( const RepoInfo & info,
01023 const ProgressData::ReceiverFnc & progressrcv)
01024 {
01025 ProgressData progress;
01026 callback::SendReport<ProgressReport> report;
01027 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
01028 progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
01029
01030 MIL << "Going to delete repo " << info.alias() << endl;
01031
01032 std::list<RepoInfo> repos = knownRepositories();
01033 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
01034 it != repos.end();
01035 ++it )
01036 {
01037
01038
01039
01040 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
01041 continue;
01042
01043
01044
01045
01046
01047 RepoInfo todelete = *it;
01048 if (todelete.filepath().empty())
01049 {
01050 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
01051 }
01052 else
01053 {
01054
01055 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
01056 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
01057 {
01058
01059 if ( filesystem::unlink(todelete.filepath()) != 0 )
01060 {
01061 ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
01062 }
01063 MIL << todelete.alias() << " sucessfully deleted." << endl;
01064 }
01065 else
01066 {
01067
01068
01069
01070
01071
01072
01073 filesystem::assert_dir(todelete.filepath().dirname());
01074
01075 std::ofstream file(todelete.filepath().c_str());
01076 if (!file) {
01077
01078 ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
01079 }
01080 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
01081 fit != filerepos.end();
01082 ++fit )
01083 {
01084 if ( (*fit).alias() != todelete.alias() )
01085 (*fit).dumpRepoOn(file);
01086 }
01087 }
01088
01089 CombinedProgressData subprogrcv(progress, 70);
01090 CombinedProgressData cleansubprogrcv(progress, 30);
01091
01092 cleanCache( todelete, subprogrcv);
01093
01094 cleanMetadata( todelete, cleansubprogrcv);
01095 MIL << todelete.alias() << " sucessfully deleted." << endl;
01096 return;
01097 }
01098
01099 }
01100
01101 ZYPP_THROW(RepoNotFoundException(info));
01102 }
01103
01105
01106 void RepoManager::modifyRepository( const std::string &alias,
01107 const RepoInfo & newinfo,
01108 const ProgressData::ReceiverFnc & progressrcv )
01109 {
01110 RepoInfo toedit = getRepositoryInfo(alias);
01111
01112 if (toedit.filepath().empty())
01113 {
01114 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
01115 }
01116 else
01117 {
01118
01119 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
01120
01121
01122
01123
01124
01125
01126
01127 filesystem::assert_dir(toedit.filepath().dirname());
01128
01129 std::ofstream file(toedit.filepath().c_str());
01130 if (!file) {
01131
01132 ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
01133 }
01134 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
01135 fit != filerepos.end();
01136 ++fit )
01137 {
01138
01139
01140 if ( (*fit).alias() != toedit.alias() )
01141 (*fit).dumpRepoOn(file);
01142 else
01143 newinfo.dumpRepoOn(file);
01144 }
01145 }
01146 }
01147
01149
01150 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
01151 const ProgressData::ReceiverFnc & progressrcv )
01152 {
01153 std::list<RepoInfo> repos = knownRepositories();
01154 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
01155 it != repos.end();
01156 ++it )
01157 {
01158 if ( (*it).alias() == alias )
01159 return *it;
01160 }
01161 RepoInfo info;
01162 info.setAlias(info.alias());
01163 ZYPP_THROW(RepoNotFoundException(info));
01164 }
01165
01167
01168 RepoInfo RepoManager::getRepositoryInfo( const Url & url,
01169 const url::ViewOption & urlview,
01170 const ProgressData::ReceiverFnc & progressrcv )
01171 {
01172 std::list<RepoInfo> repos = knownRepositories();
01173 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
01174 it != repos.end();
01175 ++it )
01176 {
01177 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
01178 urlit != (*it).baseUrlsEnd();
01179 ++urlit)
01180 {
01181 if ((*urlit).asString(urlview) == url.asString(urlview))
01182 return *it;
01183 }
01184 }
01185 RepoInfo info;
01186 info.setAlias(info.alias());
01187 info.setBaseUrl(url);
01188 ZYPP_THROW(RepoNotFoundException(info));
01189 }
01190
01192
01193 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
01194 {
01195 return str << *obj._pimpl;
01196 }
01197
01199 }