~dobey/ubuntu-app-launch/ship-app-mock

33.2.2 by Ted Gould
Adding a desktop hook binary
1
/*
2
 * Copyright 2013 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
33.2.40 by Ted Gould
Adding some documentation on what the hook is doing.
20
/*
21
22
INTRODUCTION:
23
24
This is a hook for Click packages.  You can find information on Click package hooks in
25
the click documentation:
26
135.2.1 by Colin Watson
Update Click documentation link.
27
https://click.readthedocs.org/en/latest/
33.2.40 by Ted Gould
Adding some documentation on what the hook is doing.
28
29
Probably the biggest thing to understand for how this code works is that you need to
30
understand that this hook is run after one, or many packages are installed.  A set of
31
symbolic links are made to the desktop files per-application (not per-package) in the
152.1.6 by Ted Gould
Removing more instances if 'upstart' to not confuse people, leaving the tracepoints for now
32
directory specified in ubuntu-app-launcher-desktop.click-hook.in.  Those desktop files
33.2.40 by Ted Gould
Adding some documentation on what the hook is doing.
33
give us the App ID of the packages that are installed and have applications needing
34
desktop files in them.  We then operate on each of them ensuring that they are synchronized
35
with the desktop files in ~/.local/share/applications/.
36
37
The desktop files that we're creating there ARE NOT used for execution by the
152.1.6 by Ted Gould
Removing more instances if 'upstart' to not confuse people, leaving the tracepoints for now
38
ubuntu-app-launch Upstart jobs.  They are there so that Unity can know which applications
33.2.40 by Ted Gould
Adding some documentation on what the hook is doing.
39
are installed for this user and they provide an Exec line to allow compatibility with
152.1.6 by Ted Gould
Removing more instances if 'upstart' to not confuse people, leaving the tracepoints for now
40
desktop environments that are not using ubuntu-app-launch for launching applications.
33.2.40 by Ted Gould
Adding some documentation on what the hook is doing.
41
You should not modify them and expect any executing under Unity to change.
42
43
*/
44
33.2.3 by Ted Gould
Set up the skeleton for determining state
45
#include <gio/gio.h>
33.2.16 by Ted Gould
Upping the warnings
46
#include <glib/gstdio.h>
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
47
#include <click.h>
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
48
#include <string.h>
51.1.6 by Ted Gould
Pipe directly
49
#include <errno.h>
208.9.1 by Ted Gould
Switching to libwhoopsie for recoverable problem support
50
#include <libwhoopsie/recoverable-problem.h>
33.2.3 by Ted Gould
Set up the skeleton for determining state
51
33.2.15 by Ted Gould
Move the app id checking to the helpers file
52
#include "helpers.h"
53
33.2.3 by Ted Gould
Set up the skeleton for determining state
54
typedef struct _app_state_t app_state_t;
55
struct _app_state_t {
56
	gchar * app_id;
57
	gboolean has_click;
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
58
	gboolean has_desktop;
33.2.39 by Ted Gould
Switching to modification time.
59
	guint64 click_modified;
60
	guint64 desktop_modified;
33.2.3 by Ted Gould
Set up the skeleton for determining state
61
};
62
168.1.6 by Ted Gould
Cleaning up and hadding an old key prefix
63
/* Desktop Group */
64
#define DESKTOP_GROUP      "Desktop Entry"
65
/* Desktop Keys */
66
#define APP_ID_KEY         "X-Ubuntu-Application-ID"
67
#define PATH_KEY           "Path"
68
#define EXEC_KEY           "Exec"
69
#define ICON_KEY           "Icon"
70
#define SYMBOLIC_ICON_KEY  "X-Ubuntu-SymbolicIcon"
175.5.2 by Ted Gould
Putting the built key into the cache desktop file
71
#define SOURCE_FILE_KEY    "X-Ubuntu-UAL-Source-Desktop"
168.1.6 by Ted Gould
Cleaning up and hadding an old key prefix
72
/* Other */
73
#define OLD_KEY_PREFIX     "X-Ubuntu-Old-"
168.1.3 by Ted Gould
Make desktop keys into defines
74
33.2.5 by Ted Gould
Fleshing out adding a click package directory
75
/* Find an entry in the app array */
76
app_state_t *
77
find_app_entry (const gchar * name, GArray * app_array)
78
{
79
	int i;
80
	for (i = 0; i < app_array->len; i++) {
81
		app_state_t * state = &g_array_index(app_array, app_state_t, i);
82
83
		if (g_strcmp0(state->app_id, name) == 0) {
84
			return state;
85
		}
86
	}
87
88
	app_state_t newstate;
89
	newstate.has_click = FALSE;
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
90
	newstate.has_desktop = FALSE;
33.2.39 by Ted Gould
Switching to modification time.
91
	newstate.click_modified = 0;
92
	newstate.desktop_modified = 0;
33.2.5 by Ted Gould
Fleshing out adding a click package directory
93
	newstate.app_id = g_strdup(name);
94
95
	g_array_append_val(app_array, newstate);
96
97
	/* Note: The pointer needs to be the entry in the array, not the
98
	   one that we have on the stack.  Criticaly important. */
99
	app_state_t * statepntr = &g_array_index(app_array, app_state_t, app_array->len - 1);
100
	return statepntr;
101
}
102
33.2.22 by Ted Gould
Handle the case of creation time changing
103
/* Looks up the file creation time, which seems harder with GLib
104
   than it should be */
105
guint64
33.2.39 by Ted Gould
Switching to modification time.
106
modified_time (const gchar * dir, const gchar * filename)
33.2.22 by Ted Gould
Handle the case of creation time changing
107
{
108
	gchar * path = g_build_filename(dir, filename, NULL);
109
	GFile * file = g_file_new_for_path(path);
33.2.39 by Ted Gould
Switching to modification time.
110
	GFileInfo * info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
33.2.22 by Ted Gould
Handle the case of creation time changing
111
33.2.39 by Ted Gould
Switching to modification time.
112
	guint64 time = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
33.2.22 by Ted Gould
Handle the case of creation time changing
113
114
	g_object_unref(info);
115
	g_object_unref(file);
116
	g_free(path);
117
118
	return time;
119
}
120
33.2.3 by Ted Gould
Set up the skeleton for determining state
121
/* Look at an click package entry */
122
void
33.2.22 by Ted Gould
Handle the case of creation time changing
123
add_click_package (const gchar * dir, const gchar * name, GArray * app_array)
33.2.3 by Ted Gould
Set up the skeleton for determining state
124
{
33.2.27 by Ted Gould
Change the name of the symlink
125
	if (!g_str_has_suffix(name, ".desktop")) {
126
		return;
127
	}
128
129
	gchar * appid = g_strdup(name);
130
	g_strstr_len(appid, -1, ".desktop")[0] = '\0';
131
132
	app_state_t * state = find_app_entry(appid, app_array);
33.2.5 by Ted Gould
Fleshing out adding a click package directory
133
	state->has_click = TRUE;
33.2.39 by Ted Gould
Switching to modification time.
134
	state->click_modified = modified_time(dir, name);
33.2.3 by Ted Gould
Set up the skeleton for determining state
135
33.2.27 by Ted Gould
Change the name of the symlink
136
	g_free(appid);
137
33.2.3 by Ted Gould
Set up the skeleton for determining state
138
	return;
139
}
140
175.5.1 by Ted Gould
Insert the new function to check
141
/* Look at the desktop file and ensure that it was built by us, and if it
175.5.18 by Ted Gould
Fix typo
142
   was that its source still exists */
175.5.1 by Ted Gould
Insert the new function to check
143
gboolean
144
desktop_source_exists (const gchar * dir, const gchar * name)
145
{
175.5.3 by Ted Gould
Checking the new value we're putting into the desktop file
146
	gchar * desktopfile = g_build_filename(dir, name, NULL);
147
148
	GKeyFile * keyfile = g_key_file_new();
149
	g_key_file_load_from_file(keyfile,
150
		desktopfile,
151
		G_KEY_FILE_NONE,
152
		NULL); /* No error */
153
154
	if (!g_key_file_has_key(keyfile, DESKTOP_GROUP, SOURCE_FILE_KEY, NULL)) {
195.1.1 by Ted Gould
If we don't have a source label but still have an app id assume that the file is allowed to be further checked for a match
155
		gboolean hasappid = g_key_file_has_key(keyfile, DESKTOP_GROUP, APP_ID_KEY, NULL);
175.5.3 by Ted Gould
Checking the new value we're putting into the desktop file
156
		g_free(desktopfile);
157
		g_key_file_free(keyfile);
195.1.1 by Ted Gould
If we don't have a source label but still have an app id assume that the file is allowed to be further checked for a match
158
		return hasappid;
175.5.3 by Ted Gould
Checking the new value we're putting into the desktop file
159
	}
160
161
	/* At this point we know the key exists, so if we can't find the source
162
	   file we want to delete the file as well. We need to replace it. */
163
	gchar * originalfile = g_key_file_get_string(keyfile, DESKTOP_GROUP, SOURCE_FILE_KEY, NULL);
164
	g_key_file_free(keyfile);
165
	gboolean found = TRUE;
166
167
	if (!g_file_test(originalfile, G_FILE_TEST_EXISTS)) {
168
		g_remove(desktopfile);
169
		found = FALSE;
170
	}
171
172
	g_free(originalfile);
173
	g_free(desktopfile);
174
175
	return found;
175.5.1 by Ted Gould
Insert the new function to check
176
}
177
33.2.3 by Ted Gould
Set up the skeleton for determining state
178
/* Look at an desktop file entry */
179
void
33.2.22 by Ted Gould
Handle the case of creation time changing
180
add_desktop_file (const gchar * dir, const gchar * name, GArray * app_array)
33.2.3 by Ted Gould
Set up the skeleton for determining state
181
{
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
182
	if (!g_str_has_suffix(name, ".desktop")) {
183
		return;
184
	}
185
175.5.1 by Ted Gould
Insert the new function to check
186
	if (!desktop_source_exists(dir, name)) {
187
		return;
188
	}
189
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
190
	gchar * appid = g_strdup(name);
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
191
	g_strstr_len(appid, -1, ".desktop")[0] = '\0';
192
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
193
	/* We only want valid APP IDs as desktop files */
194
	if (!app_id_to_triplet(appid, NULL, NULL, NULL)) {
195
		g_free(appid);
196
		return;
197
	}
198
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
199
	app_state_t * state = find_app_entry(appid, app_array);
200
	state->has_desktop = TRUE;
33.2.39 by Ted Gould
Switching to modification time.
201
	state->desktop_modified = modified_time(dir, name);
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
202
203
	g_free(appid);
33.2.3 by Ted Gould
Set up the skeleton for determining state
204
	return;
205
}
206
207
/* Open a directory and look at all the entries */
208
void
33.2.22 by Ted Gould
Handle the case of creation time changing
209
dir_for_each (const gchar * dirname, void(*func)(const gchar * dir, const gchar * name, GArray * app_array), GArray * app_array)
33.2.3 by Ted Gould
Set up the skeleton for determining state
210
{
33.2.4 by Ted Gould
Going through the directory entries
211
	GError * error = NULL;
212
	GDir * directory = g_dir_open(dirname, 0, &error);
213
214
	if (error != NULL) {
215
		g_warning("Unable to read directory '%s': %s", dirname, error->message);
216
		g_error_free(error);
217
		return;
218
	}
219
220
	const gchar * filename = NULL;
221
	while ((filename = g_dir_read_name(directory)) != NULL) {
33.2.22 by Ted Gould
Handle the case of creation time changing
222
		func(dirname, filename, app_array);
33.2.4 by Ted Gould
Going through the directory entries
223
	}
224
225
	g_dir_close(directory);
33.2.3 by Ted Gould
Set up the skeleton for determining state
226
	return;
227
}
228
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
229
/* Code to report an error, so we can start tracking how important this is */
230
static void
168.1.1 by Ted Gould
Support a field in our recoverable error and simplify the bucket
231
report_recoverable_error (const gchar * app_id, const gchar * iconfield, const gchar * originalicon, const gchar * iconpath)
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
232
{
208.9.1 by Ted Gould
Switching to libwhoopsie for recoverable problem support
233
	const char * properties[9] = {
234
		"IconValue", NULL,
235
		"AppID", NULL,
236
		"IconPath", NULL,
237
		"IconField", NULL,
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
238
		NULL
239
	};
240
208.9.1 by Ted Gould
Switching to libwhoopsie for recoverable problem support
241
	properties[1] = originalicon;
242
	properties[3] = app_id;
243
	properties[5] = iconpath;
244
	properties[7] = iconfield;
245
246
	whoopsie_report_recoverable_problem("icon-path-unhandled", 0, TRUE, properties);
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
247
248
	return;
249
}
250
33.2.10 by Ted Gould
Add in the manifest parsing
251
/* Function to take the source Desktop file and build a new
252
   one with similar, but not the same data in it */
253
static void
33.2.11 by Ted Gould
Parse the incomming desktop file
254
copy_desktop_file (const gchar * from, const gchar * to, const gchar * appdir, const gchar * app_id)
33.2.10 by Ted Gould
Add in the manifest parsing
255
{
33.2.11 by Ted Gould
Parse the incomming desktop file
256
	GError * error = NULL;
257
	GKeyFile * keyfile = g_key_file_new();
258
	g_key_file_load_from_file(keyfile,
259
		from,
260
		G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
261
		&error);
262
263
	if (error != NULL) {
264
		g_warning("Unable to read the desktop file '%s' in the application directory: %s", from, error->message);
265
		g_error_free(error);
266
		g_key_file_unref(keyfile);
267
		return;
268
	}
269
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
270
	/* Path Hanlding */
168.1.5 by Ted Gould
Path, Exec and App ID defines
271
	if (g_key_file_has_key(keyfile, DESKTOP_GROUP, PATH_KEY, NULL)) {
272
		gchar * oldpath = g_key_file_get_string(keyfile, DESKTOP_GROUP, PATH_KEY, NULL);
168.1.6 by Ted Gould
Cleaning up and hadding an old key prefix
273
		g_debug("Desktop file '%s' has a Path set to '%s'.  Setting as " OLD_KEY_PREFIX PATH_KEY ".", from, oldpath);
33.2.11 by Ted Gould
Parse the incomming desktop file
274
168.1.6 by Ted Gould
Cleaning up and hadding an old key prefix
275
		g_key_file_set_string(keyfile, DESKTOP_GROUP, OLD_KEY_PREFIX PATH_KEY, oldpath);
33.2.11 by Ted Gould
Parse the incomming desktop file
276
277
		g_free(oldpath);
278
	}
279
168.1.5 by Ted Gould
Path, Exec and App ID defines
280
	g_key_file_set_string(keyfile, DESKTOP_GROUP, PATH_KEY, appdir);
33.2.11 by Ted Gould
Parse the incomming desktop file
281
51.1.9 by Ted Gould
Typo in comment
282
	/* Icon Handling */
168.1.4 by Ted Gould
Desktop group in define
283
	if (g_key_file_has_key(keyfile, DESKTOP_GROUP, ICON_KEY, NULL)) {
284
		gchar * originalicon = g_key_file_get_string(keyfile, DESKTOP_GROUP, ICON_KEY, NULL);
51.1.3 by Ted Gould
If we can make things better, let's do!
285
		gchar * iconpath = g_build_filename(appdir, originalicon, NULL);
286
287
		/* If the icon in the path exists, let's use that */
288
		if (g_file_test(iconpath, G_FILE_TEST_EXISTS)) {
168.1.4 by Ted Gould
Desktop group in define
289
			g_key_file_set_string(keyfile, DESKTOP_GROUP, ICON_KEY, iconpath);
51.1.3 by Ted Gould
If we can make things better, let's do!
290
			/* Save the old value, because, debugging */
168.1.6 by Ted Gould
Cleaning up and hadding an old key prefix
291
			g_key_file_set_string(keyfile, DESKTOP_GROUP, OLD_KEY_PREFIX ICON_KEY, originalicon);
51.1.3 by Ted Gould
If we can make things better, let's do!
292
		} else {
293
			/* So here we are, realizing all is lost.  Let's file a bug. */
51.1.4 by Ted Gould
Setting up the basis for our recoverable error
294
			/* The goal here is to realize how often this case is, so we know how to prioritize fixing it */
51.1.6 by Ted Gould
Pipe directly
295
168.1.3 by Ted Gould
Make desktop keys into defines
296
			report_recoverable_error(app_id, ICON_KEY, originalicon, iconpath);
51.1.3 by Ted Gould
If we can make things better, let's do!
297
		}
298
299
		g_free(iconpath);
300
		g_free(originalicon);
301
	}
302
168.1.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
303
	/* SymbolicIcon Handling */
168.1.4 by Ted Gould
Desktop group in define
304
	if (g_key_file_has_key(keyfile, DESKTOP_GROUP, SYMBOLIC_ICON_KEY, NULL)) {
305
		gchar * originalicon = g_key_file_get_string(keyfile, DESKTOP_GROUP, SYMBOLIC_ICON_KEY, NULL);
168.1.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
306
		gchar * iconpath = g_build_filename(appdir, originalicon, NULL);
307
308
		/* If the icon in the path exists, let's use that */
309
		if (g_file_test(iconpath, G_FILE_TEST_EXISTS)) {
168.1.4 by Ted Gould
Desktop group in define
310
			g_key_file_set_string(keyfile, DESKTOP_GROUP, SYMBOLIC_ICON_KEY, iconpath);
168.1.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
311
			/* Save the old value, because, debugging */
168.1.6 by Ted Gould
Cleaning up and hadding an old key prefix
312
			g_key_file_set_string(keyfile, DESKTOP_GROUP, OLD_KEY_PREFIX SYMBOLIC_ICON_KEY, originalicon);
168.1.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
313
		} else {
314
			/* So here we are, realizing all is lost.  Let's file a bug. */
315
			/* The goal here is to realize how often this case is, so we know how to prioritize fixing it */
316
168.1.3 by Ted Gould
Make desktop keys into defines
317
			report_recoverable_error(app_id, SYMBOLIC_ICON_KEY, originalicon, iconpath);
168.1.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
318
		}
319
320
		g_free(iconpath);
321
		g_free(originalicon);
322
	}
323
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
324
	/* Exec Handling */
325
	gchar * oldexec = desktop_to_exec(keyfile, from);
326
	if (oldexec == NULL) {
327
		g_key_file_unref(keyfile);
328
		return;
329
	}
330
46.1.1 by Jamie Strandboge
application-legacy.conf.in: use aa-exec-click instead of aa-exec
331
	gchar * newexec = g_strdup_printf("aa-exec-click -p %s -- %s", app_id, oldexec);
168.1.5 by Ted Gould
Path, Exec and App ID defines
332
	g_key_file_set_string(keyfile, DESKTOP_GROUP, EXEC_KEY, newexec);
33.2.11 by Ted Gould
Parse the incomming desktop file
333
	g_free(newexec);
334
	g_free(oldexec);
335
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
336
	/* Adding an Application ID */
168.1.5 by Ted Gould
Path, Exec and App ID defines
337
	g_key_file_set_string(keyfile, DESKTOP_GROUP, APP_ID_KEY, app_id);
33.2.33 by Ted Gould
Adding the application ID, perhaps for later
338
175.5.2 by Ted Gould
Putting the built key into the cache desktop file
339
	/* Adding the source file path */
340
	g_key_file_set_string(keyfile, DESKTOP_GROUP, SOURCE_FILE_KEY, from);
341
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
342
	/* Output */
33.2.12 by Ted Gould
Write out the final built desktop file
343
	gsize datalen = 0;
344
	gchar * data = g_key_file_to_data(keyfile, &datalen, &error);
345
	g_key_file_unref(keyfile);
346
347
	if (error != NULL) {
348
		g_warning("Unable serialize keyfile built from '%s': %s", from, error->message);
349
		g_error_free(error);
350
		return;
351
	}
352
353
	g_file_set_contents(to, data, datalen, &error);
354
	g_free(data);
355
356
	if (error != NULL) {
357
		g_warning("Unable to write out desktop file to '%s': %s", to, error->message);
358
		g_error_free(error);
359
		return;
360
	}
33.2.10 by Ted Gould
Add in the manifest parsing
361
362
	return;
363
}
364
33.2.7 by Ted Gould
Set up for the handling of each app ID
365
/* Build a desktop file in the user's home directory */
366
static void
367
build_desktop_file (app_state_t * state, const gchar * symlinkdir, const gchar * desktopdir)
368
{
33.2.29 by Ted Gould
Get the directory from click
369
	GError * error = NULL;
370
	gchar * package = NULL;
33.2.9 by Ted Gould
Get all of the file name stuff out of the way and build up to parsing the manifest
371
	/* 'Parse' the App ID */
33.2.29 by Ted Gould
Get the directory from click
372
	if (!app_id_to_triplet(state->app_id, &package, NULL, NULL)) {
373
		return;
374
	}
375
175.5.7 by Ted Gould
Click DB and user database setup and testing
376
	/* Read in the database */
377
	ClickDB * db = click_db_new();
378
	click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
379
	if (error != NULL) {
380
		g_warning("Unable to read Click database: %s", error->message);
381
		g_error_free(error);
382
		g_free(package);
383
		g_object_unref(db);
384
		return;
385
	}
386
33.2.30 by Ted Gould
Make sure to clear the final newline
387
	/* Check click to find out where the files are */
175.5.7 by Ted Gould
Click DB and user database setup and testing
388
	ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
389
	g_object_unref(db);
175.5.19 by Ted Gould
Move unref so that there's only one, error or not.
390
	if (error != NULL) {
391
		g_warning("Unable to read Click database: %s", error->message);
392
		g_error_free(error);
393
		g_free(package);
394
		return;
395
	}
175.5.7 by Ted Gould
Click DB and user database setup and testing
396
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
397
	gchar * pkgdir = click_user_get_path(user, package, &error);
398
	if (error != NULL) {
399
		g_warning("Unable to get the Click package directory for %s: %s", package, error->message);
400
		g_error_free(error);
401
		g_free(package);
402
		return;
403
	}
404
	g_object_unref(user);
33.2.29 by Ted Gould
Get the directory from click
405
	g_free(package);
406
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
407
	if (!g_file_test(pkgdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
408
		g_warning("Directory returned by click '%s' couldn't be found", pkgdir);
409
		g_free(pkgdir);
410
		return;
411
	}
412
413
	gchar * indesktop = manifest_to_desktop(pkgdir, state->app_id);
33.2.17 by Ted Gould
Move the json handling into the helper file
414
	if (indesktop == NULL) {
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
415
		g_free(pkgdir);
33.2.17 by Ted Gould
Move the json handling into the helper file
416
		return;
417
	}
33.2.9 by Ted Gould
Get all of the file name stuff out of the way and build up to parsing the manifest
418
419
	/* Determine the desktop file name */
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
420
	gchar * desktopfile = g_strdup_printf("%s.desktop", state->app_id);
33.2.9 by Ted Gould
Get all of the file name stuff out of the way and build up to parsing the manifest
421
	gchar * desktoppath = g_build_filename(desktopdir, desktopfile, NULL);
422
	g_free(desktopfile);
423
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
424
	copy_desktop_file(indesktop, desktoppath, pkgdir, state->app_id);
33.2.9 by Ted Gould
Get all of the file name stuff out of the way and build up to parsing the manifest
425
426
	g_free(desktoppath);
33.2.17 by Ted Gould
Move the json handling into the helper file
427
	g_free(indesktop);
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
428
	g_free(pkgdir);
33.2.15 by Ted Gould
Move the app id checking to the helpers file
429
33.2.7 by Ted Gould
Set up for the handling of each app ID
430
	return;
431
}
432
433
/* Remove the desktop file from the user's home directory */
33.2.38 by Ted Gould
Checking to see if we did remove the desktop file before trying to build another one.
434
static gboolean
33.2.7 by Ted Gould
Set up for the handling of each app ID
435
remove_desktop_file (app_state_t * state, const gchar * desktopdir)
436
{
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
437
	gchar * desktopfile = g_strdup_printf("%s.desktop", state->app_id);
33.2.8 by Ted Gould
Get the removal code under control
438
	gchar * desktoppath = g_build_filename(desktopdir, desktopfile, NULL);
439
	g_free(desktopfile);
440
33.2.35 by Ted Gould
Checking to make sure the desktop has the application id set before deleting it.
441
	GKeyFile * keyfile = g_key_file_new();
442
	g_key_file_load_from_file(keyfile,
443
		desktoppath,
444
		G_KEY_FILE_NONE,
445
		NULL);
446
168.1.5 by Ted Gould
Path, Exec and App ID defines
447
	if (!g_key_file_has_key(keyfile, DESKTOP_GROUP, APP_ID_KEY, NULL)) {
33.2.35 by Ted Gould
Checking to make sure the desktop has the application id set before deleting it.
448
		g_debug("Desktop file '%s' is not one created by us.", desktoppath);
449
		g_key_file_unref(keyfile);
450
		g_free(desktoppath);
33.2.38 by Ted Gould
Checking to see if we did remove the desktop file before trying to build another one.
451
		return FALSE;
33.2.35 by Ted Gould
Checking to make sure the desktop has the application id set before deleting it.
452
	}
453
	g_key_file_unref(keyfile);
454
33.2.8 by Ted Gould
Get the removal code under control
455
	if (g_unlink(desktoppath) != 0) {
456
		g_warning("Unable to delete desktop file: %s", desktoppath);
457
	}
458
459
	g_free(desktoppath);
33.2.7 by Ted Gould
Set up for the handling of each app ID
460
33.2.38 by Ted Gould
Checking to see if we did remove the desktop file before trying to build another one.
461
	return TRUE;
33.2.7 by Ted Gould
Set up for the handling of each app ID
462
}
463
33.2.4 by Ted Gould
Going through the directory entries
464
/* The main function */
33.2.2 by Ted Gould
Adding a desktop hook binary
465
int
466
main (int argc, char * argv[])
467
{
33.2.3 by Ted Gould
Set up the skeleton for determining state
468
	if (argc != 1) {
469
		g_error("Shouldn't have arguments");
470
		return 1;
471
	}
472
473
	GArray * apparray = g_array_new(FALSE, FALSE, sizeof(app_state_t));
474
33.2.27 by Ted Gould
Change the name of the symlink
475
	/* Find all the symlinks of desktop files */
152.1.6 by Ted Gould
Removing more instances if 'upstart' to not confuse people, leaving the tracepoints for now
476
	gchar * symlinkdir = g_build_filename(g_get_user_cache_dir(), "ubuntu-app-launch", "desktop", NULL);
33.2.3 by Ted Gould
Set up the skeleton for determining state
477
	if (!g_file_test(symlinkdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
144.1.1 by Ted Gould
Debug message for valid, though rare, conditions.
478
		g_debug("No installed click packages");
33.2.3 by Ted Gould
Set up the skeleton for determining state
479
	} else {
480
		dir_for_each(symlinkdir, add_click_package, apparray);
481
	}
482
33.2.7 by Ted Gould
Set up for the handling of each app ID
483
	/* Find all the click desktop files */
33.2.3 by Ted Gould
Set up the skeleton for determining state
484
	gchar * desktopdir = g_build_filename(g_get_user_data_dir(), "applications", NULL);
33.2.23 by Ted Gould
Handle making sure the application dir exists
485
	gboolean desktopdirexists = FALSE;
44.2.1 by Ted Gould
Making sure to look for the application directory
486
	if (!g_file_test(desktopdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
144.1.1 by Ted Gould
Debug message for valid, though rare, conditions.
487
		g_debug("No applications defined");
33.2.3 by Ted Gould
Set up the skeleton for determining state
488
	} else {
489
		dir_for_each(desktopdir, add_desktop_file, apparray);
33.2.23 by Ted Gould
Handle making sure the application dir exists
490
		desktopdirexists = TRUE;
33.2.3 by Ted Gould
Set up the skeleton for determining state
491
	}
33.2.2 by Ted Gould
Adding a desktop hook binary
492
33.2.7 by Ted Gould
Set up for the handling of each app ID
493
	/* Process the merge */
494
	int i;
495
	for (i = 0; i < apparray->len; i++) {
496
		app_state_t * state = &g_array_index(apparray, app_state_t, i);
497
		g_debug("Processing App ID: %s", state->app_id);
498
499
		if (state->has_click && state->has_desktop) {
33.2.39 by Ted Gould
Switching to modification time.
500
			if (state->click_modified > state->desktop_modified) {
33.2.22 by Ted Gould
Handle the case of creation time changing
501
				g_debug("\tClick updated more recently");
502
				g_debug("\tRemoving desktop file");
33.2.38 by Ted Gould
Checking to see if we did remove the desktop file before trying to build another one.
503
				if (remove_desktop_file(state, desktopdir)) {
504
					g_debug("\tBuilding desktop file");
505
					build_desktop_file(state, symlinkdir, desktopdir);
506
				}
33.2.22 by Ted Gould
Handle the case of creation time changing
507
			} else {
508
				g_debug("\tAlready synchronized");
509
			}
33.2.7 by Ted Gould
Set up for the handling of each app ID
510
		} else if (state->has_click) {
33.2.23 by Ted Gould
Handle making sure the application dir exists
511
			if (!desktopdirexists) {
33.2.24 by Ted Gould
Bad permissions for directories
512
				if (g_mkdir_with_parents(desktopdir, 0755) == 0) {
33.2.23 by Ted Gould
Handle making sure the application dir exists
513
					g_debug("\tCreated applications directory");
514
					desktopdirexists = TRUE;
515
				} else {
516
					g_warning("\tUnable to create applications directory");
517
				}
518
			}
519
			if (desktopdirexists) {
520
				g_debug("\tBuilding desktop file");
521
				build_desktop_file(state, symlinkdir, desktopdir);
522
			}
33.2.7 by Ted Gould
Set up for the handling of each app ID
523
		} else if (state->has_desktop) {
524
			g_debug("\tRemoving desktop file");
525
			remove_desktop_file(state, desktopdir);
526
		}
527
528
		g_free(state->app_id);
529
	}
530
531
	g_array_free(apparray, TRUE);
532
	g_free(desktopdir);
533
	g_free(symlinkdir);
33.2.2 by Ted Gould
Adding a desktop hook binary
534
535
	return 0;
536
}