-
+// Copyright (C) 2013 James Turner - zakalawe@mac.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
#include <simgear/package/Catalog.hxx>
class Catalog::Downloader : public HTTP::Request
{
public:
- Downloader(Catalog* aOwner, const std::string& aUrl) :
+ Downloader(CatalogRef aOwner, const std::string& aUrl) :
HTTP::Request(aUrl),
m_owner(aOwner)
{
}
protected:
- virtual void responseHeadersComplete()
- {
-
- }
-
virtual void gotBodyData(const char* s, int n)
{
m_buffer += std::string(s, n);
}
- virtual void responseComplete()
+ virtual void onDone()
{
if (responseCode() != 200) {
SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url());
- m_owner->refreshComplete(false);
+ m_owner->refreshComplete(Delegate::FAIL_DOWNLOAD);
return;
}
m_owner->parseProps(props);
} catch (sg_exception& e) {
SG_LOG(SG_GENERAL, SG_ALERT, "catalog parse failure:" << m_owner->url());
- m_owner->refreshComplete(false);
+ m_owner->refreshComplete(Delegate::FAIL_EXTRACT);
+ return;
+ }
+
+ std::string ver(m_owner->root()->catalogVersion());
+ if (!checkVersion(ver, props)) {
+ SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version mismatch:\n\t"
+ << props->getStringValue("version") << " vs required " << ver);
+ m_owner->refreshComplete(Delegate::FAIL_VERSION);
return;
}
time(&m_owner->m_retrievedTime);
m_owner->writeTimestamp();
- m_owner->refreshComplete(true);
+ m_owner->refreshComplete(Delegate::FAIL_SUCCESS);
}
private:
- Catalog* m_owner;
+ bool checkVersion(const std::string& aVersion, SGPropertyNode* aProps)
+ {
+ BOOST_FOREACH(SGPropertyNode* v, aProps->getChildren("version")) {
+ if (v->getStringValue() == aVersion) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ CatalogRef m_owner;
std::string m_buffer;
};
static_catalogs.erase(it);
}
-Catalog* Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
+CatalogRef Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
{
- Catalog* c = new Catalog(aRoot);
+ CatalogRef c = new Catalog(aRoot);
+ c->m_url = aUrl;
Downloader* dl = new Downloader(c, aUrl);
- aRoot->getHTTPClient()->makeRequest(dl);
+ aRoot->makeHTTPRequest(dl);
return c;
}
-Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
+CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
{
SGPath xml = aPath;
xml.append("catalog.xml");
return NULL;
}
- Catalog* c = new Catalog(aRoot);
+ if (props->getStringValue("version") != aRoot->catalogVersion()) {
+ SG_LOG(SG_GENERAL, SG_WARN, "skipping catalog at " << aPath << ", version mismatch:\n\t"
+ << props->getStringValue("version") << " vs required " << aRoot->catalogVersion());
+ return NULL;
+ }
+
+ CatalogRef c = new Catalog(aRoot);
c->m_installRoot = aPath;
c->parseProps(props);
c->parseTimestamp();
return c;
}
+PackageList const&
+Catalog::packages() const
+{
+ return m_packages;
+}
+
PackageList
Catalog::packagesMatching(const SGPropertyNode* aFilter) const
{
PackageList r;
- BOOST_FOREACH(Package* p, m_packages) {
+ BOOST_FOREACH(PackageRef p, m_packages) {
if (p->matches(aFilter)) {
r.push_back(p);
}
Catalog::packagesNeedingUpdate() const
{
PackageList r;
- BOOST_FOREACH(Package* p, m_packages) {
+ BOOST_FOREACH(PackageRef p, m_packages) {
if (!p->isInstalled()) {
continue;
}
return r;
}
+PackageList
+Catalog::installedPackages() const
+{
+ PackageList r;
+ BOOST_FOREACH(PackageRef p, m_packages) {
+ if (p->isInstalled()) {
+ r.push_back(p);
+ }
+ }
+ return r;
+}
+
+InstallRef Catalog::installForPackage(PackageRef pkg) const
+{
+ PackageInstallDict::const_iterator it = m_installed.find(pkg);
+ if (it == m_installed.end()) {
+ // check if it exists on disk, create
+
+ SGPath p(pkg->pathOnDisk());
+ if (p.exists()) {
+ return Install::createFromPath(p, CatalogRef(const_cast<Catalog*>(this)));
+ }
+
+ return NULL;
+ }
+
+ return it->second;
+}
+
void Catalog::refresh()
{
Downloader* dl = new Downloader(this, url());
- m_root->getHTTPClient()->makeRequest(dl);
+ m_root->makeHTTPRequest(dl);
m_root->catalogRefreshBegin(this);
}
{
// copy everything except package children?
m_props = new SGPropertyNode;
-
+
+ m_variantDict.clear(); // will rebuild during parse
+ std::set<PackageRef> orphans;
+ orphans.insert(m_packages.begin(), m_packages.end());
+
int nChildren = aProps->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode* pkgProps = aProps->getChild(i);
if (strcmp(pkgProps->getName(), "package") == 0) {
- Package* p = new Package(pkgProps, this);
- m_packages.push_back(p);
+ PackageRef p = getPackageById(pkgProps->getStringValue("id"));
+ if (p) {
+ // existing package
+ p->updateFromProps(pkgProps);
+ orphans.erase(p); // not an orphan
+ } else {
+ // new package
+ p = new Package(pkgProps, this);
+ m_packages.push_back(p);
+ }
+
+ string_list vars(p->variants());
+ for (string_list::iterator it = vars.begin(); it != vars.end(); ++it) {
+ m_variantDict[*it] = p.ptr();
+ }
} else {
SGPropertyNode* c = m_props->getChild(pkgProps->getName(), pkgProps->getIndex(), true);
copyProperties(pkgProps, c);
}
} // of children iteration
-
+
+ if (!orphans.empty()) {
+ SG_LOG(SG_GENERAL, SG_WARN, "have orphan packages: will become inaccesible");
+ std::set<PackageRef>::iterator it;
+ for (it = orphans.begin(); it != orphans.end(); ++it) {
+ SG_LOG(SG_GENERAL, SG_WARN, "\torphan package:" << (*it)->qualifiedId());
+ PackageList::iterator pit = std::find(m_packages.begin(), m_packages.end(), *it);
+ assert(pit != m_packages.end());
+ m_packages.erase(pit);
+ }
+ }
+
+ if (!m_url.empty()) {
+ if (m_url != m_props->getStringValue("url")) {
+ // this effectively allows packages to migrate to new locations,
+ // although if we're going to rely on that feature we should
+ // maybe formalise it!
+ SG_LOG(SG_GENERAL, SG_WARN, "package downloaded from:" << m_url
+ << " is now at: " << m_props->getStringValue("url"));
+ }
+ }
+
+ m_url = m_props->getStringValue("url");
+
if (m_installRoot.isNull()) {
m_installRoot = m_root->path();
m_installRoot.append(id());
}
}
-Package* Catalog::getPackageById(const std::string& aId) const
+PackageRef Catalog::getPackageById(const std::string& aId) const
{
- BOOST_FOREACH(Package* p, m_packages) {
- if (p->id() == aId) {
- return p;
- }
- }
-
- return NULL; // not found
+ // search the variant dict here, so looking up aircraft variants
+ // works as expected.
+ PackageWeakMap::const_iterator it = m_variantDict.find(aId);
+ if (it == m_variantDict.end())
+ return NULL;
+
+ return it->second;
}
std::string Catalog::id() const
std::string Catalog::url() const
{
- return m_props->getStringValue("url");
+ return m_url;
}
std::string Catalog::description() const
{
return getLocalisedString(m_props, "description");
}
+
+SGPropertyNode* Catalog::properties() const
+{
+ return m_props.ptr();
+}
void Catalog::parseTimestamp()
{
return (diff < 0) ? 0 : diff;
}
+bool Catalog::needsRefresh() const
+{
+ unsigned int maxAge = m_props->getIntValue("max-age-sec", m_root->maxAgeSeconds());
+ return (ageInSeconds() > maxAge);
+}
+
std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
{
if (aRoot->hasChild(m_root->getLocale())) {
return aRoot->getStringValue(aName);
}
-void Catalog::refreshComplete(bool aSuccess)
+void Catalog::refreshComplete(Delegate::FailureCode aReason)
{
- m_root->catalogRefreshComplete(this, aSuccess);
+ m_root->catalogRefreshComplete(this, aReason);
}
+void Catalog::registerInstall(Install* ins)
+{
+ if (!ins || ins->package()->catalog() != this) {
+ return;
+ }
+
+ m_installed[ins->package()] = ins;
+}
+
+void Catalog::unregisterInstall(Install* ins)
+{
+ if (!ins || ins->package()->catalog() != this) {
+ return;
+ }
+
+ m_installed.erase(ins->package());
+}
} // of namespace pkg