2
* Copyright (C) 2014 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
16
* In addition, as a special exception, the copyright holders give
17
* permission to link the code of portions of this program with the
18
* OpenSSL library under certain conditions as described in each
19
* individual source file, and distribute linked combinations
21
* You must obey the GNU General Public License in all respects
22
* for all of the code used other than OpenSSL. If you modify
23
* file(s) with this exception, you may extend this exception to your
24
* version of the file(s), but you are not obligated to do so. If you
25
* do not wish to do so, delete this exception statement from your
26
* version. If you delete this exception statement from all source
27
* files in the program, then also delete it here.
30
#include <click/interface.h>
31
#include <click/key_file_locator.h>
32
#include <click/index.h>
33
#include <click/webclient.h>
34
#include <click/network_access_manager.h>
35
#include <click/qtbridge.h>
36
#include <click/departments-db.h>
47
// network request errors
48
DEPTS_ERROR_NETWORK = 2,
54
DEPTS_ERROR_CLICK_PARSE = 10,
55
DEPTS_ERROR_CLICK_CALL = 11,
56
DEPTS_ERROR_CLICK_UNKNOWN = 12
59
std::list<std::pair<std::string, std::string>> NON_CLICK_APPS = {
60
{"address-book-app.desktop", "accessories"},
61
{"dialer-app.desktop", "accessories"},
62
{"mediaplayer-app.desktop", "sound-video"},
63
{"messaging-app.desktop", "accessories"},
64
{"ubuntu-system-settings.desktop", "accessories"},
65
{"webbrowser-app.desktop", "web-browsers"}
68
QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
69
click::Interface iface(keyFileLocator);
71
void noDebug(QtMsgType, const QMessageLogContext&, const QString&) {}
73
int main(int argc, char **argv)
77
std::cerr << "Usage: " << argv[0] << " DBFILE LOCALE1 [LOCALE2 ...]" << std::endl;
78
return DEPTS_ERROR_ARG;
81
const std::string dbfile(argv[1]);
82
const std::set<std::string> locales(argv + 2, argv + argc);
84
if (!getenv("INIT_DEPARTMENTS_DEBUG"))
85
qInstallMessageHandler(noDebug);
87
std::unique_ptr<click::DepartmentsDb> db;
90
db.reset(new click::DepartmentsDb(dbfile));
92
catch (const std::exception &e)
94
std::cerr << "Failed to open departments database " << dbfile << std::endl;
95
return DEPTS_ERROR_DB;
98
auto nam = QSharedPointer<click::network::AccessManager>(new click::network::AccessManager());
99
auto client = QSharedPointer<click::web::Client>(new click::web::Client(nam));
100
click::Index index(client);
101
std::vector<click::web::Cancellable> cnc;
103
std::promise<void> qt_ready;
104
std::promise<void> bootstrap_ready;
105
std::promise<click::PackageSet> pkgs_ready;
106
auto qt_ready_ft = qt_ready.get_future();
107
auto bootstrap_ft = bootstrap_ready.get_future();
108
auto pkgs_ft = pkgs_ready.get_future();
110
std::atomic<int> return_val(0);
111
std::atomic<std::vector<std::string>::size_type> num_of_locales(locales.size());
112
std::atomic<click::PackageSet::size_type> num_of_pkgs(0);
115
// a thread that does bootstrap request
116
std::thread net_thread([&]() {
119
for (auto const locale: locales)
121
qt::core::world::enter_with_task([&return_val, &bootstrap_ready, &index, &cnc, &num_of_locales, &db, locale]() {
123
std::cout << "Getting departments for locale '" << locale << "'" << std::endl;
124
cnc.push_back(index.bootstrap([&return_val, &bootstrap_ready, &num_of_locales, &db, locale](const click::DepartmentList& depts, const click::HighlightList&, click::Index::Error error, int) {
125
std::cout << "Bootstrap call for locale '" << locale << "' finished" << std::endl;
127
if (error == click::Index::Error::NoError)
131
db->store_departments(depts, locale);
132
std::cout << "Stored departments for locale '" << locale << "'" << std::endl;
134
catch (const std::exception& e)
136
std::cerr << "Failed to update departments database: " << e.what() << std::endl;
137
return_val = DEPTS_ERROR_DB;
140
if (--num_of_locales == 0)
142
std::cout << "All locales processed" << std::endl;
143
bootstrap_ready.set_value();
148
std::cerr << "Network error" << std::endl;
149
return_val = DEPTS_ERROR_NETWORK;
150
bootstrap_ready.set_value();
151
qt::core::world::destroy();
160
// a thread that iterates over all packages and performs details requests
161
// it initially blocks on bootstrap_ft and pkgs_ft future
162
// (waits until bootstrap finished and main thread gets all the click packages)
163
std::thread details_thread([&]() {
165
auto const pkgs = pkgs_ft.get();
167
num_of_pkgs = pkgs.size();
169
// if click list failed or nothing to do, then end this thread and stop Qt bridge
170
if (return_val != 0 || num_of_pkgs == 0)
172
std::cerr << "No packages to process or an error occurred, not fetching departments" << std::endl;
173
qt::core::world::destroy();
177
std::cout << "Getting package details for " << num_of_pkgs << " packages" << std::endl;
180
// note: this queues requests for all the packages; the number of packages to process
181
// is kept in num_of_pkgs which gets decreased, the last processed package will stop Qt bridge.
182
for (auto const& pkg: pkgs)
184
auto const pkgname = pkg.name;
186
qt::core::world::enter_with_task([&return_val, &index, &cnc, &num_of_pkgs, pkgname, &db]() {
188
cnc.push_back(index.get_details(pkgname, [&return_val, &num_of_pkgs, pkgname, &db](const click::PackageDetails& details, click::Index::Error error) {
189
std::cout << "Details call for " << pkgname << " finished" << std::endl;
191
if (error == click::Index::Error::NoError)
195
std::cout << "Storing package department for " << pkgname << ", " << details.department << std::endl;
196
db->store_package_mapping(pkgname, details.department);
198
catch (const std::exception& e)
200
std::cerr << "Failed to update departments database: " << e.what() << std::endl;
201
return_val = DEPTS_ERROR_DB;
206
std::cerr << "Network error" << std::endl;
207
return_val = DEPTS_ERROR_NETWORK;
209
if (--num_of_pkgs == 0)
211
std::cout << "All packages processed" << std::endl;
212
qt::core::world::destroy();
220
// enter Qt world; this blocks until qt::core:;world::destroy() gets called
221
qt::core::world::build_and_run(argc, argv, [&pkgs_ready, &qt_ready, &return_val]() {
223
qt::core::world::enter_with_task([&return_val, &pkgs_ready, &qt_ready]() {
224
std::cout << "Querying click for installed packages" << std::endl;
225
iface.get_installed_packages([&return_val, &pkgs_ready, &qt_ready](click::PackageSet pkgs, click::InterfaceError error) {
226
if (error == click::InterfaceError::NoError)
228
std::cout << "Found: " << pkgs.size() << " click packages" << std::endl;
232
if (error == click::InterfaceError::ParseError)
234
std::cerr << "Error parsing click output" << std::endl;
235
return_val = DEPTS_ERROR_CLICK_PARSE;
237
else if (error == click::InterfaceError::CallError)
239
std::cerr << "Error calling click command" << std::endl;
240
return_val = DEPTS_ERROR_CLICK_CALL;
244
std::cerr << "An unknown click error occured" << std::endl;
245
return_val = DEPTS_ERROR_CLICK_UNKNOWN;
248
qt_ready.set_value();
249
pkgs_ready.set_value(pkgs); // this unblocks net_thread
255
details_thread.join();
259
for (auto const& app: NON_CLICK_APPS)
261
db->store_package_mapping(app.first, app.second);
264
catch (const std::exception &e)
266
std::cerr << "Failed to insert non-click appsint database" << std::endl;
267
return DEPTS_ERROR_DB;
270
std::cout << std::endl << "Summary:" << std::endl
271
<< "Number of department mappings: " << db->department_mapping_count() << std::endl
272
<< "Number of department names (all locales): " << db->department_name_count() << std::endl
273
<< "Number of applications: " << db->package_count() << std::endl;