]> git.mxchange.org Git - simgear.git/blob - simgear/package/Catalog.cxx
Package management tweaks.
[simgear.git] / simgear / package / Catalog.cxx
1 // Copyright (C) 2013  James Turner - zakalawe@mac.com
2 //
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.
7 //
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.
12 //
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.
16 //
17
18 #include <simgear/package/Catalog.hxx>
19
20 #include <boost/foreach.hpp>
21 #include <algorithm>
22 #include <fstream>
23 #include <cstring>
24
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>
34
35 namespace simgear {
36     
37 namespace pkg {
38
39 CatalogList static_catalogs;
40
41 //////////////////////////////////////////////////////////////////////////////
42
43 class Catalog::Downloader : public HTTP::Request
44 {
45 public:
46     Downloader(Catalog* aOwner, const std::string& aUrl) :
47         HTTP::Request(aUrl),
48         m_owner(aOwner)
49     {        
50     }
51     
52 protected:
53     virtual void responseHeadersComplete()
54     {
55         
56     }
57     
58     virtual void gotBodyData(const char* s, int n)
59     {
60         m_buffer += std::string(s, n);
61     }
62     
63     virtual void responseComplete()
64     {        
65         if (responseCode() != 200) {
66             SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url());
67             m_owner->refreshComplete(Delegate::FAIL_DOWNLOAD);
68             return;
69         }
70         
71         SGPropertyNode* props = new SGPropertyNode;
72         
73         try {
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);
79             return;
80         }
81         
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);
87             return;
88         }
89         
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");
93
94         std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc);
95         f.write(m_buffer.data(), m_buffer.size());
96         f.close();
97         
98         time(&m_owner->m_retrievedTime);
99         m_owner->writeTimestamp();
100         m_owner->refreshComplete(Delegate::FAIL_SUCCESS);
101     }
102     
103 private:
104     bool checkVersion(const std::string& aVersion, SGPropertyNode* aProps)
105     {
106         BOOST_FOREACH(SGPropertyNode* v, aProps->getChildren("version")) {
107             if (v->getStringValue() == aVersion) {
108                 return true;
109             }
110         }
111         return false;
112     }
113     
114     Catalog* m_owner;  
115     std::string m_buffer;
116 };
117
118 //////////////////////////////////////////////////////////////////////////////
119
120 CatalogList Catalog::allCatalogs()
121 {
122     return static_catalogs;
123 }
124
125 Catalog::Catalog(Root *aRoot) :
126     m_root(aRoot),
127     m_retrievedTime(0)
128 {
129     static_catalogs.push_back(this);
130 }
131
132 Catalog::~Catalog()
133 {
134     CatalogList::iterator it = std::find(static_catalogs.begin(), static_catalogs.end(), this);
135     static_catalogs.erase(it);
136 }
137
138 Catalog* Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
139 {
140     Catalog* c = new Catalog(aRoot);
141     Downloader* dl = new Downloader(c, aUrl);
142     aRoot->makeHTTPRequest(dl);
143     
144     return c;
145 }
146     
147 Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
148 {
149     SGPath xml = aPath;
150     xml.append("catalog.xml");
151     if (!xml.exists()) {
152         return NULL;
153     }
154     
155     SGPropertyNode_ptr props;
156     try {
157         props = new SGPropertyNode;
158         readProperties(xml.str(), props);
159     } catch (sg_exception& e) {
160         return NULL;    
161     }
162     
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());
166         return NULL;
167     }
168     
169     Catalog* c = new Catalog(aRoot);
170     c->m_installRoot = aPath;
171     c->parseProps(props);
172     c->parseTimestamp();
173     
174     return c;
175 }
176
177 PackageList
178 Catalog::packagesMatching(const SGPropertyNode* aFilter) const
179 {
180     PackageList r;
181     BOOST_FOREACH(Package* p, m_packages) {
182         if (p->matches(aFilter)) {
183             r.push_back(p);
184         }
185     }
186     return r;
187 }
188
189 PackageList
190 Catalog::packagesNeedingUpdate() const
191 {
192     PackageList r;
193     BOOST_FOREACH(Package* p, m_packages) {
194         if (!p->isInstalled()) {
195             continue;
196         }
197         
198         if (p->install()->hasUpdate()) {
199             r.push_back(p);
200         }
201     }
202     return r;
203 }
204
205 void Catalog::refresh()
206 {
207     Downloader* dl = new Downloader(this, url());
208     m_root->makeHTTPRequest(dl);
209     m_root->catalogRefreshBegin(this);
210 }
211
212 void Catalog::parseProps(const SGPropertyNode* aProps)
213 {
214     // copy everything except package children?
215     m_props = new SGPropertyNode;
216     
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);   
223         } else {
224             SGPropertyNode* c = m_props->getChild(pkgProps->getName(), pkgProps->getIndex(), true);
225             copyProperties(pkgProps, c);
226         }
227     } // of children iteration
228     
229     if (m_installRoot.isNull()) {
230         m_installRoot = m_root->path();
231         m_installRoot.append(id());
232         
233         Dir d(m_installRoot);
234         d.create(0755);
235     }
236 }
237
238 Package* Catalog::getPackageById(const std::string& aId) const
239 {
240     BOOST_FOREACH(Package* p, m_packages) {
241         if (p->id() == aId) {
242             return p;
243         }
244     }
245     
246     return NULL; // not found
247 }
248
249 std::string Catalog::id() const
250 {
251     return m_props->getStringValue("id");
252 }
253
254 std::string Catalog::url() const
255 {
256     return m_props->getStringValue("url");
257 }
258
259 std::string Catalog::description() const
260 {
261     return getLocalisedString(m_props, "description");
262 }
263     
264 SGPropertyNode* Catalog::properties() const
265 {
266     return m_props.ptr();
267 }
268
269 void Catalog::parseTimestamp()
270 {
271     SGPath timestampFile = m_installRoot;
272     timestampFile.append(".timestamp");
273     std::ifstream f(timestampFile.c_str(), std::ios::in);
274     f >> m_retrievedTime;
275 }
276
277 void Catalog::writeTimestamp()
278 {
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;
283 }
284
285 unsigned int Catalog::ageInSeconds() const
286 {
287     time_t now;
288     time(&now);
289     int diff = ::difftime(now, m_retrievedTime);
290     return (diff < 0) ? 0 : diff;
291 }
292
293 std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
294 {
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);
299         }
300     }
301     
302     return aRoot->getStringValue(aName);
303 }
304
305 void Catalog::refreshComplete(Delegate::FailureCode aReason)
306 {
307     m_root->catalogRefreshComplete(this, aReason);
308 }
309
310
311 } // of namespace pkg
312
313 } // of namespace simgear