~ted/ubuntu-app-launch/lp1575990-more-tollerant-stop

« back to all changes in this revision

Viewing changes to libubuntu-app-launch/app-info.c

  • Committer: CI Train Bot
  • Author(s): Robert Ancell
  • Date: 2015-08-17 21:38:04 UTC
  • mfrom: (202.1.1 trunk)
  • Revision ID: ci-train-bot@canonical.com-20150817213804-229qsxs6h6sdz0mc
Depend on upstart instead of upstart-bin which is a dummy transitional package.
Approved by: Ted Gould, PS Jenkins bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright © 2015 Canonical Ltd.
3
 
 *
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.
7
 
 *
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.
12
 
 *
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/>.
15
 
 *
16
 
 * Authors:
17
 
 *     Ted Gould <ted.gould@canonical.com>
18
 
 */
19
 
 
20
 
#include <json-glib/json-glib.h>
21
 
#include <click.h>
22
 
 
23
 
#include "ubuntu-app-launch.h"
24
 
#include "app-info.h"
25
 
 
26
 
/* Try and get a manifest and do a couple sanity checks on it */
27
 
JsonObject *
28
 
get_manifest (const gchar * pkg, gchar ** pkgpath)
29
 
{
30
 
        /* Get the directory from click */
31
 
        GError * error = NULL;
32
 
 
33
 
        ClickDB * db = click_db_new();
34
 
        /* If TEST_CLICK_DB is unset, this reads the system database. */
35
 
        click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
36
 
        if (error != NULL) {
37
 
                g_warning("Unable to read Click database: %s", error->message);
38
 
                g_error_free(error);
39
 
                g_object_unref(db);
40
 
                return NULL;
41
 
        }
42
 
        /* If TEST_CLICK_USER is unset, this uses the current user name. */
43
 
        ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
44
 
        if (error != NULL) {
45
 
                g_warning("Unable to read Click database: %s", error->message);
46
 
                g_error_free(error);
47
 
                g_object_unref(db);
48
 
                return NULL;
49
 
        }
50
 
        g_object_unref(db);
51
 
        JsonObject * manifest = click_user_get_manifest(user, pkg, &error);
52
 
        if (error != NULL) {
53
 
                g_warning("Unable to get manifest for '%s' package: %s", pkg, error->message);
54
 
                g_error_free(error);
55
 
                g_object_unref(user);
56
 
                return NULL;
57
 
        }
58
 
 
59
 
        if (pkgpath != NULL) {
60
 
                *pkgpath = click_user_get_path(user, pkg, &error);
61
 
                if (error != NULL) {
62
 
                        g_warning("Unable to get the Click package directory for %s: %s", pkg, error->message);
63
 
                        g_error_free(error);
64
 
                        g_object_unref(user);
65
 
                        return NULL;
66
 
                }
67
 
        }
68
 
        g_object_unref(user);
69
 
 
70
 
        if (!json_object_has_member(manifest, "version")) {
71
 
                g_warning("Manifest file for package '%s' does not have a version", pkg);
72
 
                json_object_unref(manifest);
73
 
                return NULL;
74
 
        }
75
 
 
76
 
        return manifest;
77
 
}
78
 
 
79
 
/* Types of search we can do for an app name */
80
 
typedef enum _app_name_t app_name_t;
81
 
enum _app_name_t {
82
 
        APP_NAME_ONLY,
83
 
        APP_NAME_FIRST,
84
 
        APP_NAME_LAST
85
 
};
86
 
 
87
 
/* Figure out the app name if it's one of the keywords */
88
 
static const gchar *
89
 
manifest_app_name (JsonObject ** manifest, const gchar * pkg, const gchar * original_app)
90
 
{
91
 
        app_name_t app_type = APP_NAME_FIRST;
92
 
 
93
 
        if (original_app == NULL) {
94
 
                /* first */
95
 
        } else if (g_strcmp0(original_app, "first-listed-app") == 0) {
96
 
                /* first */
97
 
        } else if (g_strcmp0(original_app, "last-listed-app") == 0) {
98
 
                app_type = APP_NAME_LAST;
99
 
        } else if (g_strcmp0(original_app, "only-listed-app") == 0) {
100
 
                app_type = APP_NAME_ONLY;
101
 
        } else {
102
 
                return original_app;
103
 
        }
104
 
 
105
 
        if (*manifest == NULL) {
106
 
                *manifest = get_manifest(pkg, NULL);
107
 
        }
108
 
 
109
 
        JsonObject * hooks = json_object_get_object_member(*manifest, "hooks");
110
 
 
111
 
        if (hooks == NULL) {
112
 
                return NULL;
113
 
        }
114
 
 
115
 
        GList * apps = json_object_get_members(hooks);
116
 
        if (apps == NULL) {
117
 
                return NULL;
118
 
        }
119
 
 
120
 
        const gchar * retapp = NULL;
121
 
 
122
 
        switch (app_type) {
123
 
        case APP_NAME_ONLY:
124
 
                if (g_list_length(apps) == 1) {
125
 
                        retapp = (const gchar *)apps->data;
126
 
                }
127
 
                break;
128
 
        case APP_NAME_FIRST:
129
 
                retapp = (const gchar *)apps->data;
130
 
                break;
131
 
        case APP_NAME_LAST:
132
 
                retapp = (const gchar *)(g_list_last(apps)->data);
133
 
                break;
134
 
        default:
135
 
                break;
136
 
        }
137
 
 
138
 
        g_list_free(apps);
139
 
 
140
 
        return retapp;
141
 
}
142
 
 
143
 
/* Figure out the app version using the manifest */
144
 
static const gchar *
145
 
manifest_version (JsonObject ** manifest, const gchar * pkg, const gchar * original_ver)
146
 
{
147
 
        if (original_ver != NULL && g_strcmp0(original_ver, "current-user-version") != 0) {
148
 
                return original_ver;
149
 
        } else  {
150
 
                if (*manifest == NULL) {
151
 
                        *manifest = get_manifest(pkg, NULL);
152
 
                }
153
 
                g_return_val_if_fail(*manifest != NULL, NULL);
154
 
 
155
 
                return g_strdup(json_object_get_string_member(*manifest, "version"));
156
 
        }
157
 
 
158
 
        return NULL;
159
 
}
160
 
 
161
 
/* A click triplet can require using the Click DB and getting a
162
 
   manifest. This code does that to look up the versions */
163
 
gchar *
164
 
click_triplet_to_app_id (const gchar * pkg, const gchar * app, const gchar * ver)
165
 
{
166
 
        const gchar * version = NULL;
167
 
        const gchar * application = NULL;
168
 
        JsonObject * manifest = NULL;
169
 
 
170
 
        version = manifest_version(&manifest, pkg, ver);
171
 
        g_return_val_if_fail(version != NULL, NULL);
172
 
 
173
 
        application = manifest_app_name(&manifest, pkg, app);
174
 
        g_return_val_if_fail(application != NULL, NULL);
175
 
 
176
 
        gchar * retval = g_strdup_printf("%s_%s_%s", pkg, application, version);
177
 
 
178
 
        /* The object may hold allocation for some of our strings used above */
179
 
        if (manifest)
180
 
                json_object_unref(manifest);
181
 
 
182
 
        return retval;
183
 
}
184
 
 
185
 
/* Build an appid how we think it should exist and then make sure
186
 
   we can find it. Then pull it together. */
187
 
gchar *
188
 
libertine_triplet_to_app_id (const gchar * pkg, const gchar * app, const gchar * ver)
189
 
{
190
 
        if (app == NULL) {
191
 
                return NULL;
192
 
        }
193
 
 
194
 
        gchar * synthappid = g_strdup_printf("%s_%s_0.0", pkg, app);
195
 
        if (app_info_libertine(synthappid, NULL, NULL)) {
196
 
                return synthappid;
197
 
        } else {
198
 
                g_free(synthappid);
199
 
                return NULL;
200
 
        }
201
 
}
202
 
 
203
 
/* Look to see if the app id results in a desktop file, if so, fill in the params */
204
 
static gboolean
205
 
evaluate_dir (const gchar * dir, const gchar * desktop, gchar ** appdir, gchar ** appdesktop)
206
 
{
207
 
        char * fulldir = g_build_filename(dir, "applications", desktop, NULL);
208
 
        gboolean found = FALSE;
209
 
 
210
 
        if (g_file_test(fulldir, G_FILE_TEST_EXISTS)) {
211
 
                if (appdir != NULL) {
212
 
                        *appdir = g_strdup(dir);
213
 
                }
214
 
 
215
 
                if (appdesktop != NULL) {
216
 
                        *appdesktop = g_strdup_printf("applications/%s", desktop);
217
 
                }
218
 
 
219
 
                found = TRUE;
220
 
        }
221
 
 
222
 
        g_free(fulldir);
223
 
        return found;
224
 
}
225
 
 
226
 
/* Handle the legacy case where we look through the data directories */
227
 
gboolean
228
 
app_info_legacy (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
229
 
{
230
 
        gchar * desktop = g_strdup_printf("%s.desktop", appid);
231
 
 
232
 
        /* Special case the user's dir */
233
 
        if (evaluate_dir(g_get_user_data_dir(), desktop, appdir, appdesktop)) {
234
 
                g_free(desktop);
235
 
                return TRUE;
236
 
        }
237
 
 
238
 
        const char * const * data_dirs = g_get_system_data_dirs();
239
 
        int i;
240
 
        for (i = 0; data_dirs[i] != NULL; i++) {
241
 
                if (evaluate_dir(data_dirs[i], desktop, appdir, appdesktop)) {
242
 
                        g_free(desktop);
243
 
                        return TRUE;
244
 
                }
245
 
        }
246
 
 
247
 
        return FALSE;
248
 
}
249
 
 
250
 
/* Handle the libertine case where we look in the container */
251
 
gboolean
252
 
app_info_libertine (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
253
 
{
254
 
        char * container = NULL;
255
 
        char * app = NULL;
256
 
 
257
 
        if (!ubuntu_app_launch_app_id_parse(appid, &container, &app, NULL)) {
258
 
                return FALSE;
259
 
        }
260
 
 
261
 
        gchar * desktopname = g_strdup_printf("%s.desktop", app);
262
 
 
263
 
        gchar * desktopdir = g_build_filename(g_get_user_cache_dir(), "libertine-container", container, "rootfs", "usr", "share", NULL);
264
 
        gchar * desktopfile = g_build_filename(desktopdir, "applications", desktopname, NULL);
265
 
 
266
 
        if (!g_file_test(desktopfile, G_FILE_TEST_EXISTS)) {
267
 
                g_free(desktopdir);
268
 
                g_free(desktopfile);
269
 
 
270
 
                desktopdir = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container, ".local", "share", NULL);
271
 
                desktopfile = g_build_filename(desktopdir, "applications", desktopname, NULL);
272
 
 
273
 
                if (!g_file_test(desktopfile, G_FILE_TEST_EXISTS)) {
274
 
                        g_free(desktopdir);
275
 
                        g_free(desktopfile);
276
 
 
277
 
                        g_free(desktopname);
278
 
                        g_free(container);
279
 
                        g_free(app);
280
 
 
281
 
                        return FALSE;
282
 
                }
283
 
        }
284
 
 
285
 
        if (appdir != NULL) {
286
 
                *appdir = desktopdir;
287
 
        } else {
288
 
                g_free(desktopdir);
289
 
        }
290
 
 
291
 
        if (appdesktop != NULL) {
292
 
                *appdesktop = g_build_filename("applications", desktopname, NULL);
293
 
        }
294
 
 
295
 
        g_free(desktopfile);
296
 
        g_free(desktopname);
297
 
        g_free(container);
298
 
        g_free(app);
299
 
 
300
 
        return TRUE;
301
 
}
302
 
 
303
 
/* Get the information on where the desktop file is from libclick */
304
 
gboolean
305
 
app_info_click (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
306
 
{
307
 
        gchar * package = NULL;
308
 
        gchar * application = NULL;
309
 
 
310
 
        if (!ubuntu_app_launch_app_id_parse(appid, &package, &application, NULL)) {
311
 
                return FALSE;
312
 
        }
313
 
 
314
 
        JsonObject * manifest = get_manifest(package, appdir);
315
 
        if (manifest == NULL) {
316
 
                g_free(package);
317
 
                g_free(application);
318
 
                return FALSE;
319
 
        }
320
 
 
321
 
        g_free(package);
322
 
 
323
 
        if (appdesktop != NULL) {
324
 
                JsonObject * hooks = json_object_get_object_member(manifest, "hooks");
325
 
                if (hooks == NULL) {
326
 
                        json_object_unref(manifest);
327
 
                        g_free(application);
328
 
                        return FALSE;
329
 
                }
330
 
 
331
 
                JsonObject * appobj = json_object_get_object_member(hooks, application);
332
 
                g_free(application);
333
 
 
334
 
                if (appobj == NULL) {
335
 
                        json_object_unref(manifest);
336
 
                        return FALSE;
337
 
                }
338
 
 
339
 
                const gchar * desktop = json_object_get_string_member(appobj, "desktop");
340
 
                if (desktop == NULL) {
341
 
                        json_object_unref(manifest);
342
 
                        return FALSE;
343
 
                }
344
 
 
345
 
                *appdesktop = g_strdup(desktop);
346
 
        } else {
347
 
                g_free(application);
348
 
        }
349
 
 
350
 
        json_object_unref(manifest);
351
 
 
352
 
        return TRUE;
353
 
}
354