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