1 // Copyright (C) 2013 James Turner - zakalawe@mac.com
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Library General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 #include <simgear/package/Catalog.hxx>
20 #include <boost/foreach.hpp>
25 #include <simgear/debug/logstream.hxx>
26 #include <simgear/props/props_io.hxx>
27 #include <simgear/io/HTTPRequest.hxx>
28 #include <simgear/io/HTTPClient.hxx>
29 #include <simgear/misc/sg_dir.hxx>
30 #include <simgear/structure/exception.hxx>
31 #include <simgear/package/Package.hxx>
32 #include <simgear/package/Root.hxx>
33 #include <simgear/package/Install.hxx>
39 CatalogList static_catalogs;
41 //////////////////////////////////////////////////////////////////////////////
43 class Catalog::Downloader : public HTTP::Request
46 Downloader(Catalog* aOwner, const std::string& aUrl) :
53 virtual void responseHeadersComplete()
58 virtual void gotBodyData(const char* s, int n)
60 m_buffer += std::string(s, n);
63 virtual void responseComplete()
65 if (responseCode() != 200) {
66 SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url());
67 m_owner->refreshComplete(Delegate::FAIL_DOWNLOAD);
71 SGPropertyNode* props = new SGPropertyNode;
74 readProperties(m_buffer.data(), m_buffer.size(), props);
75 m_owner->parseProps(props);
76 } catch (sg_exception& e) {
77 SG_LOG(SG_GENERAL, SG_ALERT, "catalog parse failure:" << m_owner->url());
78 m_owner->refreshComplete(Delegate::FAIL_EXTRACT);
82 std::string ver(m_owner->root()->catalogVersion());
83 if (!checkVersion(ver, props)) {
84 SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version mismatch:\n\t"
85 << props->getStringValue("version") << " vs required " << ver);
86 m_owner->refreshComplete(Delegate::FAIL_VERSION);
90 // cache the catalog data, now we have a valid install root
91 Dir d(m_owner->installRoot());
92 SGPath p = d.file("catalog.xml");
94 std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc);
95 f.write(m_buffer.data(), m_buffer.size());
98 time(&m_owner->m_retrievedTime);
99 m_owner->writeTimestamp();
100 m_owner->refreshComplete(Delegate::FAIL_SUCCESS);
104 bool checkVersion(const std::string& aVersion, SGPropertyNode* aProps)
106 BOOST_FOREACH(SGPropertyNode* v, aProps->getChildren("version")) {
107 if (v->getStringValue() == aVersion) {
115 std::string m_buffer;
118 //////////////////////////////////////////////////////////////////////////////
120 CatalogList Catalog::allCatalogs()
122 return static_catalogs;
125 Catalog::Catalog(Root *aRoot) :
129 static_catalogs.push_back(this);
134 CatalogList::iterator it = std::find(static_catalogs.begin(), static_catalogs.end(), this);
135 static_catalogs.erase(it);
138 Catalog* Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
140 Catalog* c = new Catalog(aRoot);
141 Downloader* dl = new Downloader(c, aUrl);
142 aRoot->makeHTTPRequest(dl);
147 Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
150 xml.append("catalog.xml");
155 SGPropertyNode_ptr props;
157 props = new SGPropertyNode;
158 readProperties(xml.str(), props);
159 } catch (sg_exception& e) {
163 if (props->getStringValue("version") != aRoot->catalogVersion()) {
164 SG_LOG(SG_GENERAL, SG_WARN, "skipping catalog at " << aPath << ", version mismatch:\n\t"
165 << props->getStringValue("version") << " vs required " << aRoot->catalogVersion());
169 Catalog* c = new Catalog(aRoot);
170 c->m_installRoot = aPath;
171 c->parseProps(props);
178 Catalog::packagesMatching(const SGPropertyNode* aFilter) const
181 BOOST_FOREACH(Package* p, m_packages) {
182 if (p->matches(aFilter)) {
190 Catalog::packagesNeedingUpdate() const
193 BOOST_FOREACH(Package* p, m_packages) {
194 if (!p->isInstalled()) {
198 if (p->install()->hasUpdate()) {
205 void Catalog::refresh()
207 Downloader* dl = new Downloader(this, url());
208 m_root->makeHTTPRequest(dl);
209 m_root->catalogRefreshBegin(this);
212 void Catalog::parseProps(const SGPropertyNode* aProps)
214 // copy everything except package children?
215 m_props = new SGPropertyNode;
217 int nChildren = aProps->nChildren();
218 for (int i = 0; i < nChildren; i++) {
219 const SGPropertyNode* pkgProps = aProps->getChild(i);
220 if (strcmp(pkgProps->getName(), "package") == 0) {
221 Package* p = new Package(pkgProps, this);
222 m_packages.push_back(p);
224 SGPropertyNode* c = m_props->getChild(pkgProps->getName(), pkgProps->getIndex(), true);
225 copyProperties(pkgProps, c);
227 } // of children iteration
229 if (m_installRoot.isNull()) {
230 m_installRoot = m_root->path();
231 m_installRoot.append(id());
233 Dir d(m_installRoot);
238 Package* Catalog::getPackageById(const std::string& aId) const
240 BOOST_FOREACH(Package* p, m_packages) {
241 if (p->id() == aId) {
246 return NULL; // not found
249 std::string Catalog::id() const
251 return m_props->getStringValue("id");
254 std::string Catalog::url() const
256 return m_props->getStringValue("url");
259 std::string Catalog::description() const
261 return getLocalisedString(m_props, "description");
264 SGPropertyNode* Catalog::properties() const
266 return m_props.ptr();
269 void Catalog::parseTimestamp()
271 SGPath timestampFile = m_installRoot;
272 timestampFile.append(".timestamp");
273 std::ifstream f(timestampFile.c_str(), std::ios::in);
274 f >> m_retrievedTime;
277 void Catalog::writeTimestamp()
279 SGPath timestampFile = m_installRoot;
280 timestampFile.append(".timestamp");
281 std::ofstream f(timestampFile.c_str(), std::ios::out | std::ios::trunc);
282 f << m_retrievedTime << std::endl;
285 unsigned int Catalog::ageInSeconds() const
289 int diff = ::difftime(now, m_retrievedTime);
290 return (diff < 0) ? 0 : diff;
293 std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
295 if (aRoot->hasChild(m_root->getLocale())) {
296 const SGPropertyNode* localeRoot = aRoot->getChild(m_root->getLocale().c_str());
297 if (localeRoot->hasChild(aName)) {
298 return localeRoot->getStringValue(aName);
302 return aRoot->getStringValue(aName);
305 void Catalog::refreshComplete(Delegate::FailureCode aReason)
307 m_root->catalogRefreshComplete(this, aReason);
311 } // of namespace pkg
313 } // of namespace simgear