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 |
}
|