]> git.mxchange.org Git - simgear.git/blobdiff - simgear/package/Catalog.cxx
Fix a leak / potential crash
[simgear.git] / simgear / package / Catalog.cxx
index 1a810af8a7c00b76782b5728e0363e7c1a7c498d..46f85e77b538b2f0dbd7adb2bfa1ea7102c35dd4 100644 (file)
 #include <simgear/package/Package.hxx>
 #include <simgear/package/Root.hxx>
 #include <simgear/package/Install.hxx>
+#include <simgear/misc/strutils.hxx>
 
 namespace simgear {
 
 namespace pkg {
 
+bool checkVersionString(const std::string& aVersion, const std::string& aCandidate)
+{
+    if (aCandidate == aVersion) {
+        return true;
+    }
+
+    // examine each dot-seperated component in turn, supporting a wildcard
+    // in the versions from the catalog.
+    string_list appVersionParts = simgear::strutils::split(aVersion, ".");
+    string_list catVersionParts = simgear::strutils::split(aCandidate, ".");
+
+    size_t partCount = appVersionParts.size();
+    bool previousCandidatePartWasWildcard = false;
+
+    for (unsigned int p=0; p < partCount; ++p) {
+        // candidate string is too short, can match if it ended with wildcard
+        // part. This allows candidate '2016.*' to match '2016.1.2' and so on
+        if (catVersionParts.size() <= p) {
+            return previousCandidatePartWasWildcard;
+        }
+
+        if (catVersionParts[p] == "*") {
+            // always passes
+            previousCandidatePartWasWildcard = true;
+        } else if (appVersionParts[p] != catVersionParts[p]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
 {
     BOOST_FOREACH(SGPropertyNode* v, props->getChildren("version")) {
         std::string s(v->getStringValue());
-        if (s== aVersion) {
+        if (checkVersionString(aVersion, s)) {
             return true;
         }
-
-        // allow 3.5.* to match any of 3.5.0, 3.5.1rc1, 3.5.11 or so on
-        if (strutils::ends_with(s, ".*")) {
-            size_t lastDot = aVersion.rfind('.');
-            std::string ver = aVersion.substr(0, lastDot);
-            if (ver == s.substr(0, s.length() - 2)) {
-                return true;
-            }
-        }
     }
     return false;
 }
@@ -59,11 +83,12 @@ bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
 std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
 {
     BOOST_FOREACH(SGPropertyNode* v, props->getChildren("alternate-version")) {
-        if (v->getStringValue("version") == aVersion) {
-            return v->getStringValue("url");
+        std::string s(v->getStringValue("version"));
+        if (checkVersionString(aVersion, s)) {
+            return  v->getStringValue("url");;
         }
     }
-    
+
     return std::string();
 }
 
@@ -78,7 +103,6 @@ public:
     {
         // refreshing
         m_owner->changeStatus(Delegate::STATUS_IN_PROGRESS);
-        SG_LOG(SG_GENERAL, SG_WARN, "downloading " << aUrl);
     }
 
 protected:
@@ -100,29 +124,20 @@ protected:
             return;
         }
 
-        SGPropertyNode* props = new SGPropertyNode;
+        SGPropertyNode_ptr props = new SGPropertyNode;
 
         try {
             readProperties(m_buffer.data(), m_buffer.size(), props);
             m_owner->parseProps(props);
-        } catch (sg_exception& e) {
+        } catch (sg_exception& ) {
             SG_LOG(SG_GENERAL, SG_ALERT, "catalog parse failure:" << m_owner->url());
             m_owner->refreshComplete(Delegate::FAIL_EXTRACT);
             return;
         }
 
-        if (m_owner->root()->catalogVersion() != props->getIntValue("catalog-version")) {
-            SG_LOG(SG_GENERAL, SG_WARN, "catalog:" << m_owner->url() << " is not version "
-                   << m_owner->root()->catalogVersion());
-            m_owner->refreshComplete(Delegate::FAIL_VERSION);
-            return;
-        }
-
-
         std::string ver(m_owner->root()->applicationVersion());
         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);
+            SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version required " << ver);
 
             // check for a version redirect entry
             std::string url = redirectUrlForVersion(ver, props);
@@ -131,7 +146,7 @@ protected:
                        " to \n\t" << url);
 
                 // update the URL and kick off a new request
-                m_owner->m_url = url;
+                m_owner->setUrl(url);
                 Downloader* dl = new Downloader(m_owner, url);
                 m_owner->root()->makeHTTPRequest(dl);
             } else {
@@ -177,9 +192,7 @@ CatalogRef Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
 {
     CatalogRef c = new Catalog(aRoot);
     c->m_url = aUrl;
-    Downloader* dl = new Downloader(c, aUrl);
-    aRoot->makeHTTPRequest(dl);
-
+    c->refresh();
     return c;
 }
 
@@ -195,33 +208,30 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
     try {
         props = new SGPropertyNode;
         readProperties(xml.str(), props);
-    } catch (sg_exception& e) {
+    } catch (sg_exception& ) {
         return NULL;
     }
 
-    if (!checkVersion(aRoot->applicationVersion(), props)) {
-        std::string redirect = redirectUrlForVersion(aRoot->applicationVersion(), props);
-        if (!redirect.empty()) {
-            SG_LOG(SG_GENERAL, SG_WARN, "catalog at " << aPath << ", version mismatch:\n\t"
-                   << "redirecting to alternate URL:" << redirect);
-            CatalogRef c = Catalog::createFromUrl(aRoot, redirect);
-            c->m_installRoot = aPath;
-            return c;
-        } else {
-            SG_LOG(SG_GENERAL, SG_WARN, "skipping catalog at " << aPath << ", version mismatch:\n\t"
-               << props->getStringValue("version") << " vs required " << aRoot->catalogVersion());
-            return NULL;
-        }
-
+    bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
+    if (!versionCheckOk) {
+        SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need" << aRoot->applicationVersion());
+        // keep the catalog but mark it as needing an update
     } else {
-        SG_LOG(SG_GENERAL, SG_INFO, "creating catalog from:" << aPath);
+        SG_LOG(SG_GENERAL, SG_DEBUG, "creating catalog from:" << aPath);
     }
 
     CatalogRef c = new Catalog(aRoot);
     c->m_installRoot = aPath;
-    c->parseProps(props); // will set status
+    c->parseProps(props);
     c->parseTimestamp();
 
+    if (versionCheckOk) {
+        // parsed XML ok, mark status as valid
+        c->changeStatus(Delegate::STATUS_SUCCESS);
+    } else {
+        c->changeStatus(Delegate::FAIL_VERSION);
+    }
+
     return c;
 }
 
@@ -229,7 +239,7 @@ bool Catalog::uninstall()
 {
     bool ok;
     bool atLeastOneFailure = false;
-    
+
     BOOST_FOREACH(PackageRef p, installedPackages()) {
         ok = p->existingInstall()->uninstall();
         if (!ok) {
@@ -246,7 +256,7 @@ bool Catalog::uninstall()
     if (!ok) {
         atLeastOneFailure = true;
     }
-    
+
     changeStatus(atLeastOneFailure ? Delegate::FAIL_FILESYSTEM
                                    : Delegate::STATUS_SUCCESS);
     return ok;
@@ -300,7 +310,13 @@ Catalog::installedPackages() const
 
 void Catalog::refresh()
 {
+    if (m_refreshRequest.valid()) {
+        // refresh in progress
+        return;
+    }
+
     Downloader* dl = new Downloader(this, url());
+    m_refreshRequest = dl;
     // will update status to IN_PROGRESS
     m_root->makeHTTPRequest(dl);
 }
@@ -308,15 +324,15 @@ void Catalog::refresh()
 struct FindById
 {
     FindById(const std::string &id) : m_id(id) {}
-    
+
     bool operator()(const PackageRef& ref) const
     {
         return ref->id() == m_id;
     }
-    
+
     std::string m_id;
 };
-    
+
 void Catalog::parseProps(const SGPropertyNode* aProps)
 {
     // copy everything except package children?
@@ -332,7 +348,7 @@ void Catalog::parseProps(const SGPropertyNode* aProps)
         if (strcmp(pkgProps->getName(), "package") == 0) {
             // can't use getPackageById here becuase the variant dict isn't
             // built yet. Instead we need to look at m_packages directly.
-            
+
             PackageList::iterator pit = std::find_if(m_packages.begin(), m_packages.end(),
                                                   FindById(pkgProps->getStringValue("id")));
             PackageRef p;
@@ -387,9 +403,6 @@ void Catalog::parseProps(const SGPropertyNode* aProps)
         Dir d(m_installRoot);
         d.create(0755);
     }
-    
-    // parsed XML ok, mark status as valid
-    changeStatus(Delegate::STATUS_SUCCESS);
 }
 
 PackageRef Catalog::getPackageById(const std::string& aId) const
@@ -425,6 +438,19 @@ std::string Catalog::url() const
     return m_url;
 }
 
+void Catalog::setUrl(const std::string &url)
+{
+    m_url = url;
+    if (m_status == Delegate::FAIL_NOT_FOUND) {
+        m_status = Delegate::FAIL_UNKNOWN;
+    }
+}
+
+std::string Catalog::name() const
+{
+    return getLocalisedString(m_props, "name");
+}
+
 std::string Catalog::description() const
 {
     return getLocalisedString(m_props, "description");
@@ -461,6 +487,11 @@ unsigned int Catalog::ageInSeconds() const
 
 bool Catalog::needsRefresh() const
 {
+    // always refresh in these cases
+    if ((m_status == Delegate::FAIL_VERSION) || (m_status == Delegate::FAIL_DOWNLOAD)) {
+        return true;
+    }
+
     unsigned int maxAge = m_props->getIntValue("max-age-sec", m_root->maxAgeSeconds());
     return (ageInSeconds() > maxAge);
 }
@@ -470,7 +501,7 @@ std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char*
     if (!aRoot) {
         return std::string();
     }
-    
+
     if (aRoot->hasChild(m_root->getLocale())) {
         const SGPropertyNode* localeRoot = aRoot->getChild(m_root->getLocale().c_str());
         if (localeRoot->hasChild(aName)) {
@@ -483,7 +514,7 @@ std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char*
 
 void Catalog::refreshComplete(Delegate::StatusCode aReason)
 {
-    m_root->catalogRefreshStatus(this, aReason);
+    m_refreshRequest.reset();
     changeStatus(aReason);
 }
 
@@ -492,7 +523,7 @@ void Catalog::changeStatus(Delegate::StatusCode newStatus)
     if (m_status == newStatus) {
         return;
     }
-    
+
     m_status = newStatus;
     m_root->catalogRefreshStatus(this, newStatus);
     m_statusCallbacks(this);