diff --git a/simgear/package/Catalog.cxx b/simgear/package/Catalog.cxx
index de74d19d..5092b888 100644
--- a/simgear/package/Catalog.cxx
+++ b/simgear/package/Catalog.cxx
@@ -81,17 +81,27 @@ bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
}
return false;
}
+
+SGPropertyNode_ptr alternateForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
+{
+ for (auto v : props->getChildren("alternate-version")) {
+ for (auto versionSpec : v->getChildren("version")) {
+ if (checkVersionString(aVersion, versionSpec->getStringValue())) {
+ return v;
+ }
+ }
+ }
+
+ return {};
+}
std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
{
- for (SGPropertyNode* v : props->getChildren("alternate-version")) {
- std::string s(v->getStringValue("version"));
- if (checkVersionString(aVersion, s)) {
- return v->getStringValue("url");;
- }
- }
+ auto node = alternateForVersion(aVersion, props);
+ if (node)
+ return node->getStringValue("url");;
- return std::string();
+ return {};
}
//////////////////////////////////////////////////////////////////////////////
@@ -139,19 +149,15 @@ protected:
std::string ver(m_owner->root()->applicationVersion());
if (!checkVersion(ver, props)) {
- SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", but version required " << ver);
-
// check for a version redirect entry
- std::string url = redirectUrlForVersion(ver, props);
- if (!url.empty()) {
- SG_LOG(SG_GENERAL, SG_WARN, "redirecting from " << m_owner->url() <<
- " to \n\t" << url);
-
- // update the URL and kick off a new request
- m_owner->setUrl(url);
- Downloader* dl = new Downloader(m_owner, url);
- m_owner->root()->makeHTTPRequest(dl);
+ auto alt = alternateForVersion(ver, props);
+ if (alt) {
+ SG_LOG(SG_GENERAL, SG_WARN, "have alternate version of package at:"
+ << alt->getStringValue("url"));
+ m_owner->processAlternate(alt);
} else {
+ SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url()
+ << ", but app version " << ver << " is not comaptible");
m_owner->refreshComplete(Delegate::FAIL_VERSION);
}
@@ -228,7 +234,7 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
if (!versionCheckOk) {
- SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need version: "
+ SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: app version: "
<< aRoot->applicationVersion());
// keep the catalog but mark it as needing an update
} else {
@@ -598,6 +604,50 @@ bool Catalog::isEnabled() const
}
}
+void Catalog::processAlternate(SGPropertyNode_ptr alt)
+{
+ std::string altId;
+ const auto idPtr = alt->getStringValue("id");
+ if (idPtr) {
+ altId = std::string(idPtr);
+ }
+
+ std::string altUrl;
+ if (alt->getStringValue("url")) {
+ altUrl = std::string(alt->getStringValue("url"));
+ }
+
+ CatalogRef existing;
+ if (!altId.empty()) {
+ existing = root()->getCatalogById(altId);
+ } else {
+ existing = root()->getCatalogByUrl(altUrl);
+ }
+
+ if (existing && (existing != this)) {
+ // we already have the alternate, so just go quiet here
+ changeStatus(Delegate::FAIL_VERSION);
+ return;
+ }
+
+ // we have an alternate ID, and it's differnt from our ID, so let's
+ // define a new catalog
+ if (!altId.empty()) {
+ SG_LOG(SG_GENERAL, SG_INFO, "Adding new catalog:" << altId << " as version alternate for " << id());
+ // new catalog being added
+ createFromUrl(root(), altUrl);
+
+ // and we can go idle now
+ changeStatus(Delegate::FAIL_VERSION);
+ return;
+ }
+
+ SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl);
+ setUrl(altUrl);
+ Downloader* dl = new Downloader(this, altUrl);
+ root()->makeHTTPRequest(dl);
+}
+
} // of namespace pkg
} // of namespace simgear
diff --git a/simgear/package/Catalog.hxx b/simgear/package/Catalog.hxx
index c0a6edd8..f300690a 100644
--- a/simgear/package/Catalog.hxx
+++ b/simgear/package/Catalog.hxx
@@ -179,6 +179,8 @@ private:
void changeStatus(Delegate::StatusCode newStatus);
+ void processAlternate(SGPropertyNode_ptr alt);
+
Root* m_root;
SGPropertyNode_ptr m_props;
SGPath m_installRoot;
diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx
index d0d1d93a..8b9f3435 100644
--- a/simgear/package/CatalogTest.cxx
+++ b/simgear/package/CatalogTest.cxx
@@ -741,6 +741,130 @@ void testVersionMigrate(HTTP::Client* cl)
}
}
+void testVersionMigrateToId(HTTP::Client* cl)
+{
+ global_catalogVersion = 2; // version which has migration info
+ SGPath rootPath(simgear::Dir::current().path());
+ rootPath.append("cat_migrate_version_id");
+ simgear::Dir pd(rootPath);
+ pd.removeChildren();
+
+ {
+ pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
+ root->setHTTPClient(cl);
+
+ pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
+ waitForUpdateComplete(cl, root);
+ SG_VERIFY(c->isEnabled());
+
+ // install a package
+ pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
+ SG_CHECK_EQUAL(p1->id(), "b737-NG");
+ pkg::InstallRef ins = p1->install();
+ SG_VERIFY(ins->isQueued());
+ waitForUpdateComplete(cl, root);
+ SG_VERIFY(p1->isInstalled());
+ }
+
+ // change version to an alternate one
+ {
+ pkg::RootRef root(new pkg::Root(rootPath, "7.5"));
+ root->setHTTPClient(cl);
+
+ // this should cause the alternate package to be loaded
+ root->refresh(true);
+ waitForUpdateComplete(cl, root);
+
+ pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
+ SG_VERIFY(!cat->isEnabled());
+ SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION);
+ SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1");
+ SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml");
+
+ auto enabledCats = root->catalogs();
+ auto it = std::find(enabledCats.begin(), enabledCats.end(), cat);
+ SG_VERIFY(it == enabledCats.end());
+
+ // ensure existing package is still installed
+ pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
+ SG_VERIFY(p1 != pkg::PackageRef());
+ SG_CHECK_EQUAL(p1->id(), "b737-NG");
+
+ // but not listed
+ auto packs = root->allPackages();
+ auto k = std::find(packs.begin(), packs.end(), p1);
+ SG_VERIFY(k == packs.end());
+
+ // check the new catalog
+ auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
+ SG_VERIFY(altCat->isEnabled());
+ SG_CHECK_EQUAL(altCat->status(), pkg::Delegate::STATUS_REFRESHED);
+ SG_CHECK_EQUAL(altCat->id(), "org.flightgear.test.catalog-alt");
+ SG_CHECK_EQUAL(altCat->url(), "http://localhost:2000/catalogTest1/catalog-alt.xml");
+
+ it = std::find(enabledCats.begin(), enabledCats.end(), altCat);
+ SG_VERIFY(it != enabledCats.end());
+
+ // install a parallel package from the new catalog
+ pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG");
+ SG_CHECK_EQUAL(p2->id(), "b737-NG");
+ pkg::InstallRef ins = p2->install();
+ SG_VERIFY(ins->isQueued());
+ waitForUpdateComplete(cl, root);
+ SG_VERIFY(p2->isInstalled());
+
+ // do a non-scoped lookup, we should get the new one
+ pkg::PackageRef p3 = root->getPackageById("b737-NG");
+ SG_CHECK_EQUAL(p2, p3);
+ }
+
+ // test that re-init-ing doesn't mirgate again
+ {
+ pkg::RootRef root(new pkg::Root(rootPath, "7.5"));
+ root->setHTTPClient(cl);
+
+ root->refresh(true);
+ waitForUpdateComplete(cl, root);
+
+ pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
+ SG_VERIFY(!cat->isEnabled());
+ SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION);
+
+ auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
+ SG_VERIFY(altCat->isEnabled());
+
+ auto packs = root->allPackages();
+ SG_CHECK_EQUAL(packs.size(), 4);
+ }
+
+ // and now switch back to the older version
+ {
+ pkg::RootRef root(new pkg::Root(rootPath, "8.1.0"));
+ root->setHTTPClient(cl);
+ root->refresh(true);
+ waitForUpdateComplete(cl, root);
+
+ pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
+ SG_VERIFY(cat->isEnabled());
+ SG_CHECK_EQUAL(cat->status(), pkg::Delegate::STATUS_REFRESHED);
+
+ auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
+ SG_VERIFY(!altCat->isEnabled());
+
+ // verify the original aircraft is still installed and available
+ pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
+ SG_VERIFY(p1 != pkg::PackageRef());
+ SG_CHECK_EQUAL(p1->id(), "b737-NG");
+ SG_VERIFY(p1->isInstalled());
+
+ // verify the alt package is still installed,
+ pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG");
+ SG_VERIFY(p2 != pkg::PackageRef());
+ SG_CHECK_EQUAL(p2->id(), "b737-NG");
+ SG_VERIFY(p2->isInstalled());
+ }
+}
+
void testOfflineMode(HTTP::Client* cl)
{
global_catalogVersion = 0;
@@ -990,6 +1114,8 @@ int main(int argc, char* argv[])
removeInvalidCatalog(&cl);
+ testVersionMigrateToId(&cl);
+
SG_LOG(SG_GENERAL, SG_INFO, "Successfully passed all tests!");
return EXIT_SUCCESS;
}
diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx
index b207af2c..ac19af07 100644
--- a/simgear/package/Root.cxx
+++ b/simgear/package/Root.cxx
@@ -414,7 +414,7 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) :
if (cat && cat->isEnabled()) {
d->catalogs.insert({cat->id(), cat});
} else if (cat) {
- SG_LOG(SG_GENERAL, SG_WARN, "Package-Root init: catalog is disabled: " << cat->id());
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Package-Root init: catalog is disabled: " << cat->id());
}
} // of child directories iteration
}
@@ -450,6 +450,17 @@ CatalogRef Root::getCatalogById(const std::string& aId) const
return it->second;
}
+CatalogRef Root::getCatalogByUrl(const std::string& aUrl) const
+{
+ auto it = std::find_if(d->catalogs.begin(), d->catalogs.end(),
+ [aUrl](const CatalogDict::value_type& v)
+ { return (v.second->url() == aUrl); });
+ if (it == d->catalogs.end())
+ return {};
+
+ return it->second;
+}
+
PackageRef Root::getPackageById(const std::string& aName) const
{
size_t lastDot = aName.rfind('.');
diff --git a/simgear/package/Root.hxx b/simgear/package/Root.hxx
index 70461915..2c598166 100644
--- a/simgear/package/Root.hxx
+++ b/simgear/package/Root.hxx
@@ -132,6 +132,8 @@ public:
CatalogRef getCatalogById(const std::string& aId) const;
+ CatalogRef getCatalogByUrl(const std::string& aUrl) const;
+
void scheduleToUpdate(InstallRef aInstall);
/**
diff --git a/simgear/package/catalogTest1/catalog-alt.xml b/simgear/package/catalogTest1/catalog-alt.xml
new file mode 100644
index 00000000..54d2edc2
--- /dev/null
+++ b/simgear/package/catalogTest1/catalog-alt.xml
@@ -0,0 +1,141 @@
+
+
+
+ org.flightgear.test.catalog-alt
+ Alternate test catalog
+ http://localhost:2000/catalogTest1/catalog-alt.xml
+ 4
+
+ 7.*
+
+
+
+ alpha
+ Alpha package
+ 9
+ 593
+
+ a469c4b837f0521db48616cfe65ac1ea
+ http://localhost:2000/catalogTest1/alpha.zip
+
+ alpha
+
+
+
+ c172p
+ Cessna 172-P
+ c172p
+ A plane made by Cessna
+ 42
+ 860
+
+ cessna
+ ga
+ piston
+ ifr
+
+
+ 3
+ 4
+ 5
+ 4
+
+
+
+
+ exterior
+ thumb-exterior.png
+ http://foo.bar.com/thumb-exterior.png
+
+
+
+ panel
+ thumb-panel.png
+ http://foo.bar.com/thumb-panel.png
+
+
+
+ thumb-something.png
+ http://foo.bar.com/thumb-something.png
+
+
+
+ c172p-2d-panel
+ C172 with 2d panel only
+
+
+
+ c172p-floats
+ C172 with floats
+
+
+ exterior
+ thumb-exterior-floats.png
+ http://foo.bar.com/thumb-exterior-floats.png
+
+
+
+ panel
+ thumb-panel.png
+ http://foo.bar.com/thumb-panel.png
+
+
+
+
+ c172p-skis
+ C172 with skis
+
+
+ exterior
+ thumb-exterior-skis.png
+ http://foo.bar.com/thumb-exterior-skis.png
+
+
+
+ panel
+ thumb-panel.png
+ http://foo.bar.com/thumb-panel.png
+
+
+
+ ec0e2ffdf98d6a5c05c77445e5447ff5
+ http://localhost:2000/catalogTest1/c172p.zip
+
+
+
+
+ b737-NG
+ Boeing 737 NG
+ b737NG
+ A popular twin-engined narrow body jet
+ 112
+ 860
+
+ boeing
+ jet
+ ifr
+
+
+ 5
+ 5
+ 4
+ 4
+
+
+ a94ca5704f305b90767f40617d194ed6
+ http://localhost:2000/catalogTest1/b737.tar.gz
+
+
+
+
+ dc3
+ DC-3
+ 9
+ 593
+
+ a469c4b837f0521db48616cfe65ac1ea
+ http://localhost:2000/catalogTest1/alpha.zip
+
+ dc3
+
+
diff --git a/simgear/package/catalogTest1/catalog-v2.xml b/simgear/package/catalogTest1/catalog-v2.xml
index 75a19c57..fa416115 100644
--- a/simgear/package/catalogTest1/catalog-v2.xml
+++ b/simgear/package/catalogTest1/catalog-v2.xml
@@ -15,6 +15,12 @@
http://localhost:2000/catalogTest1/catalog-v10.xml
+
+ 7.*
+ org.flightgear.test.catalog-alt
+ http://localhost:2000/catalogTest1/catalog-alt.xml
+
+
alpha
Alpha package