]> git.mxchange.org Git - flightgear.git/blob - src/Network/HTTPClient.cxx
Start wiring package manager into the launcher.
[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 namespace {
51   
52   class FGDelegate : public pkg::Delegate
53 {
54 public:
55   virtual void refreshComplete()
56   {
57     SG_LOG(SG_IO, SG_INFO, "all Catalogs refreshed");
58     
59     // auto-update; make this controlled by a property
60     pkg::Root* r = globals->packageRoot();
61     
62     pkg::PackageList toBeUpdated(r->packagesNeedingUpdate());
63     pkg::PackageList::const_iterator it;
64     for (it = toBeUpdated.begin(); it != toBeUpdated.end(); ++it) {
65       assert((*it)->isInstalled());
66       SG_LOG(SG_IO, SG_INFO, "updating:" << (*it)->id());
67       r->scheduleToUpdate((*it)->install());
68     }
69   }
70
71   virtual void failedRefresh(pkg::Catalog* aCat, FailureCode aReason)
72   {
73     switch (aReason) {
74     case pkg::Delegate::FAIL_SUCCESS:
75         SG_LOG(SG_IO, SG_WARN, "refresh of Catalog done");
76         break;
77         
78     default:
79         SG_LOG(SG_IO, SG_WARN, "refresh of Catalog " << aCat->url() << " failed:" << aReason);
80     }
81   }
82   
83   virtual void startInstall(pkg::Install* aInstall)
84   {
85     SG_LOG(SG_IO, SG_INFO, "beginning install of:" << aInstall->package()->id()
86            << " to local path:" << aInstall->path());
87
88   }
89   
90   virtual void installProgress(pkg::Install* aInstall, unsigned int aBytes, unsigned int aTotal)
91   {
92     SG_LOG(SG_IO, SG_INFO, "installing:" << aInstall->package()->id() << ":"
93            << aBytes << " of " << aTotal);
94   }
95   
96   virtual void finishInstall(pkg::Install* aInstall)
97   {
98     SG_LOG(SG_IO, SG_INFO, "finished install of:" << aInstall->package()->id()
99            << " to local path:" << aInstall->path());
100
101   }
102   
103   virtual void failedInstall(pkg::Install* aInstall, FailureCode aReason)
104   {
105     SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
106            << " to local path:" << aInstall->path());
107   }
108
109 };
110
111 } // of anonymous namespace
112
113 FGHTTPClient::FGHTTPClient() :
114     _inited(false)
115 {
116 }
117
118 FGHTTPClient::~FGHTTPClient()
119 {
120 }
121
122 void FGHTTPClient::init()
123 {
124     // launcher may need to setup HTTP access abnormally early, so
125     // guard against duplicate inits
126     if (_inited) {
127         return;
128     }
129
130   _http.reset(new simgear::HTTP::Client);
131   
132   std::string proxyHost(fgGetString("/sim/presets/proxy/host"));
133   int proxyPort(fgGetInt("/sim/presets/proxy/port"));
134   std::string proxyAuth(fgGetString("/sim/presets/proxy/auth"));
135   
136   if (!proxyHost.empty()) {
137     _http->setProxy(proxyHost, proxyPort, proxyAuth);
138   }
139   
140   pkg::Root* packageRoot = globals->packageRoot();
141   if (packageRoot) {
142     // package system needs access to the HTTP engine too
143     packageRoot->setHTTPClient(_http.get());
144     
145     packageRoot->setDelegate(new FGDelegate);
146
147     const char * defaultCatalogId = fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.default" );
148     const char * defaultCatalogUrl = fgGetString("/sim/package-system/default-catalog/url",
149             "http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/default-catalog.xml");
150     // setup default catalog if not present
151     pkg::Catalog* defaultCatalog = packageRoot->getCatalogById( defaultCatalogId );
152     if (!defaultCatalog) {
153       // always show this message
154       SG_LOG(SG_GENERAL, SG_ALERT, "default catalog not found, installing '"
155         << defaultCatalogId << "' from '" << defaultCatalogUrl << "'.");
156       pkg::Catalog::createFromUrl(packageRoot,defaultCatalogUrl);
157     }
158     
159     // start a refresh now
160     packageRoot->refresh();
161   }
162
163     _inited = true;
164 }
165
166 static naRef f_package_existingInstall( pkg::Package& pkg,
167                                         const nasal::CallContext& ctx )
168 {
169   return ctx.to_nasal(
170     pkg.existingInstall( ctx.getArg<pkg::Package::InstallCallback>(0) )
171   );
172 }
173
174 static naRef f_package_uninstall(pkg::Package& pkg, const nasal::CallContext& ctx)
175 {
176     pkg::InstallRef ins = pkg.existingInstall();
177     if (ins) {
178         ins->uninstall();
179     }
180
181     return naNil();
182 }
183
184 static SGPropertyNode_ptr queryPropsFromHash(const nasal::Hash& h)
185 {
186     SGPropertyNode_ptr props(new SGPropertyNode);
187
188     for (nasal::Hash::const_iterator it = h.begin(); it != h.end(); ++it) {
189         std::string const key = it->getKey();
190         if ((key == "name") || (key == "description")) {
191             props->setStringValue(key, it->getValue<std::string>());
192         } else if (strutils::starts_with(key, "rating-")) {
193             props->setIntValue(key, it->getValue<int>());
194         } else if (key == "tags") {
195             string_list tags = it->getValue<string_list>();
196             string_list::const_iterator tagIt;
197             int tagCount = 0;
198             for (tagIt = tags.begin(); tagIt != tags.end(); ++tagIt) {
199                 SGPropertyNode_ptr tag = props->getChild("tag", tagCount++, true);
200                 tag->setStringValue(*tagIt);
201             }
202         } else if (key == "installed") {
203             props->setBoolValue(key, it->getValue<bool>());
204         } else {
205             SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term in hash:" << key);
206         }
207     }
208
209     return props;
210 }
211
212 static naRef f_root_search(pkg::Root& root, const nasal::CallContext& ctx)
213 {
214     SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
215     pkg::PackageList result = root.packagesMatching(query);
216     return ctx.to_nasal(result);
217 }
218
219 static naRef f_catalog_search(pkg::Catalog& cat, const nasal::CallContext& ctx)
220 {
221     SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
222     pkg::PackageList result = cat.packagesMatching(query);
223     return ctx.to_nasal(result);
224 }
225
226 static naRef f_package_variants(pkg::Package& pack, naContext c)
227 {
228     nasal::Hash h(c);
229     string_list vars(pack.variants());
230     for (string_list_iterator it  = vars.begin(); it != vars.end(); ++it) {
231         h.set(*it, pack.nameForVariant(*it));
232     }
233
234     return h.get_naRef();
235 }
236
237 void FGHTTPClient::postinit()
238 {
239   NasalPackageRoot::init("PackageRoot")
240   .member("path", &pkg::Root::path)
241   .member("version", &pkg::Root::catalogVersion)
242   .method("refresh", &pkg::Root::refresh)
243   .method("catalogs", &pkg::Root::catalogs)
244   .method("packageById", &pkg::Root::getPackageById)
245   .method("catalogById", &pkg::Root::getCatalogById)
246   .method("search", &f_root_search);
247
248   NasalCatalog::init("Catalog")
249   .member("installRoot", &pkg::Catalog::installRoot)
250   .member("id", &pkg::Catalog::id)
251   .member("url", &pkg::Catalog::url)
252   .member("description", &pkg::Catalog::description)
253   .method("packages", &pkg::Catalog::packages)
254   .method("packageById", &pkg::Catalog::getPackageById)
255   .method("refresh", &pkg::Catalog::refresh)
256   .method("needingUpdate", &pkg::Catalog::packagesNeedingUpdate)
257   .member("installed", &pkg::Catalog::installedPackages)
258   .method("search", &f_catalog_search);
259   
260   NasalPackage::init("Package")
261   .member("id", &pkg::Package::id)
262   .member("name", &pkg::Package::name)
263   .member("description", &pkg::Package::description)
264   .member("installed", &pkg::Package::isInstalled)
265   .member("thumbnails", &pkg::Package::thumbnailUrls)
266   .member("variants", &f_package_variants)
267   .member("revision", &pkg::Package::revision)
268   .member("catalog", &pkg::Package::catalog)
269   .method("install", &pkg::Package::install)
270   .method("uninstall", &f_package_uninstall)
271   .method("existingInstall", &f_package_existingInstall)
272   .method("lprop", &pkg::Package::getLocalisedProp)
273   .member("fileSize", &pkg::Package::fileSizeBytes);
274   
275   typedef pkg::Install* (pkg::Install::*InstallCallback)
276                         (const pkg::Install::Callback&);
277   typedef pkg::Install* (pkg::Install::*ProgressCallback)
278                         (const pkg::Install::ProgressCallback&);
279   NasalInstall::init("Install")
280   .member("revision", &pkg::Install::revsion)
281   .member("pkg", &pkg::Install::package)
282   .member("path", &pkg::Install::path)
283   .member("hasUpdate", &pkg::Install::hasUpdate)
284   .method("startUpdate", &pkg::Install::startUpdate)
285   .method("uninstall", &pkg::Install::uninstall)
286   .method("done", static_cast<InstallCallback>(&pkg::Install::done))
287   .method("fail", static_cast<InstallCallback>(&pkg::Install::fail))
288   .method("always", static_cast<InstallCallback>(&pkg::Install::always))
289   .method("progress", static_cast<ProgressCallback>(&pkg::Install::progress));
290   
291   pkg::Root* packageRoot = globals->packageRoot();
292   if (packageRoot) {
293     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
294     nasal::Hash nasalGlobals = nasalSys->getGlobals();
295     nasal::Hash nasalPkg = nasalGlobals.createHash("pkg"); // module
296     nasalPkg.set("root", packageRoot);
297   }
298 }
299
300 void FGHTTPClient::shutdown()
301 {
302   _http.reset();
303 }
304
305 void FGHTTPClient::update(double)
306 {
307   _http->update();
308 }
309
310 void FGHTTPClient::makeRequest(const simgear::HTTP::Request_ptr& req)
311 {
312   _http->makeRequest(req);
313 }