~elementary-os/elementaryos/os-patch-desktop-file-utils-xenial

« back to all changes in this revision

Viewing changes to src/update-desktop-database.c

  • Committer: RabbitBot
  • Date: 2016-01-17 16:50:38 UTC
  • Revision ID: rabbitbot@elementary.io-20160117165038-3yu4qhcpf1rjfh01
Initial import, version 0.22-1ubuntu3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* update-desktop-database.c - maintains mimetype<->desktop mapping cache
 
2
 * vim: set ts=2 sw=2 et: */
 
3
 
 
4
/*
 
5
 * Copyright (C) 2004-2006  Red Hat, Inc.
 
6
 * Copyright (C) 2006, 2008  Vincent Untz
 
7
 *
 
8
 * Program written by Ray Strode <rstrode@redhat.com>
 
9
 *                    Vincent Untz <vuntz@gnome.org>
 
10
 *
 
11
 * update-desktop-database is free software; you can redistribute it
 
12
 * and/or modify it under the terms of the GNU General Public License as
 
13
 * published by the Free Software Foundation; either version 2 of the
 
14
 * License, or (at your option) any later version.
 
15
 *
 
16
 * update-desktop-database is distributed in the hope that it will be
 
17
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 
18
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
19
 * General Public License for more details.
 
20
 *
 
21
 * You should have received a copy of the GNU General Public License
 
22
 * along with update-desktop-database; see the file COPYING.  If not,
 
23
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite
 
24
 * 330, Boston, MA 02111-1307, USA.
 
25
 */
 
26
 
 
27
#include <config.h>
 
28
#include <errno.h>
 
29
#include <fcntl.h>
 
30
#include <stdio.h>
 
31
#include <string.h>
 
32
#include <sys/types.h>
 
33
#include <sys/stat.h>
 
34
#include <unistd.h>
 
35
 
 
36
#include <glib.h>
 
37
#include <glib/gi18n.h>
 
38
 
 
39
#include "keyfileutils.h"
 
40
#include "mimeutils.h"
 
41
 
 
42
#define NAME "update-desktop-database"
 
43
#define CACHE_FILENAME "mimeinfo.cache"
 
44
#define TEMP_CACHE_FILENAME_PREFIX ".mimeinfo.cache.XXXXXX"
 
45
 
 
46
#define udd_print(...) if (!quiet) g_printerr (__VA_ARGS__)
 
47
#define udd_verbose_print(...) if (!quiet && verbose) g_printerr (__VA_ARGS__)
 
48
 
 
49
static FILE *open_temp_cache_file (const char  *dir,
 
50
                                   char       **filename,
 
51
                                   GError     **error);
 
52
static void add_mime_type (const char *mime_type, GList *desktop_files, FILE *f);
 
53
static void sync_database (const char *dir, GError **error);
 
54
static void cache_desktop_file (const char  *desktop_file,
 
55
                                const char  *mime_type,
 
56
                                GError     **error);
 
57
static void process_desktop_file (const char  *desktop_file,
 
58
                                  const char  *name,
 
59
                                  GError     **error);
 
60
static void process_desktop_files (const char *desktop_dir,
 
61
                                   const char *prefix,
 
62
                                   GError **error);
 
63
static void update_database (const char *desktop_dir, GError **error);
 
64
static const char ** get_default_search_path (void);
 
65
static void print_desktop_dirs (const char **dirs);
 
66
 
 
67
static GHashTable *mime_types_map = NULL;
 
68
static gboolean verbose = FALSE, quiet = FALSE;
 
69
 
 
70
static void
 
71
list_free_deep (gpointer key, GList *l, gpointer data)
 
72
{
 
73
  g_list_foreach (l, (GFunc)g_free, NULL);
 
74
  g_list_free (l);
 
75
}
 
76
 
 
77
static void
 
78
cache_desktop_file (const char  *desktop_file,
 
79
                    const char  *mime_type,
 
80
                    GError     **error)
 
81
{
 
82
  GList *desktop_files;
 
83
 
 
84
  desktop_files = (GList *) g_hash_table_lookup (mime_types_map, mime_type);
 
85
 
 
86
  /* do not add twice a desktop file mentioning the mime type more than once
 
87
   * (no need to use g_list_find() because we cache all mime types registered
 
88
   * by a desktop file before moving to another desktop file) */
 
89
  if (desktop_files &&
 
90
      strcmp (desktop_file, (const char *) desktop_files->data) == 0)
 
91
    return;
 
92
 
 
93
  desktop_files = g_list_prepend (desktop_files, g_strdup (desktop_file));
 
94
  g_hash_table_insert (mime_types_map, g_strdup (mime_type), desktop_files);
 
95
}
 
96
 
 
97
 
 
98
static void
 
99
process_desktop_file (const char  *desktop_file,
 
100
                      const char  *name,
 
101
                      GError     **error)
 
102
{
 
103
  GError *load_error;
 
104
  GKeyFile *keyfile;
 
105
  char **mime_types;
 
106
  int i;
 
107
 
 
108
  keyfile = g_key_file_new ();
 
109
 
 
110
  load_error = NULL;
 
111
  g_key_file_load_from_file (keyfile, desktop_file,
 
112
                             G_KEY_FILE_NONE, &load_error);
 
113
 
 
114
  if (load_error != NULL)
 
115
    {
 
116
      g_propagate_error (error, load_error);
 
117
      return;
 
118
    }
 
119
 
 
120
  /* Hidden=true means that the .desktop file should be completely ignored */
 
121
  if (g_key_file_get_boolean (keyfile, GROUP_DESKTOP_ENTRY, "Hidden", NULL))
 
122
    {
 
123
      g_key_file_free (keyfile);
 
124
      return;
 
125
    }
 
126
 
 
127
  mime_types = g_key_file_get_string_list (keyfile,
 
128
                                           GROUP_DESKTOP_ENTRY,
 
129
                                           "MimeType", NULL, &load_error);
 
130
 
 
131
  g_key_file_free (keyfile);
 
132
 
 
133
  if (load_error != NULL)
 
134
    {
 
135
      g_propagate_error (error, load_error);
 
136
      return;
 
137
    }
 
138
 
 
139
  for (i = 0; mime_types[i] != NULL; i++)
 
140
    {
 
141
      char *mime_type;
 
142
      MimeUtilsValidity valid;
 
143
      char *valid_error;
 
144
 
 
145
      mime_type = g_strchomp (mime_types[i]);
 
146
      valid = mu_mime_type_is_valid (mime_types[i], &valid_error);
 
147
      switch (valid)
 
148
      {
 
149
        case MU_VALID:
 
150
          break;
 
151
        case MU_DISCOURAGED:
 
152
          udd_print (_("Warning in file \"%s\": usage of MIME type \"%s\" is "
 
153
                       "discouraged (%s)\n"),
 
154
                     desktop_file, mime_types[i], valid_error);
 
155
          g_free (valid_error);
 
156
          break;
 
157
        case MU_INVALID:
 
158
          udd_print (_("Error in file \"%s\": \"%s\" is an invalid MIME type "
 
159
                       "(%s)\n"),
 
160
                     desktop_file, mime_types[i], valid_error);
 
161
          g_free (valid_error);
 
162
          /* not a break: we continue to the next mime type */
 
163
          continue;
 
164
        default:
 
165
          g_assert_not_reached ();
 
166
      }
 
167
 
 
168
      cache_desktop_file (name, mime_type, &load_error);
 
169
 
 
170
      if (load_error != NULL)
 
171
        {
 
172
          g_propagate_error (error, load_error);
 
173
          g_strfreev (mime_types);
 
174
          return;
 
175
        }
 
176
    }
 
177
  g_strfreev (mime_types);
 
178
}
 
179
 
 
180
static void
 
181
process_desktop_files (const char  *desktop_dir,
 
182
                       const char  *prefix,
 
183
                       GError     **error)
 
184
{
 
185
  GError *process_error;
 
186
  GDir *dir;
 
187
  const char *filename;
 
188
 
 
189
  process_error = NULL;
 
190
  dir = g_dir_open (desktop_dir, 0, &process_error);
 
191
 
 
192
  if (process_error != NULL)
 
193
    {
 
194
      g_propagate_error (error, process_error);
 
195
      return;
 
196
    }
 
197
 
 
198
  while ((filename = g_dir_read_name (dir)) != NULL)
 
199
    {
 
200
      char *full_path, *name;
 
201
 
 
202
      full_path = g_build_filename (desktop_dir, filename, NULL);
 
203
 
 
204
      if (g_file_test (full_path, G_FILE_TEST_IS_DIR))
 
205
        {
 
206
          char *sub_prefix;
 
207
 
 
208
          sub_prefix = g_strdup_printf ("%s%s-", prefix, filename);
 
209
 
 
210
          process_desktop_files (full_path, sub_prefix, &process_error);
 
211
          g_free (sub_prefix);
 
212
 
 
213
          if (process_error != NULL)
 
214
            {
 
215
              udd_verbose_print (_("Could not process directory \"%s\": %s\n"),
 
216
                                 full_path, process_error->message);
 
217
              g_error_free (process_error);
 
218
              process_error = NULL;
 
219
            }
 
220
          g_free (full_path);
 
221
          continue;
 
222
        }
 
223
      else if (!g_str_has_suffix (filename, ".desktop"))
 
224
        {
 
225
          g_free (full_path);
 
226
          continue;
 
227
        }
 
228
 
 
229
      name = g_strdup_printf ("%s%s", prefix, filename);
 
230
      process_desktop_file (full_path, name, &process_error);
 
231
      g_free (name);
 
232
 
 
233
      if (process_error != NULL)
 
234
        {
 
235
          if (!g_error_matches (process_error,
 
236
                                G_KEY_FILE_ERROR,
 
237
                                G_KEY_FILE_ERROR_KEY_NOT_FOUND))
 
238
            {
 
239
              udd_print (_("Could not parse file \"%s\": %s\n"), full_path,
 
240
                         process_error->message);
 
241
            }
 
242
          else
 
243
            {
 
244
              udd_verbose_print (_("File \"%s\" lacks MimeType key\n"),
 
245
                                 full_path);
 
246
            }
 
247
 
 
248
          g_error_free (process_error);
 
249
          process_error = NULL;
 
250
        }
 
251
 
 
252
      g_free (full_path);
 
253
    }
 
254
 
 
255
  g_dir_close (dir);
 
256
}
 
257
 
 
258
static FILE *
 
259
open_temp_cache_file (const char *dir, char **filename, GError **error)
 
260
{
 
261
  int fd;
 
262
  char *file;
 
263
  FILE *fp;
 
264
  mode_t mask;
 
265
 
 
266
  file = g_build_filename (dir, TEMP_CACHE_FILENAME_PREFIX, NULL);
 
267
  fd = g_mkstemp (file);
 
268
 
 
269
  if (fd < 0)
 
270
    {
 
271
      g_set_error (error, G_FILE_ERROR,
 
272
                   g_file_error_from_errno (errno),
 
273
                   "%s", g_strerror (errno));
 
274
      g_free (file);
 
275
      return NULL;
 
276
    }
 
277
 
 
278
  mask = umask(0);
 
279
  (void) umask (mask);
 
280
 
 
281
  fchmod (fd, 0666 & ~mask);
 
282
 
 
283
  fp = fdopen (fd, "w+");
 
284
  if (fp == NULL)
 
285
    {
 
286
      g_set_error (error, G_FILE_ERROR,
 
287
                   g_file_error_from_errno (errno),
 
288
                   "%s", g_strerror (errno));
 
289
      g_free (file);
 
290
      close (fd);
 
291
      return NULL;
 
292
    }
 
293
 
 
294
  if (filename)
 
295
    *filename = file;
 
296
  else
 
297
    g_free (file);
 
298
 
 
299
  return fp;
 
300
}
 
301
 
 
302
static void
 
303
add_mime_type (const char *mime_type, GList *desktop_files, FILE *f)
 
304
{
 
305
  GString *list;
 
306
  GList *desktop_file;
 
307
 
 
308
  list = g_string_new (mime_type);
 
309
  g_string_append_c (list, '=');
 
310
  for (desktop_file = desktop_files;
 
311
       desktop_file != NULL;
 
312
       desktop_file = desktop_file->next)
 
313
    {
 
314
      g_string_append (list, (const char *) desktop_file->data);
 
315
      g_string_append_c (list, ';');
 
316
    }
 
317
  g_string_append_c (list, '\n');
 
318
 
 
319
  fputs (list->str, f);
 
320
 
 
321
  g_string_free (list, TRUE);
 
322
}
 
323
 
 
324
static void
 
325
sync_database (const char *dir, GError **error)
 
326
{
 
327
  GError *sync_error;
 
328
  char *temp_cache_file, *cache_file;
 
329
  FILE *tmp_file;
 
330
  GList *keys, *key;
 
331
 
 
332
  temp_cache_file = NULL;
 
333
  sync_error = NULL;
 
334
  tmp_file = open_temp_cache_file (dir, &temp_cache_file, &sync_error);
 
335
 
 
336
  if (sync_error != NULL)
 
337
    {
 
338
      g_propagate_error (error, sync_error);
 
339
      return;
 
340
    }
 
341
 
 
342
  fputs ("[MIME Cache]\n", tmp_file);
 
343
 
 
344
  keys = g_hash_table_get_keys (mime_types_map);
 
345
  keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
 
346
 
 
347
  for (key = keys; key != NULL; key = key->next)
 
348
    add_mime_type (key->data,
 
349
                   g_hash_table_lookup (mime_types_map, key->data),
 
350
                   tmp_file);
 
351
 
 
352
  g_list_free (keys);
 
353
  fclose (tmp_file);
 
354
 
 
355
  cache_file = g_build_filename (dir, CACHE_FILENAME, NULL);
 
356
  if (rename (temp_cache_file, cache_file) < 0)
 
357
    {
 
358
      g_set_error (error, G_FILE_ERROR,
 
359
                   g_file_error_from_errno (errno),
 
360
                   _("Cache file \"%s\" could not be written: %s"),
 
361
                   cache_file, g_strerror (errno));
 
362
 
 
363
      unlink (temp_cache_file);
 
364
    }
 
365
  g_free (temp_cache_file);
 
366
  g_free (cache_file);
 
367
}
 
368
 
 
369
static void
 
370
update_database (const char  *desktop_dir,
 
371
                 GError     **error)
 
372
{
 
373
  GError *update_error;
 
374
 
 
375
  mime_types_map = g_hash_table_new_full (g_str_hash, g_str_equal,
 
376
                                          (GDestroyNotify)g_free,
 
377
                                          NULL);
 
378
 
 
379
  update_error = NULL;
 
380
  process_desktop_files (desktop_dir, "", &update_error);
 
381
 
 
382
  if (update_error != NULL)
 
383
    g_propagate_error (error, update_error);
 
384
  else
 
385
    {
 
386
      sync_database (desktop_dir, &update_error);
 
387
      if (update_error != NULL)
 
388
        g_propagate_error (error, update_error);
 
389
    }
 
390
  g_hash_table_foreach (mime_types_map, (GHFunc) list_free_deep, NULL);
 
391
  g_hash_table_destroy (mime_types_map);
 
392
}
 
393
 
 
394
static const char **
 
395
get_default_search_path (void)
 
396
{
 
397
  static char **args = NULL;
 
398
  const char * const *data_dirs;
 
399
  int i;
 
400
 
 
401
  if (args != NULL)
 
402
    return (const char **) args;
 
403
 
 
404
  data_dirs = g_get_system_data_dirs ();
 
405
 
 
406
  for (i = 0; data_dirs[i] != NULL; i++);
 
407
 
 
408
  args = g_new (char *, i + 1);
 
409
 
 
410
  for (i = 0; data_dirs[i] != NULL; i++)
 
411
    args[i] = g_build_filename (data_dirs[i], "applications", NULL);
 
412
 
 
413
  args[i] = NULL;
 
414
 
 
415
  return (const char **) args;
 
416
}
 
417
 
 
418
void
 
419
print_desktop_dirs (const char **dirs)
 
420
{
 
421
  char *directories;
 
422
 
 
423
  directories = g_strjoinv (", ", (char **) dirs);
 
424
  udd_verbose_print(_("Search path is now: [%s]\n"), directories);
 
425
  g_free (directories);
 
426
}
 
427
 
 
428
int
 
429
main (int    argc,
 
430
      char **argv)
 
431
{
 
432
  GError *error;
 
433
  GOptionContext *context;
 
434
  const char **desktop_dirs;
 
435
  int i;
 
436
  gboolean found_processable_dir;
 
437
 
 
438
  const GOptionEntry options[] =
 
439
   {
 
440
     { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,
 
441
       N_("Do not display any information about processing and "
 
442
          "updating progress"), NULL},
 
443
 
 
444
     { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
 
445
       N_("Display more information about processing and updating progress"),
 
446
       NULL},
 
447
 
 
448
     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &desktop_dirs,
 
449
       NULL, N_("[DIRECTORY...]") },
 
450
     { NULL }
 
451
   };
 
452
 
 
453
  context = g_option_context_new ("");
 
454
  g_option_context_set_summary (context, _("Build cache database of MIME types handled by desktop files."));
 
455
  g_option_context_add_main_entries (context, options, NULL);
 
456
 
 
457
  desktop_dirs = NULL;
 
458
  error = NULL;
 
459
  g_option_context_parse (context, &argc, &argv, &error);
 
460
 
 
461
  if (error != NULL) {
 
462
    g_printerr ("%s\n", error->message);
 
463
    g_printerr (_("Run \"%s --help\" to see a full list of available command line options.\n"), argv[0]);
 
464
    g_error_free (error);
 
465
    return 1;
 
466
  }
 
467
 
 
468
  if (desktop_dirs == NULL || desktop_dirs[0] == NULL)
 
469
    desktop_dirs = get_default_search_path ();
 
470
 
 
471
  print_desktop_dirs (desktop_dirs);
 
472
 
 
473
  found_processable_dir = FALSE;
 
474
  for (i = 0; desktop_dirs[i] != NULL; i++)
 
475
    {
 
476
      error = NULL;
 
477
      update_database (desktop_dirs[i], &error);
 
478
 
 
479
      if (error != NULL)
 
480
        {
 
481
          udd_verbose_print (_("Could not create cache file in \"%s\": %s\n"),
 
482
                             desktop_dirs[i], error->message);
 
483
          g_error_free (error);
 
484
          error = NULL;
 
485
        }
 
486
      else
 
487
        found_processable_dir = TRUE;
 
488
    }
 
489
  g_option_context_free (context);
 
490
 
 
491
  if (!found_processable_dir)
 
492
    {
 
493
      char *directories;
 
494
 
 
495
      directories = g_strjoinv (", ", (char **) desktop_dirs);
 
496
      udd_print (_("The databases in [%s] could not be updated.\n"),
 
497
                 directories);
 
498
 
 
499
      g_free (directories);
 
500
 
 
501
      return 1;
 
502
    }
 
503
 
 
504
  return 0;
 
505
}