Fix typo (thanks lintian.d.o!)
[claws.git] / src / plugins / notification / gtkhotkey / gtk-hotkey-key-file-registry.c
1 /*
2  * This file is part of GtkHotkey.
3  * Copyright Mikkel Kamstrup Erlandsen, March, 2008
4  *
5  *   GtkHotkey is free software: you can redistribute it and/or modify
6  *   it under the terms of the GNU Lesser General Public License as published by
7  *   the Free Software Foundation, either version 3 of the License, or
8  *   (at your option) any later version.
9  *
10  *   GtkHotkey is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU Lesser General Public License for more details.
14  *
15  *   You should have received a copy of the GNU Lesser General Public License
16  *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
17  */
18  
19 #include "gtk-hotkey-key-file-registry.h"
20 #include "gtk-hotkey-utils.h"
21
22 #include <gio/gio.h>
23 #include <gio/gdesktopappinfo.h>
24
25 enum  {
26         GTK_HOTKEY_KEY_FILE_REGISTRY_DUMMY_PROPERTY
27 };
28
29 /* Beware - the lengths of these strings are hardcoded throughout
30  * this file (sorry) */
31 #define HOTKEY_HOME "~/.config/hotkeys"
32 #define HOTKEY_FILE_EXT ".hotkeys"
33 #define HOTKEY_GROUP "hotkey:"
34
35 static GtkHotkeyInfo*   gtk_hotkey_key_file_registry_real_get_hotkey    (GtkHotkeyRegistry      *base,
36                                                                                                                                          const char                     *app_id,
37                                                                                                                                          const char                     *key_id,
38                                                                                                                                          GError                         **error);
39
40 static GList*                   gtk_hotkey_key_file_registry_real_get_application_hotkeys
41                                                                                                                                         (GtkHotkeyRegistry      *base,
42                                                                                                                                          const char                     *app_id,
43                                                                                                                                          GError                         **error);
44
45 static GList*                   gtk_hotkey_key_file_registry_real_get_all_hotkeys
46                                                                                                                                         (GtkHotkeyRegistry      *base);
47
48 static gboolean                 gtk_hotkey_key_file_registry_real_store_hotkey(GtkHotkeyRegistry        *base,
49                                                                                                                                          GtkHotkeyInfo          *info,
50                                                                                                                                          GError                         **error);
51
52 static gboolean                 gtk_hotkey_key_file_registry_real_delete_hotkey
53                                                                                                                                         (GtkHotkeyRegistry      *base,
54                                                                                                                                          const gchar            *app_id,
55                                                                                                                                          const gchar            *key_id,
56                                                                                                                                          GError                         **error);
57
58 static gboolean                 gtk_hotkey_key_file_registry_real_has_hotkey  (GtkHotkeyRegistry        *base,
59                                                                                                                                          const gchar            *app_id,
60                                                                                                                                          const gchar            *key_id);
61
62 static GFile*                   get_hotkey_home                                                         (void);
63
64 static GFile*                   get_hotkey_file                                                         (const gchar            *app_id);
65
66 static GKeyFile*                get_hotkey_key_file                                                     (const gchar            *app_id,
67                                                                                                                                          GError                         **error);
68
69 static GtkHotkeyInfo*   get_hotkey_info_from_key_file                           (GKeyFile                       *keyfile,
70                                                                                                                                          const gchar            *app_id,
71                                                                                                                                          const gchar            *key_id,
72                                                                                                                                          GError                         **error);
73
74 static GList*                   get_all_hotkey_infos_from_key_file                      (GKeyFile                       *keyfile,
75                                                                                                                                          const gchar            *app_id);
76
77 static gpointer gtk_hotkey_key_file_registry_parent_class = NULL;
78
79
80 static
81 GtkHotkeyInfo*
82 gtk_hotkey_key_file_registry_real_get_hotkey (GtkHotkeyRegistry *base,
83                                                                                         const char                      *app_id,
84                                                                                         const char                      *key_id,
85                                                                                         GError                          **error)
86 {
87         GKeyFile                                        *keyfile = NULL;
88         GtkHotkeyInfo                           *info = NULL;
89         
90         g_return_val_if_fail (GTK_HOTKEY_IS_KEY_FILE_REGISTRY(base), NULL);
91         g_return_val_if_fail (app_id != NULL, NULL);
92         g_return_val_if_fail (key_id != NULL, NULL);
93         
94         keyfile = get_hotkey_key_file (app_id, error);
95         if (keyfile == NULL)
96                 goto clean_up;
97         
98         info = get_hotkey_info_from_key_file (keyfile, app_id, key_id, error);
99         
100         clean_up:
101                 if (keyfile) g_key_file_free (keyfile);
102         
103         return info;
104         
105 }
106
107
108 static GList*
109 gtk_hotkey_key_file_registry_real_get_application_hotkeys (GtkHotkeyRegistry    *base,
110                                                                                                                  const char                     *app_id,
111                                                                                                                  GError                         **error)
112 {
113         GKeyFile                                        *keyfile;
114         
115         g_return_val_if_fail (app_id != NULL, NULL);
116         
117         keyfile = get_hotkey_key_file (app_id, error);
118         
119         if (keyfile == NULL)
120                 return NULL; /* error is set by get_hotkey_key_file() */
121         
122         return get_all_hotkey_infos_from_key_file (keyfile, app_id);
123 }
124
125
126 static GList*
127 gtk_hotkey_key_file_registry_real_get_all_hotkeys (GtkHotkeyRegistry *base)
128 {
129         GFile                                   *home;
130         GFileEnumerator                 *dir;
131         GFileInfo                               *file_info;
132         GError                                  *error;
133         GList                                   *result = NULL;
134         
135         home = get_hotkey_home ();
136         
137         error = NULL;
138         dir = g_file_enumerate_children (home, G_FILE_ATTRIBUTE_STANDARD_NAME,
139                                                                          0, NULL, &error);
140         if (error) {
141                 gchar *path = g_file_get_path (home);
142                 g_critical ("Failed to read hotkey home directory '%s': %s",
143                                         path, error->message);
144                 g_free (path);
145                 g_error_free (error);
146                 return NULL;
147         }
148         
149         error = NULL;
150         while ((file_info = g_file_enumerator_next_file (dir, NULL, &error)) != NULL) {
151                 const gchar *filename;
152                 GFile           *file;
153                 GString         *app_id;
154                 GList           *app_hotkeys;
155                 
156                 filename = g_file_info_get_name(file_info);
157                 
158                 if (g_str_has_suffix (filename, HOTKEY_FILE_EXT)) {
159                         file = g_file_get_child (home, filename);
160                         
161                         /* Extract app_id from file name */
162                         app_id = g_string_new (filename);
163                         g_string_erase (app_id, app_id->len - 8, 8);
164                         
165                         /* Load all hotkeys from the file, and append it to
166                          * the total result */
167                         app_hotkeys = gtk_hotkey_registry_get_application_hotkeys (base,
168                                                                                                                                           app_id->str,
169                                                                                                                                           &error);
170                         if (error) {
171                                 g_warning ("Failed to read hotkeys for application '%s': %s",
172                                                    app_id->str, error->message);
173                                 g_error_free (error);
174                                 error = NULL;
175                         } else {
176                                 result = g_list_concat (result, app_hotkeys);
177                         }
178                         
179                         g_string_free (app_id, TRUE);
180                         g_object_unref (file);
181                 }
182                 
183                 g_object_unref (file_info);
184         }
185         
186         if (error) {
187                 gchar *path = g_file_get_path (home);
188                 g_warning ("Failed to read hotkey home directory '%s': %s",
189                                    path, error->message);
190                 g_free (path);
191                 g_error_free (error);
192         }
193         
194         
195         g_object_unref (dir);
196         g_object_unref (home);
197         
198         return result;
199 }
200
201
202 static gboolean
203 gtk_hotkey_key_file_registry_real_store_hotkey (GtkHotkeyRegistry       *base,
204                                                                                           GtkHotkeyInfo         *info,
205                                                                                           GError                        **error)
206 {
207         GKeyFile                                        *keyfile;
208         GFile                                           *file, *home;
209         GError                                          *tmp_error;
210         gchar                                           *file_path, *group = NULL;
211         
212         
213         g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info), FALSE);
214         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
215         
216         /* Make sure we have our root dir */
217         home = get_hotkey_home ();
218         if (!g_file_query_exists(home, NULL)) {
219                 tmp_error = NULL;
220                 if (!g_file_make_directory (home, NULL, &tmp_error)) {
221                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
222                                                  GTK_HOTKEY_REGISTRY_ERROR_IO,
223                                                  "Failed to create hotkey configuration dir "
224                                                 HOTKEY_HOME": %s", tmp_error->message);
225                         g_error_free (tmp_error);
226                         g_object_unref (home);
227                         return FALSE;
228                 }
229         }
230         
231         /* Now load any old contents of the keyfile */
232         file = get_hotkey_file (gtk_hotkey_info_get_application_id (info));
233         file_path = g_file_get_path (file);
234         keyfile = g_key_file_new ();
235         
236         tmp_error = NULL;
237         if (!g_key_file_load_from_file (keyfile, file_path, 0, &tmp_error)) {
238                 if (tmp_error->code == G_KEY_FILE_ERROR_PARSE) {
239                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
240                                                  GTK_HOTKEY_REGISTRY_ERROR_MALFORMED_MEDIUM,
241                                                  "The file %s is not in a valid key-file format: %s",
242                                                  file_path, tmp_error->message);
243                         goto clean_up;
244                 }
245                 /* Ignore other errors */
246                 g_error_free (tmp_error);
247         }
248         
249         /* Prepare keyfile data */
250         group = g_strconcat (HOTKEY_GROUP, gtk_hotkey_info_get_key_id (info), NULL);
251         
252         g_key_file_set_string (keyfile, group, "Owner",
253                                                    gtk_hotkey_info_get_application_id (info));
254         g_key_file_set_string (keyfile, group, "Signature",
255                                                    gtk_hotkey_info_get_signature (info));
256         
257         if (gtk_hotkey_info_get_description (info))
258                 g_key_file_set_string (keyfile, group, "Description",
259                                                            gtk_hotkey_info_get_description (info));
260         
261         if (gtk_hotkey_info_get_app_info (info)) {
262                 GAppInfo *ai = gtk_hotkey_info_get_app_info (info);
263                 g_key_file_set_string (keyfile, group, "AppInfo",
264                                                            g_app_info_get_id (ai));
265         }
266         
267         gsize size;
268         gchar *contents;
269         tmp_error = NULL;
270         contents = g_key_file_to_data (keyfile, &size, &tmp_error);
271         if (tmp_error) {
272                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
273                                          GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
274                                          "Failed to generate keyfile contents: %s",
275                                          tmp_error->message);
276                 goto clean_up;
277         }
278         
279         /* Write the actual data */
280         g_file_set_contents (file_path, contents, size, &tmp_error);
281         if (tmp_error) {
282                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
283                                          GTK_HOTKEY_REGISTRY_ERROR_IO,
284                                          "Failed to write keyfile '%s': %s",
285                                          file_path, tmp_error->message);
286                 goto clean_up;
287         }
288         
289         clean_up:
290                 if (tmp_error) g_error_free (tmp_error);
291                 g_free (file_path);
292                 if (group) g_free (group);
293                 g_key_file_free (keyfile);
294                 g_object_unref (file);
295                 g_object_unref (home);
296         
297         if (*error)
298                 return FALSE;
299         
300         g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info), FALSE);
301         gtk_hotkey_registry_hotkey_stored (base, info);
302         return TRUE;
303 }
304
305 static gboolean
306 gtk_hotkey_key_file_registry_real_delete_hotkey (GtkHotkeyRegistry      *base,
307                                                                                            const gchar          *app_id,
308                                                                                            const gchar          *key_id,
309                                                                                            GError                       **error)
310 {
311         GtkHotkeyInfo                   *info = NULL;
312         GFile                                   *file;
313         GKeyFile                                *keyfile;
314         GError                                  *tmp_error;
315         gboolean                                is_error = FALSE;
316         gchar                                   *path, *group;
317         
318         g_return_val_if_fail (app_id != NULL, FALSE);
319         g_return_val_if_fail (key_id != NULL, FALSE);
320         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
321         
322         group = NULL;
323         
324         file = get_hotkey_file (app_id);
325         g_return_val_if_fail (G_IS_FILE(file), FALSE);
326         
327         path = g_file_get_path (file);
328         keyfile = g_key_file_new ();
329         
330         /* Load the old keyfile */
331         tmp_error = NULL;
332         g_key_file_load_from_file (keyfile, path, 0, &tmp_error);
333         if (tmp_error) {
334                 if ((tmp_error->domain == G_FILE_ERROR &&
335                          tmp_error->code == G_FILE_ERROR_NOENT) ||
336                         (tmp_error->domain == G_KEY_FILE_ERROR &&
337                          tmp_error->code == G_KEY_FILE_ERROR_NOT_FOUND))
338                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
339                                                  GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
340                                                  "No such keyfile '%s'. Application '%s' has not "
341                                                  "registered any hotkeys",
342                                                  path, app_id);
343                 else
344                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
345                                                  GTK_HOTKEY_REGISTRY_ERROR_IO,
346                                                  "Failed to load keyfile '%s': %s",
347                                                  app_id, tmp_error->message);
348                 is_error = TRUE;
349                 goto clean_up;
350         }
351         
352         /* Get a ref to the GtkHotkeyInfo so that we can emit it with the
353          * hotkey-deleted signal */
354         tmp_error = NULL;
355         info = get_hotkey_info_from_key_file (keyfile, app_id, key_id, error);
356         if (info == NULL) {
357                 is_error = TRUE;
358                 goto clean_up;
359         }
360         
361         /* Remove the group for key_id */
362         group = g_strconcat (HOTKEY_GROUP, key_id, NULL);
363         tmp_error = NULL;
364         g_key_file_remove_group (keyfile, group, &tmp_error);
365         if (tmp_error) {
366                 if (tmp_error->domain == G_KEY_FILE_ERROR &&
367                          tmp_error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
368                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
369                                                  GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
370                                                  "Application '%s' has not registered a hotkey with"
371                                                  "id '%s'", app_id, key_id);
372                 else
373                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
374                                                  GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
375                                                  "Failed to delete hotkey '%s' from application %s: %s",
376                                                  key_id, app_id, tmp_error->message);
377                 is_error = TRUE;
378                 goto clean_up;
379         }
380         
381         /* Check if the keyfile is empty. If it is we delete it */
382         gsize count;
383         GStrv groups;
384         groups = g_key_file_get_groups (keyfile, &count);
385         g_strfreev (groups);
386         if (count == 0) {
387                 tmp_error = NULL;
388                 g_file_delete (file, NULL, &tmp_error);
389                 
390                 if (tmp_error) {
391                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
392                                                  GTK_HOTKEY_REGISTRY_ERROR_IO,
393                                                  "Failed to delete empty keyfile '%s': %s",
394                                                  path, tmp_error->message);
395                         is_error = TRUE;
396                 }
397                 /* File deleted, we should just clean up and exit */
398                 goto clean_up;
399         }
400         
401         /* Write new keyfile */
402         gsize size;
403         gchar *contents;
404         tmp_error = NULL;
405         contents = g_key_file_to_data (keyfile, &size, &tmp_error);
406         if (tmp_error) {
407                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
408                                          GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
409                                          "Failed to generate keyfile contents: %s",
410                                          tmp_error->message);
411                 is_error = TRUE;
412                 goto clean_up;
413         }
414         
415         tmp_error = NULL;
416         g_file_set_contents (path, contents, size, &tmp_error);
417         if (tmp_error) {
418                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
419                                          GTK_HOTKEY_REGISTRY_ERROR_IO,
420                                          "Failed to write keyfile '%s': %s",
421                                          path, tmp_error->message);
422                 is_error = TRUE;
423                 goto clean_up;
424         }
425         
426         clean_up:
427                 if (tmp_error) g_error_free (tmp_error);
428                 g_object_unref (file);
429                 g_free (path);
430                 if (group) g_free (group);
431                 g_key_file_free (keyfile);
432                 
433         if (is_error)
434                 return FALSE;
435         
436         gtk_hotkey_registry_hotkey_deleted (base, info);
437         g_object_unref (info);
438         return TRUE;
439 }
440
441 static gboolean
442 gtk_hotkey_key_file_registry_real_has_hotkey (GtkHotkeyRegistry *base,
443                                                                                         const gchar                     *app_id,
444                                                                                         const gchar                     *key_id)
445 {
446         GFile                                   *file;
447         gboolean                                exists;
448         
449         g_return_val_if_fail (app_id != NULL, FALSE);
450         g_return_val_if_fail (key_id != NULL, FALSE);
451         
452         file = get_hotkey_file (app_id);
453         g_return_val_if_fail (G_IS_FILE(file), FALSE);
454         
455         if (g_file_query_exists (file, NULL))
456                 exists = TRUE;
457         else
458                 exists = FALSE;
459         
460         g_object_unref (file);
461         return exists;
462 }
463
464 static void
465 gtk_hotkey_key_file_registry_class_init (GtkHotkeyKeyFileRegistryClass *klass)
466 {
467         gtk_hotkey_key_file_registry_parent_class = g_type_class_peek_parent (klass);
468         GTK_HOTKEY_REGISTRY_CLASS (klass)->get_hotkey = gtk_hotkey_key_file_registry_real_get_hotkey;
469         GTK_HOTKEY_REGISTRY_CLASS (klass)->get_application_hotkeys = gtk_hotkey_key_file_registry_real_get_application_hotkeys;
470         GTK_HOTKEY_REGISTRY_CLASS (klass)->get_all_hotkeys = gtk_hotkey_key_file_registry_real_get_all_hotkeys;
471         GTK_HOTKEY_REGISTRY_CLASS (klass)->store_hotkey = gtk_hotkey_key_file_registry_real_store_hotkey;
472         GTK_HOTKEY_REGISTRY_CLASS (klass)->delete_hotkey = gtk_hotkey_key_file_registry_real_delete_hotkey;
473         GTK_HOTKEY_REGISTRY_CLASS (klass)->has_hotkey = gtk_hotkey_key_file_registry_real_has_hotkey;
474 }
475
476
477 static void
478 gtk_hotkey_key_file_registry_init (GtkHotkeyKeyFileRegistry *self)
479 {
480         
481 }
482
483 static void
484 gtk_hotkey_key_file_registry_finalize (GtkHotkeyKeyFileRegistry *self)
485 {
486         
487 }
488
489 GType
490 gtk_hotkey_key_file_registry_get_type (void)
491 {
492         static GType gtk_hotkey_key_file_registry_type_id = 0;
493         
494         if (G_UNLIKELY (gtk_hotkey_key_file_registry_type_id == 0)) {
495                 static const GTypeInfo g_define_type_info = {
496                         sizeof (GtkHotkeyKeyFileRegistryClass),
497                         (GBaseInitFunc) gtk_hotkey_key_file_registry_init,
498                         (GBaseFinalizeFunc) gtk_hotkey_key_file_registry_finalize,
499                         (GClassInitFunc) gtk_hotkey_key_file_registry_class_init,
500                         (GClassFinalizeFunc) NULL,
501                         NULL,
502                         sizeof (GtkHotkeyKeyFileRegistry),
503                         0,
504                         (GInstanceInitFunc) gtk_hotkey_key_file_registry_init
505                 };
506                 
507                 gtk_hotkey_key_file_registry_type_id = g_type_register_static (GTK_HOTKEY_TYPE_STORAGE, "GtkHotkeyKeyFileRegistry", &g_define_type_info, 0);
508         }
509         return gtk_hotkey_key_file_registry_type_id;
510 }
511
512 static GFile*
513 get_hotkey_home (void)
514 {
515         GFile   *home;
516         
517         home = g_file_parse_name (HOTKEY_HOME);
518         
519         if (g_file_query_exists(home, NULL) &&
520                 !gtk_hotkey_g_file_is_directory(home)) {
521                 g_critical (HOTKEY_HOME" exists but is not a directory");
522                 g_object_unref (home);
523                 return NULL;
524         }
525         
526         return home;
527 }
528
529 /* It is not guaranteed that the keyfile exists */
530 static GFile*
531 get_hotkey_file (const gchar *app_id)
532 {
533         GFile   *home, *file;
534         gchar   *filename;
535         
536         g_return_val_if_fail (app_id != NULL, NULL);
537         
538         home = get_hotkey_home();
539         g_return_val_if_fail (home != NULL, NULL);
540         
541         filename = g_strconcat (app_id, HOTKEY_FILE_EXT, NULL);
542         file = g_file_get_child (home, filename);
543         
544         g_object_unref (home);
545         g_free (filename);
546         return file;
547 }
548
549 static GKeyFile*
550 get_hotkey_key_file (const gchar *app_id, GError **error)
551 {
552         gchar           *path;
553         GFile           *file;
554         GKeyFile        *keyfile = NULL;
555         GError          *tmp_error;
556         
557         g_return_val_if_fail (app_id != NULL, NULL);
558         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
559         
560         file = get_hotkey_file (app_id);
561         if (!g_file_query_exists (file, NULL)) {
562                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
563                                          GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
564                                          "Application '%s' has not registered any hotkeys", app_id);
565                 g_object_unref (file);
566                 return NULL;
567         }
568         
569         path = g_file_get_path (file);
570         keyfile = g_key_file_new ();
571         
572         tmp_error = NULL;
573         g_key_file_load_from_file (keyfile, path, 0, &tmp_error);
574         if (tmp_error) {
575                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
576                                          GTK_HOTKEY_REGISTRY_ERROR_IO,
577                                          "Failed to load keyfile '%s': %s", path, tmp_error->message);
578                 goto clean_up;
579         }
580         
581         clean_up:
582                 g_free (path);
583                 g_object_unref (file);
584                 if (tmp_error) g_error_free (tmp_error);
585         
586         if (*error) {
587                 g_key_file_free (keyfile);
588                 return NULL;
589         }
590         
591         return keyfile;
592 }
593
594 static GtkHotkeyInfo*
595 get_hotkey_info_from_key_file (GKeyFile *keyfile,
596                                                            const gchar *app_id,
597                                                            const gchar *key_id,
598                                                            GError **error)
599 {
600         GtkHotkeyInfo   *info = NULL;
601         gchar                   *group, *description, *app_info_id, *signature;
602         GAppInfo                *app_info = NULL;
603         
604         g_return_val_if_fail (keyfile != NULL, NULL);
605         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
606         g_return_val_if_fail (app_id != NULL, NULL);
607         g_return_val_if_fail (key_id != NULL, NULL);
608         
609         group = g_strconcat (HOTKEY_GROUP, key_id, NULL);
610         description = g_key_file_get_string (keyfile, group, "Description", NULL);
611         app_info_id = g_key_file_get_string (keyfile, group, "AppInfo", NULL);
612         signature = g_key_file_get_string (keyfile, group, "Signature", NULL);
613         
614         if (!g_key_file_has_group (keyfile, group)) {
615                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
616                                          GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_KEY,
617                                          "Keyfile has no group "HOTKEY_GROUP"%s", key_id);
618                 goto clean_up;
619         }
620         
621         if (!signature) {
622                 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
623                                          GTK_HOTKEY_REGISTRY_ERROR_BAD_SIGNATURE,
624                                          "No 'Signature' defined for hotkey '%s' for application '%s'",
625                                          key_id, app_id);
626                 goto clean_up;
627         }
628         
629         if (app_info_id) {
630                 app_info = G_APP_INFO(g_desktop_app_info_new (app_info_id));
631                 if (!G_IS_APP_INFO(app_info)) {
632                         g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
633                                                  GTK_HOTKEY_REGISTRY_ERROR_MISSING_APP,
634                                                  "Keyfile referring to 'AppInfo = %s', but no application"
635                                                  "by that id is registered on the system", app_info_id);
636                         goto clean_up;
637                 }       
638         }
639         
640         info = gtk_hotkey_info_new (app_id, key_id, signature, app_info);
641         if (description)
642                 gtk_hotkey_info_set_description (info, description);
643         
644         clean_up:
645                 g_free (group);
646                 if (signature) g_free (signature);
647                 if (description) g_free (description);
648                 if (app_info_id) g_free (app_info_id);
649                 if (app_info) g_object_unref (app_info);
650                         
651         return info;
652 }
653
654 static GList*
655 get_all_hotkey_infos_from_key_file (GKeyFile    *keyfile,
656                                                                         const gchar     *app_id)
657 {
658         GList                   *hotkeys;
659         GtkHotkeyInfo   *hotkey;
660         GStrv                   groups;
661         gsize                   count;
662         gchar                   *group;
663         GString                 *key_id;
664         GError                  *error;
665         
666         g_return_val_if_fail (keyfile != NULL, NULL);
667         g_return_val_if_fail (app_id != NULL, NULL);
668         
669         hotkeys = NULL;
670         groups = g_key_file_get_groups (keyfile, &count);
671         
672         int i;
673         for (i = 0; i < count; i++) {
674                 group = groups[i];
675                 key_id = g_string_new (group);
676                 
677                 /* Ignore non hotkey groups */
678                 if (!g_str_has_prefix (key_id->str, HOTKEY_GROUP)) {
679                         g_warning ("Hotkey file for %s contains non 'hotkey:' group '%s'",
680                                            app_id, group);
681                         g_string_free (key_id, TRUE);
682                         continue;
683                 }
684                 
685                 /* Strip 'hotkey:' prefix from key_id */
686                 g_string_erase (key_id, 0, 7);
687                 
688                 error = NULL;
689                 hotkey = get_hotkey_info_from_key_file (keyfile, app_id, key_id->str, &error);
690                 if (error) {
691                         g_warning ("Failed to read hotkey '%s' for application '%s': %s",
692                                            key_id->str, app_id, error->message);
693                         g_error_free (error);
694                         g_string_free (key_id, TRUE);
695                         continue;
696                 }
697                 
698                 hotkeys = g_list_prepend (hotkeys, hotkey);
699                         
700                 g_string_free (key_id, TRUE);
701         }
702         
703         g_strfreev (groups);
704         return hotkeys;
705 }