~larryprice/ubuntu-app-launch/humanity-theme-icons

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
144.7.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
144.7.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
144.7.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>
33.2.3 by Ted Gould
Set up the skeleton for determining state
50
33.2.15 by Ted Gould
Move the app id checking to the helpers file
51
#include "helpers.h"
52
33.2.3 by Ted Gould
Set up the skeleton for determining state
53
typedef struct _app_state_t app_state_t;
54
struct _app_state_t {
55
	gchar * app_id;
56
	gboolean has_click;
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
57
	gboolean has_desktop;
33.2.39 by Ted Gould
Switching to modification time.
58
	guint64 click_modified;
59
	guint64 desktop_modified;
33.2.3 by Ted Gould
Set up the skeleton for determining state
60
};
61
144.19.6 by Ted Gould
Cleaning up and hadding an old key prefix
62
/* Desktop Group */
63
#define DESKTOP_GROUP      "Desktop Entry"
64
/* Desktop Keys */
65
#define APP_ID_KEY         "X-Ubuntu-Application-ID"
66
#define PATH_KEY           "Path"
67
#define EXEC_KEY           "Exec"
68
#define ICON_KEY           "Icon"
69
#define SYMBOLIC_ICON_KEY  "X-Ubuntu-SymbolicIcon"
144.31.2 by Ted Gould
Putting the built key into the cache desktop file
70
#define SOURCE_FILE_KEY    "X-Ubuntu-UAL-Source-Desktop"
144.19.6 by Ted Gould
Cleaning up and hadding an old key prefix
71
/* Other */
72
#define OLD_KEY_PREFIX     "X-Ubuntu-Old-"
144.19.3 by Ted Gould
Make desktop keys into defines
73
33.2.5 by Ted Gould
Fleshing out adding a click package directory
74
/* Find an entry in the app array */
75
app_state_t *
76
find_app_entry (const gchar * name, GArray * app_array)
77
{
78
	int i;
79
	for (i = 0; i < app_array->len; i++) {
80
		app_state_t * state = &g_array_index(app_array, app_state_t, i);
81
82
		if (g_strcmp0(state->app_id, name) == 0) {
83
			return state;
84
		}
85
	}
86
87
	app_state_t newstate;
88
	newstate.has_click = FALSE;
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
89
	newstate.has_desktop = FALSE;
33.2.39 by Ted Gould
Switching to modification time.
90
	newstate.click_modified = 0;
91
	newstate.desktop_modified = 0;
33.2.5 by Ted Gould
Fleshing out adding a click package directory
92
	newstate.app_id = g_strdup(name);
93
94
	g_array_append_val(app_array, newstate);
95
96
	/* Note: The pointer needs to be the entry in the array, not the
97
	   one that we have on the stack.  Criticaly important. */
98
	app_state_t * statepntr = &g_array_index(app_array, app_state_t, app_array->len - 1);
99
	return statepntr;
100
}
101
33.2.22 by Ted Gould
Handle the case of creation time changing
102
/* Looks up the file creation time, which seems harder with GLib
103
   than it should be */
104
guint64
33.2.39 by Ted Gould
Switching to modification time.
105
modified_time (const gchar * dir, const gchar * filename)
33.2.22 by Ted Gould
Handle the case of creation time changing
106
{
107
	gchar * path = g_build_filename(dir, filename, NULL);
108
	GFile * file = g_file_new_for_path(path);
33.2.39 by Ted Gould
Switching to modification time.
109
	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
110
33.2.39 by Ted Gould
Switching to modification time.
111
	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
112
113
	g_object_unref(info);
114
	g_object_unref(file);
115
	g_free(path);
116
117
	return time;
118
}
119
33.2.3 by Ted Gould
Set up the skeleton for determining state
120
/* Look at an click package entry */
121
void
33.2.22 by Ted Gould
Handle the case of creation time changing
122
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
123
{
33.2.27 by Ted Gould
Change the name of the symlink
124
	if (!g_str_has_suffix(name, ".desktop")) {
125
		return;
126
	}
127
128
	gchar * appid = g_strdup(name);
129
	g_strstr_len(appid, -1, ".desktop")[0] = '\0';
130
131
	app_state_t * state = find_app_entry(appid, app_array);
33.2.5 by Ted Gould
Fleshing out adding a click package directory
132
	state->has_click = TRUE;
33.2.39 by Ted Gould
Switching to modification time.
133
	state->click_modified = modified_time(dir, name);
33.2.3 by Ted Gould
Set up the skeleton for determining state
134
33.2.27 by Ted Gould
Change the name of the symlink
135
	g_free(appid);
136
33.2.3 by Ted Gould
Set up the skeleton for determining state
137
	return;
138
}
139
144.31.1 by Ted Gould
Insert the new function to check
140
/* Look at the desktop file and ensure that it was built by us, and if it
144.31.18 by Ted Gould
Fix typo
141
   was that its source still exists */
144.31.1 by Ted Gould
Insert the new function to check
142
gboolean
143
desktop_source_exists (const gchar * dir, const gchar * name)
144
{
144.31.3 by Ted Gould
Checking the new value we're putting into the desktop file
145
	gchar * desktopfile = g_build_filename(dir, name, NULL);
146
147
	GKeyFile * keyfile = g_key_file_new();
148
	g_key_file_load_from_file(keyfile,
149
		desktopfile,
150
		G_KEY_FILE_NONE,
151
		NULL); /* No error */
152
153
	if (!g_key_file_has_key(keyfile, DESKTOP_GROUP, SOURCE_FILE_KEY, NULL)) {
144.33.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
154
		gboolean hasappid = g_key_file_has_key(keyfile, DESKTOP_GROUP, APP_ID_KEY, NULL);
144.31.3 by Ted Gould
Checking the new value we're putting into the desktop file
155
		g_free(desktopfile);
156
		g_key_file_free(keyfile);
144.33.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
157
		return hasappid;
144.31.3 by Ted Gould
Checking the new value we're putting into the desktop file
158
	}
159
160
	/* At this point we know the key exists, so if we can't find the source
161
	   file we want to delete the file as well. We need to replace it. */
162
	gchar * originalfile = g_key_file_get_string(keyfile, DESKTOP_GROUP, SOURCE_FILE_KEY, NULL);
163
	g_key_file_free(keyfile);
164
	gboolean found = TRUE;
165
166
	if (!g_file_test(originalfile, G_FILE_TEST_EXISTS)) {
167
		g_remove(desktopfile);
168
		found = FALSE;
169
	}
170
171
	g_free(originalfile);
172
	g_free(desktopfile);
173
174
	return found;
144.31.1 by Ted Gould
Insert the new function to check
175
}
176
33.2.3 by Ted Gould
Set up the skeleton for determining state
177
/* Look at an desktop file entry */
178
void
33.2.22 by Ted Gould
Handle the case of creation time changing
179
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
180
{
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
181
	if (!g_str_has_suffix(name, ".desktop")) {
182
		return;
183
	}
184
144.31.1 by Ted Gould
Insert the new function to check
185
	if (!desktop_source_exists(dir, name)) {
186
		return;
187
	}
188
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
189
	gchar * appid = g_strdup(name);
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
190
	g_strstr_len(appid, -1, ".desktop")[0] = '\0';
191
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
192
	/* We only want valid APP IDs as desktop files */
193
	if (!app_id_to_triplet(appid, NULL, NULL, NULL)) {
194
		g_free(appid);
195
		return;
196
	}
197
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
198
	app_state_t * state = find_app_entry(appid, app_array);
199
	state->has_desktop = TRUE;
33.2.39 by Ted Gould
Switching to modification time.
200
	state->desktop_modified = modified_time(dir, name);
33.2.6 by Ted Gould
Fleshing out listing all the desktop files
201
202
	g_free(appid);
33.2.3 by Ted Gould
Set up the skeleton for determining state
203
	return;
204
}
205
206
/* Open a directory and look at all the entries */
207
void
33.2.22 by Ted Gould
Handle the case of creation time changing
208
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
209
{
33.2.4 by Ted Gould
Going through the directory entries
210
	GError * error = NULL;
211
	GDir * directory = g_dir_open(dirname, 0, &error);
212
213
	if (error != NULL) {
214
		g_warning("Unable to read directory '%s': %s", dirname, error->message);
215
		g_error_free(error);
216
		return;
217
	}
218
219
	const gchar * filename = NULL;
220
	while ((filename = g_dir_read_name(directory)) != NULL) {
33.2.22 by Ted Gould
Handle the case of creation time changing
221
		func(dirname, filename, app_array);
33.2.4 by Ted Gould
Going through the directory entries
222
	}
223
224
	g_dir_close(directory);
33.2.3 by Ted Gould
Set up the skeleton for determining state
225
	return;
226
}
227
51.1.6 by Ted Gould
Pipe directly
228
/* Helpers to ensure we write nicely */
229
static void 
230
write_string (int          fd,
231
              const gchar *string)
232
{
233
	int res; 
234
	do
235
		res = write (fd, string, strlen (string));
236
	while (G_UNLIKELY (res == -1 && errno == EINTR));
237
}
238
239
/* Make NULLs fast and fun! */
240
static void 
241
write_null (int fd)
242
{
243
	int res; 
244
	do
245
		res = write (fd, "", 1);
246
	while (G_UNLIKELY (res == -1 && errno == EINTR));
247
}
248
51.1.8 by Ted Gould
Make sure we wait to report the error
249
/* Child watcher */
250
static gboolean
251
apport_child_watch (GPid pid, gint status, gpointer user_data)
252
{
253
	g_main_loop_quit((GMainLoop *)user_data);
254
	return FALSE;
255
}
256
257
static gboolean
258
apport_child_timeout (gpointer user_data)
259
{
260
	g_warning("Recoverable Error Reporter Timeout");
261
	g_main_loop_quit((GMainLoop *)user_data);
262
	return FALSE;
263
}
264
265
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
266
/* Code to report an error, so we can start tracking how important this is */
267
static void
144.19.1 by Ted Gould
Support a field in our recoverable error and simplify the bucket
268
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
269
{
270
	GError * error = NULL;
271
	gint error_stdin = 0;
51.1.8 by Ted Gould
Make sure we wait to report the error
272
	GPid pid = 0;
273
	gchar * argv[2] = {
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
274
		"/usr/share/apport/recoverable_problem",
275
		NULL
276
	};
277
278
	g_spawn_async_with_pipes(NULL, /* cwd */
279
		argv,
280
		NULL, /* envp */
51.1.8 by Ted Gould
Make sure we wait to report the error
281
		G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
282
		NULL, NULL, /* child setup func */
51.1.8 by Ted Gould
Make sure we wait to report the error
283
		&pid,
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
284
		&error_stdin,
285
		NULL, /* stdout */
286
		NULL, /* stderr */
287
		&error);
288
289
	if (error != NULL) {
290
		g_warning("Unable to report a recoverable error: %s", error->message);
291
		g_error_free(error);
292
	}
293
294
	if (error_stdin != 0) {
295
		write_string(error_stdin, "IconValue");
296
		write_null(error_stdin);
297
		write_string(error_stdin, originalicon);
298
		write_null(error_stdin);
299
300
		write_string(error_stdin, "AppID");
301
		write_null(error_stdin);
302
		write_string(error_stdin, app_id);
303
		write_null(error_stdin);
304
305
		write_string(error_stdin, "IconPath");
306
		write_null(error_stdin);
307
		write_string(error_stdin, iconpath);
308
		write_null(error_stdin);
309
144.19.1 by Ted Gould
Support a field in our recoverable error and simplify the bucket
310
		write_string(error_stdin, "IconField");
311
		write_null(error_stdin);
312
		write_string(error_stdin, iconfield);
313
		write_null(error_stdin);
314
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
315
		write_string(error_stdin, "DuplicateSignature");
316
		write_null(error_stdin);
144.19.1 by Ted Gould
Support a field in our recoverable error and simplify the bucket
317
		write_string(error_stdin, "icon-path-unhandled");
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
318
		/* write_null(error_stdin); -- No final NULL */
319
320
		close(error_stdin);
321
	}
322
51.1.8 by Ted Gould
Make sure we wait to report the error
323
	if (pid != 0) {
324
		GSource * child_source, * timeout_source;
325
		GMainContext * context = g_main_context_new();
326
		GMainLoop * loop = g_main_loop_new(context, FALSE);
327
328
		child_source = g_child_watch_source_new(pid);
329
		g_source_attach(child_source, context);
330
		g_source_set_callback(child_source, (GSourceFunc)apport_child_watch, loop, NULL);
331
332
		timeout_source = g_timeout_source_new_seconds(5);
333
		g_source_attach(timeout_source, context);
334
		g_source_set_callback(timeout_source, apport_child_timeout, loop, NULL);
335
336
		g_main_loop_run(loop);
337
338
		g_source_destroy(timeout_source);
339
		g_source_destroy(child_source);
340
		g_main_loop_unref(loop);
341
		g_main_context_unref(context);
342
343
		g_spawn_close_pid(pid);
344
	}
51.1.7 by Ted Gould
Pull the recoverable error stuff into it's own function
345
346
	return;
347
}
348
33.2.10 by Ted Gould
Add in the manifest parsing
349
/* Function to take the source Desktop file and build a new
350
   one with similar, but not the same data in it */
351
static void
33.2.11 by Ted Gould
Parse the incomming desktop file
352
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
353
{
33.2.11 by Ted Gould
Parse the incomming desktop file
354
	GError * error = NULL;
355
	GKeyFile * keyfile = g_key_file_new();
356
	g_key_file_load_from_file(keyfile,
357
		from,
358
		G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
359
		&error);
360
361
	if (error != NULL) {
362
		g_warning("Unable to read the desktop file '%s' in the application directory: %s", from, error->message);
363
		g_error_free(error);
364
		g_key_file_unref(keyfile);
365
		return;
366
	}
367
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
368
	/* Path Hanlding */
144.19.5 by Ted Gould
Path, Exec and App ID defines
369
	if (g_key_file_has_key(keyfile, DESKTOP_GROUP, PATH_KEY, NULL)) {
370
		gchar * oldpath = g_key_file_get_string(keyfile, DESKTOP_GROUP, PATH_KEY, NULL);
144.19.6 by Ted Gould
Cleaning up and hadding an old key prefix
371
		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
372
144.19.6 by Ted Gould
Cleaning up and hadding an old key prefix
373
		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
374
375
		g_free(oldpath);
376
	}
377
144.19.5 by Ted Gould
Path, Exec and App ID defines
378
	g_key_file_set_string(keyfile, DESKTOP_GROUP, PATH_KEY, appdir);
33.2.11 by Ted Gould
Parse the incomming desktop file
379
51.1.9 by Ted Gould
Typo in comment
380
	/* Icon Handling */
144.19.4 by Ted Gould
Desktop group in define
381
	if (g_key_file_has_key(keyfile, DESKTOP_GROUP, ICON_KEY, NULL)) {
382
		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!
383
		gchar * iconpath = g_build_filename(appdir, originalicon, NULL);
384
385
		/* If the icon in the path exists, let's use that */
386
		if (g_file_test(iconpath, G_FILE_TEST_EXISTS)) {
144.19.4 by Ted Gould
Desktop group in define
387
			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!
388
			/* Save the old value, because, debugging */
144.19.6 by Ted Gould
Cleaning up and hadding an old key prefix
389
			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!
390
		} else {
391
			/* 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
392
			/* 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
393
144.19.3 by Ted Gould
Make desktop keys into defines
394
			report_recoverable_error(app_id, ICON_KEY, originalicon, iconpath);
51.1.3 by Ted Gould
If we can make things better, let's do!
395
		}
396
397
		g_free(iconpath);
398
		g_free(originalicon);
399
	}
400
144.19.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
401
	/* SymbolicIcon Handling */
144.19.4 by Ted Gould
Desktop group in define
402
	if (g_key_file_has_key(keyfile, DESKTOP_GROUP, SYMBOLIC_ICON_KEY, NULL)) {
403
		gchar * originalicon = g_key_file_get_string(keyfile, DESKTOP_GROUP, SYMBOLIC_ICON_KEY, NULL);
144.19.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
404
		gchar * iconpath = g_build_filename(appdir, originalicon, NULL);
405
406
		/* If the icon in the path exists, let's use that */
407
		if (g_file_test(iconpath, G_FILE_TEST_EXISTS)) {
144.19.4 by Ted Gould
Desktop group in define
408
			g_key_file_set_string(keyfile, DESKTOP_GROUP, SYMBOLIC_ICON_KEY, iconpath);
144.19.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
409
			/* Save the old value, because, debugging */
144.19.6 by Ted Gould
Cleaning up and hadding an old key prefix
410
			g_key_file_set_string(keyfile, DESKTOP_GROUP, OLD_KEY_PREFIX SYMBOLIC_ICON_KEY, originalicon);
144.19.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
411
		} else {
412
			/* So here we are, realizing all is lost.  Let's file a bug. */
413
			/* The goal here is to realize how often this case is, so we know how to prioritize fixing it */
414
144.19.3 by Ted Gould
Make desktop keys into defines
415
			report_recoverable_error(app_id, SYMBOLIC_ICON_KEY, originalicon, iconpath);
144.19.2 by Ted Gould
Add handling for our symbolic icon just like our icon handling
416
		}
417
418
		g_free(iconpath);
419
		g_free(originalicon);
420
	}
421
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
422
	/* Exec Handling */
423
	gchar * oldexec = desktop_to_exec(keyfile, from);
424
	if (oldexec == NULL) {
425
		g_key_file_unref(keyfile);
426
		return;
427
	}
428
46.1.1 by Jamie Strandboge
application-legacy.conf.in: use aa-exec-click instead of aa-exec
429
	gchar * newexec = g_strdup_printf("aa-exec-click -p %s -- %s", app_id, oldexec);
144.19.5 by Ted Gould
Path, Exec and App ID defines
430
	g_key_file_set_string(keyfile, DESKTOP_GROUP, EXEC_KEY, newexec);
33.2.11 by Ted Gould
Parse the incomming desktop file
431
	g_free(newexec);
432
	g_free(oldexec);
433
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
434
	/* Adding an Application ID */
144.19.5 by Ted Gould
Path, Exec and App ID defines
435
	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
436
144.31.2 by Ted Gould
Putting the built key into the cache desktop file
437
	/* Adding the source file path */
438
	g_key_file_set_string(keyfile, DESKTOP_GROUP, SOURCE_FILE_KEY, from);
439
51.1.1 by Ted Gould
Add some comments and make the code flow more clear
440
	/* Output */
33.2.12 by Ted Gould
Write out the final built desktop file
441
	gsize datalen = 0;
442
	gchar * data = g_key_file_to_data(keyfile, &datalen, &error);
443
	g_key_file_unref(keyfile);
444
445
	if (error != NULL) {
446
		g_warning("Unable serialize keyfile built from '%s': %s", from, error->message);
447
		g_error_free(error);
448
		return;
449
	}
450
451
	g_file_set_contents(to, data, datalen, &error);
452
	g_free(data);
453
454
	if (error != NULL) {
455
		g_warning("Unable to write out desktop file to '%s': %s", to, error->message);
456
		g_error_free(error);
457
		return;
458
	}
33.2.10 by Ted Gould
Add in the manifest parsing
459
460
	return;
461
}
462
33.2.7 by Ted Gould
Set up for the handling of each app ID
463
/* Build a desktop file in the user's home directory */
464
static void
465
build_desktop_file (app_state_t * state, const gchar * symlinkdir, const gchar * desktopdir)
466
{
33.2.29 by Ted Gould
Get the directory from click
467
	GError * error = NULL;
468
	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
469
	/* 'Parse' the App ID */
33.2.29 by Ted Gould
Get the directory from click
470
	if (!app_id_to_triplet(state->app_id, &package, NULL, NULL)) {
471
		return;
472
	}
473
144.31.7 by Ted Gould
Click DB and user database setup and testing
474
	/* Read in the database */
475
	ClickDB * db = click_db_new();
476
	click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
477
	if (error != NULL) {
478
		g_warning("Unable to read Click database: %s", error->message);
479
		g_error_free(error);
480
		g_free(package);
481
		g_object_unref(db);
482
		return;
483
	}
484
33.2.30 by Ted Gould
Make sure to clear the final newline
485
	/* Check click to find out where the files are */
144.31.7 by Ted Gould
Click DB and user database setup and testing
486
	ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
487
	g_object_unref(db);
144.31.19 by Ted Gould
Move unref so that there's only one, error or not.
488
	if (error != NULL) {
489
		g_warning("Unable to read Click database: %s", error->message);
490
		g_error_free(error);
491
		g_free(package);
492
		return;
493
	}
144.31.7 by Ted Gould
Click DB and user database setup and testing
494
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
495
	gchar * pkgdir = click_user_get_path(user, package, &error);
496
	if (error != NULL) {
497
		g_warning("Unable to get the Click package directory for %s: %s", package, error->message);
498
		g_error_free(error);
499
		g_free(package);
500
		return;
501
	}
502
	g_object_unref(user);
33.2.29 by Ted Gould
Get the directory from click
503
	g_free(package);
504
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
505
	if (!g_file_test(pkgdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
506
		g_warning("Directory returned by click '%s' couldn't be found", pkgdir);
507
		g_free(pkgdir);
508
		return;
509
	}
510
511
	gchar * indesktop = manifest_to_desktop(pkgdir, state->app_id);
33.2.17 by Ted Gould
Move the json handling into the helper file
512
	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.
513
		g_free(pkgdir);
33.2.17 by Ted Gould
Move the json handling into the helper file
514
		return;
515
	}
33.2.9 by Ted Gould
Get all of the file name stuff out of the way and build up to parsing the manifest
516
517
	/* Determine the desktop file name */
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
518
	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
519
	gchar * desktoppath = g_build_filename(desktopdir, desktopfile, NULL);
520
	g_free(desktopfile);
521
135.2.2 by Colin Watson
Use libclick to get the package directory, saving about 0.7 seconds from Click application startup.
522
	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
523
524
	g_free(desktoppath);
33.2.17 by Ted Gould
Move the json handling into the helper file
525
	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.
526
	g_free(pkgdir);
33.2.15 by Ted Gould
Move the app id checking to the helpers file
527
33.2.7 by Ted Gould
Set up for the handling of each app ID
528
	return;
529
}
530
531
/* 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.
532
static gboolean
33.2.7 by Ted Gould
Set up for the handling of each app ID
533
remove_desktop_file (app_state_t * state, const gchar * desktopdir)
534
{
33.2.34 by Ted Gould
Instead of prefixing the files check to seeif the appids are valid
535
	gchar * desktopfile = g_strdup_printf("%s.desktop", state->app_id);
33.2.8 by Ted Gould
Get the removal code under control
536
	gchar * desktoppath = g_build_filename(desktopdir, desktopfile, NULL);
537
	g_free(desktopfile);
538
33.2.35 by Ted Gould
Checking to make sure the desktop has the application id set before deleting it.
539
	GKeyFile * keyfile = g_key_file_new();
540
	g_key_file_load_from_file(keyfile,
541
		desktoppath,
542
		G_KEY_FILE_NONE,
543
		NULL);
544
144.19.5 by Ted Gould
Path, Exec and App ID defines
545
	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.
546
		g_debug("Desktop file '%s' is not one created by us.", desktoppath);
547
		g_key_file_unref(keyfile);
548
		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.
549
		return FALSE;
33.2.35 by Ted Gould
Checking to make sure the desktop has the application id set before deleting it.
550
	}
551
	g_key_file_unref(keyfile);
552
33.2.8 by Ted Gould
Get the removal code under control
553
	if (g_unlink(desktoppath) != 0) {
554
		g_warning("Unable to delete desktop file: %s", desktoppath);
555
	}
556
557
	g_free(desktoppath);
33.2.7 by Ted Gould
Set up for the handling of each app ID
558
33.2.38 by Ted Gould
Checking to see if we did remove the desktop file before trying to build another one.
559
	return TRUE;
33.2.7 by Ted Gould
Set up for the handling of each app ID
560
}
561
33.2.4 by Ted Gould
Going through the directory entries
562
/* The main function */
33.2.2 by Ted Gould
Adding a desktop hook binary
563
int
564
main (int argc, char * argv[])
565
{
33.2.3 by Ted Gould
Set up the skeleton for determining state
566
	if (argc != 1) {
567
		g_error("Shouldn't have arguments");
568
		return 1;
569
	}
570
571
	GArray * apparray = g_array_new(FALSE, FALSE, sizeof(app_state_t));
572
33.2.27 by Ted Gould
Change the name of the symlink
573
	/* Find all the symlinks of desktop files */
144.7.6 by Ted Gould
Removing more instances if 'upstart' to not confuse people, leaving the tracepoints for now
574
	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
575
	if (!g_file_test(symlinkdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
144.2.1 by Ted Gould
Debug message for valid, though rare, conditions.
576
		g_debug("No installed click packages");
33.2.3 by Ted Gould
Set up the skeleton for determining state
577
	} else {
578
		dir_for_each(symlinkdir, add_click_package, apparray);
579
	}
580
33.2.7 by Ted Gould
Set up for the handling of each app ID
581
	/* Find all the click desktop files */
33.2.3 by Ted Gould
Set up the skeleton for determining state
582
	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
583
	gboolean desktopdirexists = FALSE;
44.2.1 by Ted Gould
Making sure to look for the application directory
584
	if (!g_file_test(desktopdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
144.2.1 by Ted Gould
Debug message for valid, though rare, conditions.
585
		g_debug("No applications defined");
33.2.3 by Ted Gould
Set up the skeleton for determining state
586
	} else {
587
		dir_for_each(desktopdir, add_desktop_file, apparray);
33.2.23 by Ted Gould
Handle making sure the application dir exists
588
		desktopdirexists = TRUE;
33.2.3 by Ted Gould
Set up the skeleton for determining state
589
	}
33.2.2 by Ted Gould
Adding a desktop hook binary
590
33.2.7 by Ted Gould
Set up for the handling of each app ID
591
	/* Process the merge */
592
	int i;
593
	for (i = 0; i < apparray->len; i++) {
594
		app_state_t * state = &g_array_index(apparray, app_state_t, i);
595
		g_debug("Processing App ID: %s", state->app_id);
596
597
		if (state->has_click && state->has_desktop) {
33.2.39 by Ted Gould
Switching to modification time.
598
			if (state->click_modified > state->desktop_modified) {
33.2.22 by Ted Gould
Handle the case of creation time changing
599
				g_debug("\tClick updated more recently");
600
				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.
601
				if (remove_desktop_file(state, desktopdir)) {
602
					g_debug("\tBuilding desktop file");
603
					build_desktop_file(state, symlinkdir, desktopdir);
604
				}
33.2.22 by Ted Gould
Handle the case of creation time changing
605
			} else {
606
				g_debug("\tAlready synchronized");
607
			}
33.2.7 by Ted Gould
Set up for the handling of each app ID
608
		} else if (state->has_click) {
33.2.23 by Ted Gould
Handle making sure the application dir exists
609
			if (!desktopdirexists) {
33.2.24 by Ted Gould
Bad permissions for directories
610
				if (g_mkdir_with_parents(desktopdir, 0755) == 0) {
33.2.23 by Ted Gould
Handle making sure the application dir exists
611
					g_debug("\tCreated applications directory");
612
					desktopdirexists = TRUE;
613
				} else {
614
					g_warning("\tUnable to create applications directory");
615
				}
616
			}
617
			if (desktopdirexists) {
618
				g_debug("\tBuilding desktop file");
619
				build_desktop_file(state, symlinkdir, desktopdir);
620
			}
33.2.7 by Ted Gould
Set up for the handling of each app ID
621
		} else if (state->has_desktop) {
622
			g_debug("\tRemoving desktop file");
623
			remove_desktop_file(state, desktopdir);
624
		}
625
626
		g_free(state->app_id);
627
	}
628
629
	g_array_free(apparray, TRUE);
630
	g_free(desktopdir);
631
	g_free(symlinkdir);
33.2.2 by Ted Gould
Adding a desktop hook binary
632
633
	return 0;
634
}