]> git.mxchange.org Git - flightgear.git/blob - src/Network/HTTPClient.cxx
Code cleanups, code updates and fix at least on (possible) devide-by-zero
[flightgear.git] / src / Network / HTTPClient.cxx
1 // HTTPClient.cxx -- Singleton HTTP client object
2 //
3 // Written by James Turner, started April 2012.
4 //
5 // Copyright (C) 2012  James Turner
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #include "HTTPClient.hxx"
22
23 #include <cassert>
24
25 #include <Main/fg_props.hxx>
26 #include <Include/version.h>
27
28 #include <simgear/sg_inlines.h>
29
30 #include <simgear/package/Root.hxx>
31 #include <simgear/package/Catalog.hxx>
32 #include <simgear/package/Delegate.hxx>
33 #include <simgear/package/Install.hxx>
34 #include <simgear/package/Package.hxx>
35
36 #include <simgear/nasal/cppbind/from_nasal.hxx>
37 #include <simgear/nasal/cppbind/to_nasal.hxx>
38 #include <simgear/nasal/cppbind/NasalHash.hxx>
39 #include <simgear/nasal/cppbind/Ghost.hxx>
40
41 #include <Scripting/NasalSys.hxx>
42
43 using namespace simgear;
44
45 typedef nasal::Ghost<pkg::RootRef> NasalPackageRoot;
46 typedef nasal::Ghost<pkg::PackageRef> NasalPackage;
47 typedef nasal::Ghost<pkg::CatalogRef> NasalCatalog;
48 typedef nasal::Ghost<pkg::InstallRef> NasalInstall;
49
50
51 class FGHTTPClient::FGDelegate : public pkg::Delegate
52 {
53 public:
54   virtual void refreshComplete()
55   {
56     SG_LOG(SG_IO, SG_INFO, "all Catalogs refreshed");
57     
58     // auto-update; make this controlled by a property
59     pkg::Root* r = globals->packageRoot();
60     
61     pkg::PackageList toBeUpdated(r->packagesNeedingUpdate());
62     pkg::PackageList::const_iterator it;
63     for (it = toBeUpdated.begin(); it != toBeUpdated.end(); ++it) {
64       assert((*it)->isInstalled());
65       SG_LOG(SG_IO, SG_INFO, "updating:" << (*it)->id());
66       r->scheduleToUpdate((*it)->install());
67     }
68   }
69     
70   virtual void catalogRefreshed(pkg::CatalogRef aCat, StatusCode aReason)
71   {
72       if (aCat.ptr() == NULL) {
73           SG_LOG(SG_IO, SG_INFO, "refresh of all catalogs done");
74           return;
75       }
76       
77     switch (aReason) {
78     case pkg::Delegate::STATUS_SUCCESS:
79     case pkg::Delegate::STATUS_REFRESHED:
80         SG_LOG(SG_IO, SG_INFO, "refresh of Catalog done:" << aCat->url());
81         break;
82             
83     case pkg::Delegate::STATUS_IN_PROGRESS:
84         SG_LOG(SG_IO, SG_INFO, "refresh of Catalog started:" << aCat->url());
85         break;
86             
87     default:
88         SG_LOG(SG_IO, SG_WARN, "refresh of Catalog " << aCat->url() << " failed:" << aReason);
89     }
90   }
91   
92   virtual void startInstall(pkg::InstallRef aInstall)
93   {
94     SG_LOG(SG_IO, SG_INFO, "beginning install of:" << aInstall->package()->id()
95            << " to local path:" << aInstall->path());
96
97   }
98   
99   virtual void installProgress(pkg::InstallRef aInstall, unsigned int aBytes, unsigned int aTotal)
100   {
101   }
102   
103   virtual void finishInstall(pkg::InstallRef aInstall, StatusCode aReason)
104   {
105       if (aReason == STATUS_SUCCESS) {
106     SG_LOG(SG_IO, SG_INFO, "finished install of:" << aInstall->package()->id()
107            << " to local path:" << aInstall->path());
108       } else {
109           SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
110                  << " to local path:" << aInstall->path());
111       }
112
113   }
114 }; // of FGHTTPClient::FGDelegate
115
116 FGHTTPClient::FGHTTPClient() :
117     _inited(false)
118 {
119 }
120
121 FGHTTPClient::~FGHTTPClient()
122 {
123 }
124
125 void FGHTTPClient::init()
126 {
127     // launcher may need to setup HTTP access abnormally early, so
128     // guard against duplicate inits
129     if (_inited) {
130         return;
131     }
132
133   _http.reset(new simgear::HTTP::Client);
134   
135   std::string proxyHost(fgGetString("/sim/presets/proxy/host"));
136   int proxyPort(fgGetInt("/sim/presets/proxy/port"));
137   std::string proxyAuth(fgGetString("/sim/presets/proxy/auth"));
138   
139   if (!proxyHost.empty()) {
140     _http->setProxy(proxyHost, proxyPort, proxyAuth);
141   }
142   
143   pkg::Root* packageRoot = globals->packageRoot();
144   if (packageRoot) {
145     // package system needs access to the HTTP engine too
146     packageRoot->setHTTPClient(_http.get());
147     
148     _packageDelegate.reset(new FGDelegate);
149     packageRoot->addDelegate(_packageDelegate.get());
150     
151     // start a refresh now
152     // setting 'force' true to work around the problem where a slightly stale
153     // catalog exists, but aircraft are modified - this causes an MD5 sum
154     // mismatch
155     packageRoot->refresh(true);
156   }
157
158     _inited = true;
159 }
160
161 namespace {
162
163 std::string _getDefaultCatalogId()
164 {
165     return fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.official" );
166 }
167
168 pkg::CatalogRef getDefaultCatalog()
169 {
170     if (!globals->packageRoot())
171         return pkg::CatalogRef();
172
173     return globals->packageRoot()->getCatalogById(_getDefaultCatalogId());
174 }
175
176 } // of anonymous namespace
177
178 bool FGHTTPClient::isDefaultCatalogInstalled() const
179 {
180     return getDefaultCatalog().valid();
181 }
182
183 void FGHTTPClient::addDefaultCatalog()
184 {
185     pkg::CatalogRef defaultCatalog = getDefaultCatalog();
186     if (!defaultCatalog.valid()) {
187       pkg::Catalog::createFromUrl(globals->packageRoot(), getDefaultCatalogUrl());
188     }
189 }
190
191 std::string FGHTTPClient::getDefaultCatalogId() const
192 {
193     return _getDefaultCatalogId();
194 }
195
196 std::string FGHTTPClient::getDefaultCatalogUrl() const
197 {
198     return fgGetString("/sim/package-system/default-catalog/url",
199                        "http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/catalog.xml");;
200 }
201
202 static naRef f_package_existingInstall( pkg::Package& pkg,
203                                         const nasal::CallContext& ctx )
204 {
205   return ctx.to_nasal(
206     pkg.existingInstall( ctx.getArg<pkg::Package::InstallCallback>(0) )
207   );
208 }
209
210 static naRef f_package_uninstall(pkg::Package& pkg, const nasal::CallContext& ctx)
211 {
212     pkg::InstallRef ins = pkg.existingInstall();
213     if (ins) {
214         ins->uninstall();
215     }
216
217     return naNil();
218 }
219
220 static SGPropertyNode_ptr queryPropsFromHash(const nasal::Hash& h)
221 {
222     SGPropertyNode_ptr props(new SGPropertyNode);
223
224     for (nasal::Hash::const_iterator it = h.begin(); it != h.end(); ++it) {
225         std::string const key = it->getKey();
226         if ((key == "name") || (key == "description")) {
227             props->setStringValue(key, it->getValue<std::string>());
228         } else if (strutils::starts_with(key, "rating-")) {
229             props->setIntValue(key, it->getValue<int>());
230         } else if (key == "tags") {
231             string_list tags = it->getValue<string_list>();
232             string_list::const_iterator tagIt;
233             int tagCount = 0;
234             for (tagIt = tags.begin(); tagIt != tags.end(); ++tagIt) {
235                 SGPropertyNode_ptr tag = props->getChild("tag", tagCount++, true);
236                 tag->setStringValue(*tagIt);
237             }
238         } else if (key == "installed") {
239             props->setBoolValue(key, it->getValue<bool>());
240         } else {
241             SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term in hash:" << key);
242         }
243     }
244
245     return props;
246 }
247
248 static naRef f_root_search(pkg::Root& root, const nasal::CallContext& ctx)
249 {
250     SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
251     pkg::PackageList result = root.packagesMatching(query);
252     return ctx.to_nasal(result);
253 }
254
255 static naRef f_catalog_search(pkg::Catalog& cat, const nasal::CallContext& ctx)
256 {
257     SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
258     pkg::PackageList result = cat.packagesMatching(query);
259     return ctx.to_nasal(result);
260 }
261
262 static naRef f_package_variants(pkg::Package& pack, naContext c)
263 {
264     nasal::Hash h(c);
265     string_list vars(pack.variants());
266     for (string_list_iterator it  = vars.begin(); it != vars.end(); ++it) {
267         h.set(*it, pack.nameForVariant(*it));
268     }
269
270     return h.get_naRef();
271 }
272
273 void FGHTTPClient::postinit()
274 {
275   NasalPackageRoot::init("PackageRoot")
276   .member("path", &pkg::Root::path)
277   .member("version", &pkg::Root::catalogVersion)
278   .method("refresh", &pkg::Root::refresh)
279   .method("catalogs", &pkg::Root::catalogs)
280   .method("packageById", &pkg::Root::getPackageById)
281   .method("catalogById", &pkg::Root::getCatalogById)
282   .method("search", &f_root_search);
283
284   NasalCatalog::init("Catalog")
285   .member("installRoot", &pkg::Catalog::installRoot)
286   .member("id", &pkg::Catalog::id)
287   .member("url", &pkg::Catalog::url)
288   .member("description", &pkg::Catalog::description)
289   .method("packages", &pkg::Catalog::packages)
290   .method("packageById", &pkg::Catalog::getPackageById)
291   .method("refresh", &pkg::Catalog::refresh)
292   .method("needingUpdate", &pkg::Catalog::packagesNeedingUpdate)
293   .member("installed", &pkg::Catalog::installedPackages)
294   .method("search", &f_catalog_search);
295   
296   NasalPackage::init("Package")
297   .member("id", &pkg::Package::id)
298   .member("name", &pkg::Package::name)
299   .member("description", &pkg::Package::description)
300   .member("installed", &pkg::Package::isInstalled)
301   .member("thumbnails", &pkg::Package::thumbnailUrls)
302   .member("variants", &f_package_variants)
303   .member("revision", &pkg::Package::revision)
304   .member("catalog", &pkg::Package::catalog)
305   .method("install", &pkg::Package::install)
306   .method("uninstall", &f_package_uninstall)
307   .method("existingInstall", &f_package_existingInstall)
308   .method("lprop", &pkg::Package::getLocalisedProp)
309   .member("fileSize", &pkg::Package::fileSizeBytes);
310   
311   typedef pkg::Install* (pkg::Install::*InstallCallback)
312                         (const pkg::Install::Callback&);
313   typedef pkg::Install* (pkg::Install::*ProgressCallback)
314                         (const pkg::Install::ProgressCallback&);
315   NasalInstall::init("Install")
316   .member("revision", &pkg::Install::revsion)
317   .member("pkg", &pkg::Install::package)
318   .member("path", &pkg::Install::path)
319   .member("hasUpdate", &pkg::Install::hasUpdate)
320   .method("startUpdate", &pkg::Install::startUpdate)
321   .method("uninstall", &pkg::Install::uninstall)
322   .method("done", static_cast<InstallCallback>(&pkg::Install::done))
323   .method("fail", static_cast<InstallCallback>(&pkg::Install::fail))
324   .method("always", static_cast<InstallCallback>(&pkg::Install::always))
325   .method("progress", static_cast<ProgressCallback>(&pkg::Install::progress));
326   
327   pkg::Root* packageRoot = globals->packageRoot();
328   if (packageRoot) {
329     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
330     nasal::Hash nasalGlobals = nasalSys->getGlobals();
331     nasal::Hash nasalPkg = nasalGlobals.createHash("pkg"); // module
332     nasalPkg.set("root", packageRoot);
333   }
334 }
335
336 void FGHTTPClient::shutdown()
337 {
338     pkg::Root* packageRoot = globals->packageRoot();
339     if (packageRoot && _packageDelegate.get()) {
340         packageRoot->removeDelegate(_packageDelegate.get());
341     }
342
343     _packageDelegate.reset();
344     _http.reset();
345
346     _inited = false;
347 }
348
349 void FGHTTPClient::update(double)
350 {
351   _http->update();
352 }
353
354 void FGHTTPClient::makeRequest(const simgear::HTTP::Request_ptr& req)
355 {
356   _http->makeRequest(req);
357 }