]> git.mxchange.org Git - simgear.git/blob - simgear/package/Catalog.cxx
LGPL license on package files.
[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(false);
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(false);
79             return;
80         }
81         
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");
85
86         std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc);
87         f.write(m_buffer.data(), m_buffer.size());
88         f.close();
89         
90         time(&m_owner->m_retrievedTime);
91         m_owner->writeTimestamp();
92         m_owner->refreshComplete(true);
93     }
94     
95 private:
96     Catalog* m_owner;  
97     std::string m_buffer;
98 };
99
100 //////////////////////////////////////////////////////////////////////////////
101
102 CatalogList Catalog::allCatalogs()
103 {
104     return static_catalogs;
105 }
106
107 Catalog::Catalog(Root *aRoot) :
108     m_root(aRoot),
109     m_retrievedTime(0)
110 {
111     static_catalogs.push_back(this);
112 }
113
114 Catalog::~Catalog()
115 {
116     CatalogList::iterator it = std::find(static_catalogs.begin(), static_catalogs.end(), this);
117     static_catalogs.erase(it);
118 }
119
120 Catalog* Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
121 {
122     Catalog* c = new Catalog(aRoot);
123     Downloader* dl = new Downloader(c, aUrl);
124     aRoot->getHTTPClient()->makeRequest(dl);
125     
126     return c;
127 }
128     
129 Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
130 {
131     SGPath xml = aPath;
132     xml.append("catalog.xml");
133     if (!xml.exists()) {
134         return NULL;
135     }
136     
137     SGPropertyNode_ptr props;
138     try {
139         props = new SGPropertyNode;
140         readProperties(xml.str(), props);
141     } catch (sg_exception& e) {
142         return NULL;    
143     }
144     
145     Catalog* c = new Catalog(aRoot);
146     c->m_installRoot = aPath;
147     c->parseProps(props);
148     c->parseTimestamp();
149     
150     return c;
151 }
152
153 PackageList
154 Catalog::packagesMatching(const SGPropertyNode* aFilter) const
155 {
156     PackageList r;
157     BOOST_FOREACH(Package* p, m_packages) {
158         if (p->matches(aFilter)) {
159             r.push_back(p);
160         }
161     }
162     return r;
163 }
164
165 PackageList
166 Catalog::packagesNeedingUpdate() const
167 {
168     PackageList r;
169     BOOST_FOREACH(Package* p, m_packages) {
170         if (!p->isInstalled()) {
171             continue;
172         }
173         
174         if (p->install()->hasUpdate()) {
175             r.push_back(p);
176         }
177     }
178     return r;
179 }
180
181 void Catalog::refresh()
182 {
183     Downloader* dl = new Downloader(this, url());
184     m_root->getHTTPClient()->makeRequest(dl);
185     m_root->catalogRefreshBegin(this);
186 }
187
188 void Catalog::parseProps(const SGPropertyNode* aProps)
189 {
190     // copy everything except package children?
191     m_props = new SGPropertyNode;
192     
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);   
199         } else {
200             SGPropertyNode* c = m_props->getChild(pkgProps->getName(), pkgProps->getIndex(), true);
201             copyProperties(pkgProps, c);
202         }
203     } // of children iteration
204     
205     if (m_installRoot.isNull()) {
206         m_installRoot = m_root->path();
207         m_installRoot.append(id());
208         
209         Dir d(m_installRoot);
210         d.create(0755);
211     }
212 }
213
214 Package* Catalog::getPackageById(const std::string& aId) const
215 {
216     BOOST_FOREACH(Package* p, m_packages) {
217         if (p->id() == aId) {
218             return p;
219         }
220     }
221     
222     return NULL; // not found
223 }
224
225 std::string Catalog::id() const
226 {
227     return m_props->getStringValue("id");
228 }
229
230 std::string Catalog::url() const
231 {
232     return m_props->getStringValue("url");
233 }
234
235 std::string Catalog::description() const
236 {
237     return getLocalisedString(m_props, "description");
238 }
239     
240 SGPropertyNode* Catalog::properties() const
241 {
242     return m_props.ptr();
243 }
244
245 void Catalog::parseTimestamp()
246 {
247     SGPath timestampFile = m_installRoot;
248     timestampFile.append(".timestamp");
249     std::ifstream f(timestampFile.c_str(), std::ios::in);
250     f >> m_retrievedTime;
251 }
252
253 void Catalog::writeTimestamp()
254 {
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;
259 }
260
261 unsigned int Catalog::ageInSeconds() const
262 {
263     time_t now;
264     time(&now);
265     int diff = ::difftime(now, m_retrievedTime);
266     return (diff < 0) ? 0 : diff;
267 }
268
269 std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
270 {
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);
275         }
276     }
277     
278     return aRoot->getStringValue(aName);
279 }
280
281 void Catalog::refreshComplete(bool aSuccess)
282 {
283     m_root->catalogRefreshComplete(this, aSuccess);
284 }
285
286
287 } // of namespace pkg
288
289 } // of namespace simgear