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(false);
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(false);
82 // cache the catalog data, now we have a valid install root
83 Dir d(m_owner->installRoot());
84 SGPath p = d.file("catalog.xml");
86 std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc);
87 f.write(m_buffer.data(), m_buffer.size());
90 time(&m_owner->m_retrievedTime);
91 m_owner->writeTimestamp();
92 m_owner->refreshComplete(true);
100 //////////////////////////////////////////////////////////////////////////////
102 CatalogList Catalog::allCatalogs()
104 return static_catalogs;
107 Catalog::Catalog(Root *aRoot) :
111 static_catalogs.push_back(this);
116 CatalogList::iterator it = std::find(static_catalogs.begin(), static_catalogs.end(), this);
117 static_catalogs.erase(it);
120 Catalog* Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
122 Catalog* c = new Catalog(aRoot);
123 Downloader* dl = new Downloader(c, aUrl);
124 aRoot->getHTTPClient()->makeRequest(dl);
129 Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
132 xml.append("catalog.xml");
137 SGPropertyNode_ptr props;
139 props = new SGPropertyNode;
140 readProperties(xml.str(), props);
141 } catch (sg_exception& e) {
145 Catalog* c = new Catalog(aRoot);
146 c->m_installRoot = aPath;
147 c->parseProps(props);
154 Catalog::packagesMatching(const SGPropertyNode* aFilter) const
157 BOOST_FOREACH(Package* p, m_packages) {
158 if (p->matches(aFilter)) {
166 Catalog::packagesNeedingUpdate() const
169 BOOST_FOREACH(Package* p, m_packages) {
170 if (!p->isInstalled()) {
174 if (p->install()->hasUpdate()) {
181 void Catalog::refresh()
183 Downloader* dl = new Downloader(this, url());
184 m_root->getHTTPClient()->makeRequest(dl);
185 m_root->catalogRefreshBegin(this);
188 void Catalog::parseProps(const SGPropertyNode* aProps)
190 // copy everything except package children?
191 m_props = new SGPropertyNode;
193 int nChildren = aProps->nChildren();
194 for (int i = 0; i < nChildren; i++) {
195 const SGPropertyNode* pkgProps = aProps->getChild(i);
196 if (strcmp(pkgProps->getName(), "package") == 0) {
197 Package* p = new Package(pkgProps, this);
198 m_packages.push_back(p);
200 SGPropertyNode* c = m_props->getChild(pkgProps->getName(), pkgProps->getIndex(), true);
201 copyProperties(pkgProps, c);
203 } // of children iteration
205 if (m_installRoot.isNull()) {
206 m_installRoot = m_root->path();
207 m_installRoot.append(id());
209 Dir d(m_installRoot);
214 Package* Catalog::getPackageById(const std::string& aId) const
216 BOOST_FOREACH(Package* p, m_packages) {
217 if (p->id() == aId) {
222 return NULL; // not found
225 std::string Catalog::id() const
227 return m_props->getStringValue("id");
230 std::string Catalog::url() const
232 return m_props->getStringValue("url");
235 std::string Catalog::description() const
237 return getLocalisedString(m_props, "description");
240 SGPropertyNode* Catalog::properties() const
242 return m_props.ptr();
245 void Catalog::parseTimestamp()
247 SGPath timestampFile = m_installRoot;
248 timestampFile.append(".timestamp");
249 std::ifstream f(timestampFile.c_str(), std::ios::in);
250 f >> m_retrievedTime;
253 void Catalog::writeTimestamp()
255 SGPath timestampFile = m_installRoot;
256 timestampFile.append(".timestamp");
257 std::ofstream f(timestampFile.c_str(), std::ios::out | std::ios::trunc);
258 f << m_retrievedTime << std::endl;
261 unsigned int Catalog::ageInSeconds() const
265 int diff = ::difftime(now, m_retrievedTime);
266 return (diff < 0) ? 0 : diff;
269 std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
271 if (aRoot->hasChild(m_root->getLocale())) {
272 const SGPropertyNode* localeRoot = aRoot->getChild(m_root->getLocale().c_str());
273 if (localeRoot->hasChild(aName)) {
274 return localeRoot->getStringValue(aName);
278 return aRoot->getStringValue(aName);
281 void Catalog::refreshComplete(bool aSuccess)
283 m_root->catalogRefreshComplete(this, aSuccess);
287 } // of namespace pkg
289 } // of namespace simgear