1 // HTTPClient.cxx -- Singleton HTTP client object
3 // Written by James Turner, started April 2012.
5 // Copyright (C) 2012 James Turner
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.
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.
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.
21 #include "HTTPClient.hxx"
25 #include <Main/fg_props.hxx>
26 #include <Include/version.h>
28 #include <simgear/sg_inlines.h>
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>
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>
41 #include <Scripting/NasalSys.hxx>
43 using namespace simgear;
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;
51 class FGHTTPClient::FGDelegate : public pkg::Delegate
54 virtual void refreshComplete()
56 SG_LOG(SG_IO, SG_INFO, "all Catalogs refreshed");
58 // auto-update; make this controlled by a property
59 pkg::Root* r = globals->packageRoot();
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());
70 virtual void catalogRefreshed(pkg::CatalogRef aCat, StatusCode aReason)
72 if (aCat.ptr() == NULL) {
73 SG_LOG(SG_IO, SG_INFO, "refresh of all catalogs done");
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());
83 case pkg::Delegate::STATUS_IN_PROGRESS:
84 SG_LOG(SG_IO, SG_INFO, "refresh of Catalog started:" << aCat->url());
88 SG_LOG(SG_IO, SG_WARN, "refresh of Catalog " << aCat->url() << " failed:" << aReason);
92 virtual void startInstall(pkg::InstallRef aInstall)
94 SG_LOG(SG_IO, SG_INFO, "beginning install of:" << aInstall->package()->id()
95 << " to local path:" << aInstall->path());
99 virtual void installProgress(pkg::InstallRef aInstall, unsigned int aBytes, unsigned int aTotal)
103 virtual void finishInstall(pkg::InstallRef aInstall, StatusCode aReason)
105 if (aReason == STATUS_SUCCESS) {
106 SG_LOG(SG_IO, SG_INFO, "finished install of:" << aInstall->package()->id()
107 << " to local path:" << aInstall->path());
109 SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
110 << " to local path:" << aInstall->path());
114 }; // of FGHTTPClient::FGDelegate
116 FGHTTPClient::FGHTTPClient() :
121 FGHTTPClient::~FGHTTPClient()
125 void FGHTTPClient::init()
127 // launcher may need to setup HTTP access abnormally early, so
128 // guard against duplicate inits
133 _http.reset(new simgear::HTTP::Client);
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"));
139 if (!proxyHost.empty()) {
140 _http->setProxy(proxyHost, proxyPort, proxyAuth);
143 pkg::Root* packageRoot = globals->packageRoot();
145 // package system needs access to the HTTP engine too
146 packageRoot->setHTTPClient(_http.get());
148 _packageDelegate.reset(new FGDelegate);
149 packageRoot->addDelegate(_packageDelegate.get());
151 // start a refresh now
152 packageRoot->refresh();
160 std::string _getDefaultCatalogId()
162 return fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.official" );
165 pkg::CatalogRef getDefaultCatalog()
167 if (!globals->packageRoot())
168 return pkg::CatalogRef();
170 return globals->packageRoot()->getCatalogById(_getDefaultCatalogId());
173 } // of anonymous namespace
175 bool FGHTTPClient::isDefaultCatalogInstalled() const
177 return getDefaultCatalog().valid();
180 void FGHTTPClient::addDefaultCatalog()
182 pkg::CatalogRef defaultCatalog = getDefaultCatalog();
183 if (!defaultCatalog.valid()) {
184 pkg::Catalog::createFromUrl(globals->packageRoot(), getDefaultCatalogUrl());
188 std::string FGHTTPClient::getDefaultCatalogId() const
190 return _getDefaultCatalogId();
193 std::string FGHTTPClient::getDefaultCatalogUrl() const
195 return fgGetString("/sim/package-system/default-catalog/url",
196 "http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/catalog.xml");;
199 static naRef f_package_existingInstall( pkg::Package& pkg,
200 const nasal::CallContext& ctx )
203 pkg.existingInstall( ctx.getArg<pkg::Package::InstallCallback>(0) )
207 static naRef f_package_uninstall(pkg::Package& pkg, const nasal::CallContext& ctx)
209 pkg::InstallRef ins = pkg.existingInstall();
217 static SGPropertyNode_ptr queryPropsFromHash(const nasal::Hash& h)
219 SGPropertyNode_ptr props(new SGPropertyNode);
221 for (nasal::Hash::const_iterator it = h.begin(); it != h.end(); ++it) {
222 std::string const key = it->getKey();
223 if ((key == "name") || (key == "description")) {
224 props->setStringValue(key, it->getValue<std::string>());
225 } else if (strutils::starts_with(key, "rating-")) {
226 props->setIntValue(key, it->getValue<int>());
227 } else if (key == "tags") {
228 string_list tags = it->getValue<string_list>();
229 string_list::const_iterator tagIt;
231 for (tagIt = tags.begin(); tagIt != tags.end(); ++tagIt) {
232 SGPropertyNode_ptr tag = props->getChild("tag", tagCount++, true);
233 tag->setStringValue(*tagIt);
235 } else if (key == "installed") {
236 props->setBoolValue(key, it->getValue<bool>());
238 SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term in hash:" << key);
245 static naRef f_root_search(pkg::Root& root, const nasal::CallContext& ctx)
247 SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
248 pkg::PackageList result = root.packagesMatching(query);
249 return ctx.to_nasal(result);
252 static naRef f_catalog_search(pkg::Catalog& cat, const nasal::CallContext& ctx)
254 SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
255 pkg::PackageList result = cat.packagesMatching(query);
256 return ctx.to_nasal(result);
259 static naRef f_package_variants(pkg::Package& pack, naContext c)
262 string_list vars(pack.variants());
263 for (string_list_iterator it = vars.begin(); it != vars.end(); ++it) {
264 h.set(*it, pack.nameForVariant(*it));
267 return h.get_naRef();
270 void FGHTTPClient::postinit()
272 NasalPackageRoot::init("PackageRoot")
273 .member("path", &pkg::Root::path)
274 .member("version", &pkg::Root::catalogVersion)
275 .method("refresh", &pkg::Root::refresh)
276 .method("catalogs", &pkg::Root::catalogs)
277 .method("packageById", &pkg::Root::getPackageById)
278 .method("catalogById", &pkg::Root::getCatalogById)
279 .method("search", &f_root_search);
281 NasalCatalog::init("Catalog")
282 .member("installRoot", &pkg::Catalog::installRoot)
283 .member("id", &pkg::Catalog::id)
284 .member("url", &pkg::Catalog::url)
285 .member("description", &pkg::Catalog::description)
286 .method("packages", &pkg::Catalog::packages)
287 .method("packageById", &pkg::Catalog::getPackageById)
288 .method("refresh", &pkg::Catalog::refresh)
289 .method("needingUpdate", &pkg::Catalog::packagesNeedingUpdate)
290 .member("installed", &pkg::Catalog::installedPackages)
291 .method("search", &f_catalog_search);
293 NasalPackage::init("Package")
294 .member("id", &pkg::Package::id)
295 .member("name", &pkg::Package::name)
296 .member("description", &pkg::Package::description)
297 .member("installed", &pkg::Package::isInstalled)
298 .member("thumbnails", &pkg::Package::thumbnailUrls)
299 .member("variants", &f_package_variants)
300 .member("revision", &pkg::Package::revision)
301 .member("catalog", &pkg::Package::catalog)
302 .method("install", &pkg::Package::install)
303 .method("uninstall", &f_package_uninstall)
304 .method("existingInstall", &f_package_existingInstall)
305 .method("lprop", &pkg::Package::getLocalisedProp)
306 .member("fileSize", &pkg::Package::fileSizeBytes);
308 typedef pkg::Install* (pkg::Install::*InstallCallback)
309 (const pkg::Install::Callback&);
310 typedef pkg::Install* (pkg::Install::*ProgressCallback)
311 (const pkg::Install::ProgressCallback&);
312 NasalInstall::init("Install")
313 .member("revision", &pkg::Install::revsion)
314 .member("pkg", &pkg::Install::package)
315 .member("path", &pkg::Install::path)
316 .member("hasUpdate", &pkg::Install::hasUpdate)
317 .method("startUpdate", &pkg::Install::startUpdate)
318 .method("uninstall", &pkg::Install::uninstall)
319 .method("done", static_cast<InstallCallback>(&pkg::Install::done))
320 .method("fail", static_cast<InstallCallback>(&pkg::Install::fail))
321 .method("always", static_cast<InstallCallback>(&pkg::Install::always))
322 .method("progress", static_cast<ProgressCallback>(&pkg::Install::progress));
324 pkg::Root* packageRoot = globals->packageRoot();
326 FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
327 nasal::Hash nasalGlobals = nasalSys->getGlobals();
328 nasal::Hash nasalPkg = nasalGlobals.createHash("pkg"); // module
329 nasalPkg.set("root", packageRoot);
333 void FGHTTPClient::shutdown()
335 pkg::Root* packageRoot = globals->packageRoot();
336 if (packageRoot && _packageDelegate.get()) {
337 packageRoot->removeDelegate(_packageDelegate.get());
340 _packageDelegate.reset();
344 void FGHTTPClient::update(double)
349 void FGHTTPClient::makeRequest(const simgear::HTTP::Request_ptr& req)
351 _http->makeRequest(req);