ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-application-registry.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* gnome-vfs-mime-info.c - GNOME mime-information implementation.
4
5    Copyright (C) 1998 Miguel de Icaza
6    Copyright (C) 2000 Eazel, Inc
7    All rights reserved.
8
9    The Gnome Library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public License as
11    published by the Free Software Foundation; either version 2 of the
12    License, or (at your option) any later version.
13
14    The Gnome Library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18
19    You should have received a copy of the GNU Library General Public
20    License along with the Gnome Library; see the file COPYING.LIB.  If not,
21    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA 02111-1307, USA.  */
23 /*
24  * Authors: George Lebl
25  *      Based on original mime-info database code by Miguel de Icaza
26  */
27
28 #include "config.h"
29 #include "gnome-vfs-application-registry.h"
30 #include "gnome-vfs-configuration.h"
31 #include "gnome-vfs-mime-handlers.h"
32 #include "gnome-vfs-mime-private.h"
33 #include "gnome-vfs-mime.h"
34 #include "gnome-vfs-private-utils.h"
35 #include "gnome-vfs-result.h"
36 #include <dirent.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <time.h>
47 #include <unistd.h>
48
49 #if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED
50 # define getc_unlocked(fp) getc (fp)
51 #endif
52
53 typedef struct _Application Application;
54 struct _Application {
55         char       *app_id;
56         int         ref_count;
57         /* The following is true if this was found in the
58          * home directory or if the user changed any keys
59          * here.  It means that it will be saved into a user
60          * file */
61         gboolean    user_owned;
62         GHashTable *keys;
63         GnomeVFSMimeApplicationArgumentType expects_uris;
64         GList      *mime_types;
65         GList      *supported_uri_schemes;
66         /* The user_owned version of this if this is a system
67          * version */
68         Application *user_application;
69 };
70
71 /* Describes the directories we scan for information */
72 typedef struct {
73         char *dirname;
74         unsigned int valid : 1;
75         unsigned int system_dir : 1;
76 } ApplicationRegistryDir;
77
78 /* These ones are used to automatically reload mime info on demand */
79 static FileDateTracker *registry_date_tracker;
80 static ApplicationRegistryDir gnome_registry_dir;
81 static ApplicationRegistryDir user_registry_dir;
82
83 /* To initialize the module automatically */
84 static gboolean gnome_vfs_application_registry_initialized = FALSE;
85
86
87 static GList *current_lang = NULL;
88 /* we want to replace the previous key if the current key has a higher
89    language level */
90 static char *previous_key = NULL;
91 static int previous_key_lang_level = -1;
92
93 /*
94  * A hash table containing application registry record (Application)
95  * keyed by application ids.
96  */
97 static GHashTable *global_applications = NULL;
98 /*
99  * A hash table containing GList's of application registry records (Application)
100  * keyed by the mime types
101  */
102 /* specific mime_types (e.g. image/png) */
103 static GHashTable *specific_mime_types = NULL;
104 /* generic mime_types (e.g. image/<star>) */
105 static GHashTable *generic_mime_types = NULL;
106
107 /*
108  * Dirty flag, just to make sure we don't sync needlessly
109  */
110 static gboolean user_file_dirty = FALSE;
111
112 /*
113  * Some local prototypes
114  */
115 static void gnome_vfs_application_registry_init (void);
116 static void application_clear_mime_types (Application *application);
117
118 static Application *
119 application_ref (Application *application)
120 {
121         g_return_val_if_fail(application != NULL, NULL);
122
123         application->ref_count ++;
124
125         return application;
126 }
127
128 static void
129 hash_foreach_free_key_value(gpointer key, gpointer value, gpointer user_data)
130 {
131         g_free(key);
132         g_free(value);
133 }
134
135 static void
136 application_unref (Application *application)
137 {
138         g_return_if_fail(application != NULL);
139
140         application->ref_count --;
141
142         if (application->ref_count == 0) {
143                 application_clear_mime_types (application);
144
145                 if (application->keys != NULL) {
146                         g_hash_table_foreach(application->keys, hash_foreach_free_key_value, NULL);
147                         g_hash_table_destroy(application->keys);
148                         application->keys = NULL;
149                 }
150
151                 g_free(application->app_id);
152                 application->app_id = NULL;
153
154                 if (application->user_application != NULL) {
155                         application_unref (application->user_application);
156                         application->user_application = NULL;
157                 }
158
159                 g_free(application);
160         }
161 }
162
163 static Application *
164 application_new (const char *app_id, gboolean user_owned)
165 {
166         Application *application;
167
168         application = g_new0 (Application, 1);
169         application->app_id = g_strdup(app_id);
170         application->ref_count = 1;
171         application->expects_uris = GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS;
172         application->user_owned = user_owned;
173
174         return application;
175 }
176
177 static Application *
178 application_lookup_or_create (const char *app_id, gboolean user_owned)
179 {
180         Application *application;
181
182         g_return_val_if_fail(app_id != NULL, NULL);
183
184         gnome_vfs_application_registry_init ();
185
186         application = g_hash_table_lookup (global_applications, app_id);
187         if (application != NULL) {
188                 if ( ! user_owned) {
189                         /* if we find only a user app, do magic */
190                         if (application->user_owned) {
191                                 Application *new_application;
192                                 new_application = application_new (app_id, FALSE/*user_owned*/);
193                                 new_application->user_application = application;
194                                 /* override the user application */
195                                 g_hash_table_insert (global_applications, new_application->app_id,
196                                                      new_application);
197                                 return new_application;
198                         } else {
199                                 return application;
200                         }
201                 } else {
202                         if (application->user_owned) {
203                                 return application;
204                         } if (application->user_application != NULL) {
205                                 return application->user_application;
206                         } else {
207                                 Application *new_application;
208                                 new_application = application_new (app_id, TRUE/*user_owned*/);
209                                 application->user_application = new_application;
210                                 return new_application;
211                         }
212                 }
213         }
214
215         application = application_new (app_id, user_owned);
216
217         g_hash_table_insert (global_applications, application->app_id, application);
218
219         return application;
220 }
221
222 static Application *
223 application_lookup (const char *app_id)
224 {
225         g_return_val_if_fail(app_id != NULL, NULL);
226
227         if (global_applications == NULL)
228                 return NULL;
229
230         return g_hash_table_lookup (global_applications, app_id);
231 }
232
233 static const char *
234 peek_value (const Application *application, const char *key)
235 {
236         g_return_val_if_fail(application != NULL, NULL);
237         g_return_val_if_fail(key != NULL, NULL);
238
239         if (application->keys == NULL)
240                 return NULL;
241
242         return g_hash_table_lookup (application->keys, key);
243 }
244
245 static void
246 set_value (Application *application, const char *key, const char *value)
247 {
248         char *old_value, *old_key;
249
250         g_return_if_fail(application != NULL);
251         g_return_if_fail(key != NULL);
252         g_return_if_fail(value != NULL);
253
254         if (application->keys == NULL)
255                 application->keys = g_hash_table_new (g_str_hash, g_str_equal);
256
257         if (g_hash_table_lookup_extended (application->keys, key,
258                                           (gpointer *)&old_key,
259                                           (gpointer *)&old_value)) {
260                 g_hash_table_insert (application->keys,
261                                      old_key, g_strdup (value));
262                 g_free (old_value);
263         } else {
264                 g_hash_table_insert (application->keys,
265                                      g_strdup (key), g_strdup (value));
266         }
267 }
268
269 static void
270 unset_key (Application *application, const char *key)
271 {
272         char *old_value, *old_key;
273
274         g_return_if_fail(application != NULL);
275         g_return_if_fail(key != NULL);
276
277         if (application->keys == NULL)
278                 return;
279
280         if (g_hash_table_lookup_extended (application->keys, key,
281                                           (gpointer *)&old_key,
282                                           (gpointer *)&old_value)) {
283                 g_hash_table_remove (application->keys, old_key);
284                 g_free (old_key);
285                 g_free (old_value);
286         }
287 }
288
289 static gboolean
290 value_looks_true (const char *value)
291 {
292         if (value &&
293             (value[0] == 'T' ||
294              value[0] == 't' ||
295              value[0] == 'Y' ||
296              value[0] == 'y' ||
297              atoi (value) != 0)) {
298                 return TRUE;
299         } else {
300                 return FALSE;
301         }
302 }
303
304 static gboolean
305 get_bool_value (const Application *application, const char *key,
306                 gboolean *got_key)
307 {
308         const char *value = peek_value (application, key);
309         if (got_key) {
310                 if (value != NULL)
311                         *got_key = TRUE;
312                 else
313                         *got_key = FALSE;
314         }
315         return value_looks_true (value);
316
317 }
318
319 static void
320 set_bool_value (Application *application, const char *key,
321                 gboolean value)
322 {
323         set_value (application, key, value ? "true" : "false");
324 }
325
326 static int
327 application_compare (Application *application1,
328                      Application *application2)
329 {
330         return strcmp (application1->app_id, application2->app_id);
331 }
332
333 static void
334 add_application_to_mime_type_table (Application *application,
335                                     const char *mime_type)
336 {
337         GList *application_list;
338         GHashTable *table;
339         char *old_key;
340
341         if (gnome_vfs_mime_type_is_supertype (mime_type))
342                 table = generic_mime_types;
343         else
344                 table = specific_mime_types;
345         
346         g_assert (table != NULL);
347
348         if (g_hash_table_lookup_extended (table, mime_type,
349                                           (gpointer *)&old_key,
350                                           (gpointer *)&application_list)) {
351                 /* Sorted order is important as we can then easily
352                  * uniquify the results */
353                 application_list = g_list_insert_sorted
354                         (application_list,
355                          application_ref (application),
356                          (GCompareFunc) application_compare);
357                 g_hash_table_insert (table, old_key, application_list);
358         } else {
359                 application_list = g_list_prepend (NULL,
360                                                    application_ref (application));
361                 g_hash_table_insert (table, g_strdup (mime_type), application_list);
362         }
363 }
364
365 static void
366 add_mime_type_to_application (Application *application, const char *mime_type)
367 {
368         /* if this exists already, just return */
369         if (g_list_find_custom (application->mime_types,
370                                 /*glib is const incorrect*/(gpointer)mime_type,
371                                 (GCompareFunc) strcmp) != NULL)
372                 return;
373         
374         application->mime_types =
375                 g_list_prepend (application->mime_types,
376                                 g_strdup (mime_type));
377         
378         add_application_to_mime_type_table (application, mime_type);
379
380 }
381
382 static void
383 add_supported_uri_scheme_to_application (Application *application,
384                                          const char *supported_uri_scheme)
385 {
386         if (g_list_find_custom (application->supported_uri_schemes,
387                                 /*glib is const incorrect*/(gpointer) supported_uri_scheme,
388                                 (GCompareFunc) strcmp) != NULL) {
389                 return;
390         }
391         
392         application->supported_uri_schemes =
393                 g_list_prepend (application->supported_uri_schemes,
394                                 g_strdup (supported_uri_scheme));
395
396 }
397
398 static GList *
399 supported_uri_scheme_list_copy (GList *supported_uri_schemes)
400 {
401         GList *copied_list, *node;
402
403         copied_list = NULL;
404         for (node = supported_uri_schemes; node != NULL;
405              node = node->next) {
406                 copied_list = g_list_prepend (copied_list,
407                                               g_strdup ((char *) node->data));
408         }
409
410         return copied_list;
411 }
412
413 static void
414 remove_application_from_mime_type_table (Application *application,
415                                          const char *mime_type)
416 {
417         GHashTable *table;
418         char *old_key;
419         GList *application_list, *entry;
420
421         if (gnome_vfs_mime_type_is_supertype (mime_type))
422                 table = generic_mime_types;
423         else
424                 table = specific_mime_types;
425
426         g_assert (table != NULL);
427
428         if (g_hash_table_lookup_extended (table, mime_type,
429                                           (gpointer *)&old_key,
430                                           (gpointer *)&application_list)) {
431                 entry = g_list_find (application_list, application);
432
433                 /* if this fails we're in deep doodoo I guess */
434                 g_assert (entry != NULL);
435
436                 application_list = g_list_remove_link (application_list, entry);
437                 entry->data = NULL;
438                 application_unref (application);
439
440                 if (application_list != NULL) {
441                         g_hash_table_insert (table, old_key, application_list);
442                 } else {
443                         g_hash_table_remove (table, old_key);
444                         g_free(old_key);
445                 }
446         } else
447                 g_assert_not_reached ();
448 }
449
450 static void
451 remove_mime_type_for_application (Application *application, const char *mime_type)
452 {
453         GList *entry;
454
455         g_return_if_fail(application != NULL);
456         g_return_if_fail(mime_type != NULL);
457
458         entry = g_list_find_custom
459                 (application->mime_types,
460                  /*glib is const incorrect*/(gpointer)mime_type,
461                  (GCompareFunc) strcmp);
462
463         /* if this doesn't exist, just return */
464         if (entry == NULL) {
465                 return;
466         }
467
468         remove_application_from_mime_type_table (application, mime_type);
469
470         /* Free data last, in case caller passed in mime_type string
471          * that was stored in this table.
472          */
473         application->mime_types =
474                 g_list_remove_link (application->mime_types, entry);
475         g_free (entry->data);
476         g_list_free_1 (entry);  
477 }
478
479
480 static void
481 application_clear_mime_types (Application *application)
482 {
483         g_return_if_fail (application != NULL);
484
485         while (application->mime_types)
486                 remove_mime_type_for_application (application, application->mime_types->data);
487 }
488
489 static void
490 application_remove (Application *application)
491 {
492         Application *main_application;
493
494         g_return_if_fail (application != NULL);
495
496         if (global_applications == NULL) {
497                 return;
498         }
499
500         main_application = application_lookup (application->app_id);
501         if (main_application == NULL) {
502                 return;
503         }
504
505         /* We make sure the mime types are killed even if the application
506          * entry lives after unreffing it */
507         application_clear_mime_types (application);
508
509         if (main_application == application) {
510                 if (application->user_application)
511                         application_clear_mime_types (application->user_application);
512
513                 g_hash_table_remove (global_applications, application->app_id);
514         } else {
515                 /* This must be a user application */
516                 g_assert (main_application->user_application == application);
517
518                 main_application->user_application = NULL;
519         }
520
521         application_unref (application);
522
523 }
524
525 static void
526 sync_key (gpointer key, gpointer value, gpointer user_data)
527 {
528         char *key_string = key;
529         char *value_string = value;
530         FILE *fp = user_data;
531
532         fprintf (fp, "\t%s=%s\n", key_string, value_string);
533 }
534
535 /* write an application to a file */
536 static void
537 application_sync (Application *application, FILE *fp)
538 {
539         GList *li;
540
541         g_return_if_fail (application != NULL);
542         g_return_if_fail (application->app_id != NULL);
543         g_return_if_fail (fp != NULL);
544
545         fprintf (fp, "%s\n", application->app_id);
546
547         if (application->keys != NULL)
548                 g_hash_table_foreach (application->keys, sync_key, fp);
549
550         if (application->mime_types != NULL) {
551                 char *separator;
552                 fprintf (fp, "\tmime_types=");
553                 separator = "";
554                 for (li = application->mime_types; li != NULL; li = li->next) {
555                         char *mime_type = li->data;
556                         fprintf (fp, "%s%s", separator, mime_type);
557                         separator = ",";
558                 }
559                 fprintf (fp, "\n");
560         }
561         fprintf (fp, "\n");
562 }
563
564
565 /* this gives us a number of the language in the current language list,
566    the higher the number the "better" the translation */
567 static int
568 language_level (const char *lang)
569 {
570         int i;
571         GList *li;
572
573         if (lang == NULL)
574                 return 0;
575
576         for (i = 1, li = current_lang; li != NULL; i++, li = g_list_next (li)) {
577                 if (strcmp ((const char *) li->data, lang) == 0)
578                         return i;
579         }
580
581         return -1;
582 }
583
584
585 static void
586 application_add_key (Application *application, const char *key,
587                      const char *lang, const char *value)
588 {
589         int lang_level;
590
591         g_return_if_fail (application != NULL);
592         g_return_if_fail (key != NULL);
593         g_return_if_fail (value != NULL);
594
595         if (strcmp (key, "mime_types") == 0 ||
596             strcmp (key, "supported_uri_schemes") == 0) {
597                 char *value_copy = g_strdup (value);
598                 char *next_value;
599                 /* FIXME: There used to be a check here for
600                    the value of "lang", but spamming
601                    the terminal about it is not really
602                    the right way to deal with that, nor
603                    is "MIME Types can't have languages, bad!"
604                    which is what was here before */
605                 next_value = strtok (value_copy, ", \t");
606                 while (next_value != NULL) {
607                         if (strcmp (key, "mime_types") == 0) {
608                                 add_mime_type_to_application (application, next_value);
609                         }
610                         else {
611                                 add_supported_uri_scheme_to_application (application, next_value);
612                         }
613                         next_value = strtok (NULL, ", \t");
614                 }
615                 g_free(value_copy);
616                 /* fall through so that we can store the values as keys too */
617         }
618         else if (strcmp (key, "expects_uris") == 0) {
619                 if (strcmp (value, "non-file") == 0) {
620                         application->expects_uris = GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS_FOR_NON_FILES;
621                 }
622                 else if (value_looks_true (value)) {
623                         application->expects_uris = GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS;
624                 }
625                 else {
626                         application->expects_uris = GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS;
627                 }
628         }
629
630         lang_level = language_level (lang);
631         /* wrong language completely */
632         if (lang_level < 0)
633                 return;
634
635         /* if we have some language defined and
636            if there was a previous_key */
637         if (lang_level > 0 &&
638             previous_key &&
639             /* our language level really sucks and the previous
640                translation was of better language quality so just
641                ignore us */
642             previous_key_lang_level > lang_level) {
643                 return;
644         }
645
646         set_value (application, key, value);
647
648         /* set this as the previous key */
649         g_free(previous_key);
650         previous_key = g_strdup(key);
651         previous_key_lang_level = lang_level;
652 }
653
654 typedef enum {
655         STATE_NONE,
656         STATE_LANG,
657         STATE_LOOKING_FOR_KEY,
658         STATE_ON_APPLICATION,
659         STATE_ON_KEY,
660         STATE_ON_VALUE
661 } ParserState;
662
663 /**
664  * strip_trailing_whitespace
665  *
666  * string
667  *
668  * strips the white space from a string.
669  *
670  */
671
672 static void
673 strip_trailing_whitespace (GString *string)
674 {
675         int i;
676
677         for (i = string->len - 1; i >= 0; i--) {
678                 if (!g_ascii_isspace (string->str[i]))
679                         break;
680         }
681
682         g_string_truncate (string, i + 1);
683 }
684
685 /**
686  * load_application_info_from
687  *
688  * filename:  Target filename to application info from.
689  * user_owned: if application is user owned or not.
690  *
691  * This function will load application info from a file and parse through the
692  * application loading the registry with the information contained in the file.
693  *
694  * 
695  **/
696
697 static void
698 load_application_info_from (const char *filename, gboolean user_owned)
699 {
700         FILE *fp;
701         gboolean in_comment, app_used;
702         GString *line;
703         int column, c;
704         ParserState state;
705         Application *application;
706         char *key;
707         char *lang;
708
709         fp = fopen (filename, "r");
710         if (fp == NULL)
711                 return;
712
713         in_comment = FALSE;
714         app_used = FALSE;
715         column = -1;
716         application = NULL;
717         key = NULL;
718         lang = NULL;
719         line = g_string_sized_new (120);
720         state = STATE_NONE;
721         
722         while ((c = getc_unlocked (fp)) != EOF){
723                 column++;
724                 if (c == '\r')
725                         continue;
726
727                 if (c == '#' && column == 0){           
728                         in_comment = TRUE;
729                         continue;
730                 }
731                 
732                 if (c == '\n'){
733                         in_comment = FALSE;
734                         column = -1;
735                         if (state == STATE_ON_APPLICATION) {
736
737                                 /* set previous key to nothing
738                                    for this mime type */
739                                 g_free(previous_key);
740                                 previous_key = NULL;
741                                 previous_key_lang_level = -1;
742
743                                 strip_trailing_whitespace (line);
744                                 application = application_lookup_or_create (line->str, user_owned);
745                                 app_used = FALSE;
746                                 g_string_assign (line, "");
747                                 state = STATE_LOOKING_FOR_KEY;
748                                 continue;
749                         }
750                         if (state == STATE_ON_VALUE){
751                                 app_used = TRUE;
752                                 application_add_key (application, key, lang, line->str);
753                                 g_string_assign (line, "");
754                                 g_free (key);
755                                 key = NULL;
756                                 g_free (lang);
757                                 lang = NULL;
758                                 state = STATE_LOOKING_FOR_KEY;
759                                 continue;
760                         }
761                         continue;
762                 }
763
764                 if (in_comment)
765                         continue;
766
767                 switch (state){
768                 case STATE_NONE:
769                         if (c != ' ' && c != '\t')
770                                 state = STATE_ON_APPLICATION;
771                         else
772                                 break;
773                         /* fall down */
774                         
775                 case STATE_ON_APPLICATION:
776                         if (c == ':'){
777                                 in_comment = TRUE;
778                                 break;
779                         }
780                         g_string_append_c (line, c);
781                         break;
782
783                 case STATE_LOOKING_FOR_KEY:
784                         if (c == '\t' || c == ' ')
785                                 break;
786
787                         if (c == '['){
788                                 state = STATE_LANG;
789                                 break;
790                         }
791
792                         if (column == 0){
793                                 state = STATE_ON_APPLICATION;
794                                 g_string_append_c (line, c);
795                                 break;
796                         }
797                         state = STATE_ON_KEY;
798                         /* falldown */
799
800                 case STATE_ON_KEY:
801                         if (c == '\\'){
802                                 c = getc (fp);
803                                 if (c == EOF)
804                                         break;
805                         }
806                         if (c == '='){
807                                 key = g_strdup (line->str);
808                                 g_string_assign (line, "");
809                                 state = STATE_ON_VALUE;
810                                 break;
811                         }
812                         g_string_append_c (line, c);
813                         break;
814
815                 case STATE_ON_VALUE:
816                         g_string_append_c (line, c);
817                         break;
818                         
819                 case STATE_LANG:
820                         if (c == ']'){
821                                 state = STATE_ON_KEY;      
822                                 if (line->str [0]){
823                                         g_free(lang);
824                                         lang = g_strdup(line->str);
825                                 } else {
826                                         in_comment = TRUE;
827                                         state = STATE_LOOKING_FOR_KEY;
828                                 }
829                                 g_string_assign (line, "");
830                                 break;
831                         }
832                         g_string_append_c (line, c);
833                         break;
834                 }
835         }
836
837         if (application){
838                 if (key && line->str [0])
839                         application_add_key (application, key, lang, line->str);
840                 else
841                         if ( ! app_used)
842                                 application_remove (application);
843         }
844
845         g_string_free (line, TRUE);
846         g_free (key);
847         g_free (lang);
848
849         /* free the previous_key stuff */
850         g_free(previous_key);
851         previous_key = NULL;
852         previous_key_lang_level = -1;
853
854         fclose (fp);
855
856         _gnome_vfs_file_date_tracker_start_tracking_file (registry_date_tracker, filename);
857 }
858
859 /**
860  * application_info_load
861  *
862  * source:
863  *
864  * 
865  */
866
867 static void
868 application_info_load (ApplicationRegistryDir *source)
869 {
870         DIR *dir;
871         struct dirent *dent;
872         const int extlen = sizeof (".applications") - 1;
873         char *filename;
874         struct stat s;
875
876         if (stat (source->dirname, &s) != -1)
877                 source->valid = TRUE;
878         else
879                 source->valid = FALSE;
880         
881         dir = opendir (source->dirname);
882         if (dir == NULL) {
883                 source->valid = FALSE;
884                 return;
885         }
886         if (source->system_dir) {
887                 filename = g_strconcat (source->dirname, "/gnome-vfs.applications", NULL);
888                 load_application_info_from (filename, FALSE /*user_owned*/);
889                 g_free (filename);
890         }
891
892         while ((dent = readdir (dir)) != NULL){
893                 
894                 int len = strlen (dent->d_name);
895
896                 if (len <= extlen)
897                         continue;
898                 if (strcmp (dent->d_name + len - extlen, ".applications"))
899                         continue;
900                 if (source->system_dir && strcmp (dent->d_name, "gnome-vfs.applications") == 0)
901                         continue;
902                 if ( ! source->system_dir && strcmp (dent->d_name, "user.applications") == 0)
903                         continue;
904                 filename = g_strconcat (source->dirname, "/", dent->d_name, NULL);
905                 load_application_info_from (filename, FALSE /*user_owned*/);
906                 g_free (filename);
907         }
908
909         if ( ! source->system_dir) {
910                 filename = g_strconcat (source->dirname, "/user.applications", NULL);
911                 /* Currently this is the only file that is "user owned".  It actually makes
912                  * sense.  Editting of other files from the API would be too complex */
913                 load_application_info_from (filename, TRUE /*user_owned*/);
914                 g_free (filename);
915         }
916         closedir (dir);
917
918         _gnome_vfs_file_date_tracker_start_tracking_file (registry_date_tracker, source->dirname);
919 }
920
921 /**
922  * load_application_info
923  *
924  * This function will load the registry for an application from disk.
925  **/
926
927 static void
928 load_application_info (void)
929 {
930         application_info_load (&gnome_registry_dir);
931         application_info_load (&user_registry_dir);
932 }
933
934 /**
935  * gnome_vfs_application_registry_init
936  *
937  * This function initializes the gnome-vfs application registry.  
938  **/
939
940 static void
941 gnome_vfs_application_registry_init (void)
942 {
943         if (gnome_vfs_application_registry_initialized)
944                 return;
945
946         registry_date_tracker = _gnome_vfs_file_date_tracker_new ();
947
948         /*
949          * The hash tables that store the mime keys.
950          */
951         global_applications = g_hash_table_new (g_str_hash, g_str_equal);
952         generic_mime_types  = g_hash_table_new (g_str_hash, g_str_equal);
953         specific_mime_types  = g_hash_table_new (g_str_hash, g_str_equal);
954         
955         current_lang = gnome_vfs_i18n_get_language_list ("LC_MESSAGES");
956
957         /*
958          * Setup the descriptors for the information loading
959          */
960
961         gnome_registry_dir.dirname = g_strdup (DATADIR "/application-registry");
962         gnome_registry_dir.system_dir = TRUE;
963         
964         user_registry_dir.dirname = g_strconcat (g_get_home_dir(), "/.gnome/application-info", NULL);
965         user_registry_dir.system_dir = FALSE;
966
967         /* Make sure user directory exists */
968         if (mkdir (user_registry_dir.dirname, 0700) &&
969             errno != EEXIST) {
970                 g_warning("Could not create per-user Gnome application-registry directory: %s",
971                           user_registry_dir.dirname);
972         }
973
974         /* Things have been initialized flag it as ready so that we can load
975          * the applications without attempting to reinitialize
976          */
977         gnome_vfs_application_registry_initialized = TRUE;
978
979         load_application_info ();
980 }
981
982 /*
983  * maybe_reload
984  *
985  * This function will initialize the registry in memory and then reloads the
986  *
987  */
988
989 static void
990 maybe_reload (void)
991 {
992         gnome_vfs_application_registry_init ();
993
994         if (!_gnome_vfs_file_date_tracker_date_has_changed (registry_date_tracker)) {
995                 return;
996         }
997         
998         gnome_vfs_application_registry_reload ();
999 }
1000
1001 /**
1002  * remove_apps
1003  *
1004  * key: 
1005  * value:
1006  * user_data:
1007  *
1008  * FIXME: I need a clearer explanation on what this does.
1009  *
1010  */
1011
1012 static gboolean
1013 remove_apps (gpointer key, gpointer value, gpointer user_data)
1014 {
1015         Application *application = value;
1016
1017         application_clear_mime_types (application);
1018
1019         application_unref (application);
1020         
1021         return TRUE;
1022 }
1023
1024 /**
1025  * gnome_vfs_application_registry_clear:
1026  *
1027  * This will wipe the registry clean removing everything from the registry.
1028  * This is different from gnome_vfs_application_registry_shutdown which will
1029  * actually delete the registry and leave it in an uninitialized state.
1030  *
1031  */
1032
1033 static void
1034 gnome_vfs_application_registry_clear (void)
1035 {
1036         if (global_applications != NULL)
1037                 g_hash_table_foreach_remove (global_applications, remove_apps, NULL);
1038 }
1039
1040 /**
1041  * gnome_vfs_application_registry_shutdown
1042  *
1043  * Synchronize gnome-vfs application registry data to disk, and free
1044  * resources.
1045  * 
1046  */
1047
1048 void
1049 gnome_vfs_application_registry_shutdown (void)
1050 {
1051         gnome_vfs_application_registry_clear ();
1052
1053         if (global_applications != NULL) {
1054                 g_hash_table_destroy (global_applications);
1055                 global_applications = NULL;
1056         }
1057
1058         if(generic_mime_types != NULL) {
1059                 g_hash_table_destroy (generic_mime_types);
1060                 generic_mime_types = NULL;
1061         }
1062
1063         if(specific_mime_types != NULL) {
1064                 g_hash_table_destroy (specific_mime_types);
1065                 specific_mime_types = NULL;
1066         }
1067
1068         _gnome_vfs_file_date_tracker_free (registry_date_tracker);
1069
1070         g_free(gnome_registry_dir.dirname);
1071         gnome_registry_dir.dirname = NULL;
1072         g_free(user_registry_dir.dirname);
1073         user_registry_dir.dirname = NULL;
1074
1075         g_list_free(current_lang);
1076         current_lang = NULL;
1077
1078         gnome_vfs_application_registry_initialized = FALSE;
1079 }
1080
1081
1082 /**
1083  * gnome_vfs_application_registry_reload
1084  *
1085  * If this function is called for the first time it will initialize the
1086  * registry.  Subsequent calls to the function will clear out the current
1087  * registry contents and load registry contents from the save file.  Make
1088  * certain that you've saved your registry before calling this function.  It
1089  * will destroy unsaved changes.
1090  *
1091  */
1092
1093 void
1094 gnome_vfs_application_registry_reload (void)
1095 {
1096         if ( ! gnome_vfs_application_registry_initialized) {
1097                 /* If not initialized, initialization will do a "reload" */
1098                 gnome_vfs_application_registry_init ();
1099         } else {
1100                 gnome_vfs_application_registry_clear ();
1101                 load_application_info ();
1102         }
1103 }
1104
1105 /*
1106  * Existance check
1107  */
1108
1109 /**
1110  * gnome_vfs_application_registry_exists
1111  * @app_id: an application ID
1112  *
1113  * This function will return TRUE if there is an entry for @app_id in
1114  * the registry, otherwise FALSE.
1115  *
1116  * Returns: TRUE if the application is in the registry, FALSE if not 
1117  *
1118  */
1119
1120 gboolean
1121 gnome_vfs_application_registry_exists (const char *app_id)
1122 {
1123         g_return_val_if_fail (app_id != NULL, FALSE);
1124
1125         maybe_reload ();
1126
1127         if (application_lookup (app_id) != NULL)
1128                 return TRUE;
1129         else
1130                 return FALSE;
1131 }
1132
1133
1134 /*
1135  * Getting arbitrary keys
1136  */
1137
1138
1139 /**
1140  * get_keys_foreach
1141  *
1142  * key:
1143  * user_data:
1144  *
1145  * FIXME: I have no idea what this function does.
1146  **/
1147
1148 static void
1149 get_keys_foreach(gpointer key, gpointer value, gpointer user_data)
1150 {
1151         GList **listp = user_data;
1152
1153         /* make sure we only insert unique keys */
1154         if ( (*listp) && strcmp ((const char *) (*listp)->data, (const char *) key) == 0)
1155                 return;
1156
1157         (*listp) = g_list_insert_sorted ((*listp), key,
1158                                          (GCompareFunc) strcmp);
1159 }
1160
1161 /**
1162  * gnome_vfs_application_registry_get_keys:
1163  * @app_id: the application ID for which to get keys
1164  * 
1165  * This function wil return a list of strings which is the list of
1166  * keys set for @app_id in the application registry.
1167  *
1168  * Returns: A list of the keys set for @app_id
1169  *
1170  */
1171
1172 GList *
1173 gnome_vfs_application_registry_get_keys (const char *app_id)
1174 {
1175         GList *retval;
1176         Application *application;
1177
1178         g_return_val_if_fail (app_id != NULL, NULL);
1179
1180         maybe_reload ();
1181
1182         application = application_lookup (app_id);
1183         if (application == NULL)
1184                 return NULL;
1185
1186         retval = NULL;
1187
1188         if (application->keys != NULL)
1189                 g_hash_table_foreach (application->keys, get_keys_foreach,
1190                                       &retval);
1191
1192         if (application->user_application != NULL &&
1193             application->user_application->keys)
1194                 g_hash_table_foreach (application->user_application->keys,
1195                                       get_keys_foreach, &retval);
1196
1197         return retval;
1198 }
1199
1200
1201 /**
1202  * real_peek_value:
1203  *
1204  * @application: registry application
1205  * @key: target key
1206  *
1207  * Returns: the value associated with the key, or NULL if there is no
1208  * associated value.
1209  *
1210  * This function looks and returns the value of the target key in the registry
1211  * application
1212  *
1213  */
1214
1215 static const char *
1216 real_peek_value (const Application *application, const char *key)
1217 {
1218         const char *retval;
1219
1220         g_return_val_if_fail (application != NULL, NULL);
1221         g_return_val_if_fail (key != NULL, NULL);
1222
1223         retval = NULL;
1224
1225         if (application->user_application)
1226                 retval = peek_value (application->user_application, key);
1227
1228         if (retval == NULL)
1229                 retval = peek_value (application, key);
1230
1231         return retval;
1232 }
1233
1234 /**
1235  * real_get_bool_value
1236  *
1237  * application:  Application structure
1238  * key: taget key
1239  * got_key: actual key stored in application if key exists.
1240  *
1241  * This function will try to determine whether a key exists in the application.
1242  * It first checks the user applications and then the system applications and
1243  * then returns whether the key exists and what the value is from the value of
1244  * got_key.
1245  *
1246  * Returns: gboolean
1247  **/
1248
1249 static gboolean
1250 real_get_bool_value (const Application *application, const char *key, gboolean *got_key)
1251 {
1252         gboolean sub_got_key, retval;
1253
1254         g_return_val_if_fail (application != NULL, FALSE);
1255         g_return_val_if_fail (key != NULL, FALSE);
1256
1257         sub_got_key = FALSE;
1258         retval = FALSE;
1259         if (application->user_application)
1260                 retval = get_bool_value (application->user_application, key,
1261                                          &sub_got_key);
1262
1263         if ( ! sub_got_key)
1264                 retval = get_bool_value (application, key, &sub_got_key);
1265
1266         if (got_key != NULL)
1267                 *got_key = sub_got_key;
1268
1269         return retval;
1270 }
1271
1272
1273 /**
1274  * gnome_vfs_application_registry_peek_value
1275  * @app_id: the application ID for which to look up a value
1276  * @key: the key to look up
1277  *
1278  * This will return the value associated with @key for @app_id in the
1279  * application registry. There is no need to free the return value.
1280  *
1281  * Returns: the value associated with the key, or NULL if there is no
1282  * associated value
1283  *
1284  */
1285
1286 const char *
1287 gnome_vfs_application_registry_peek_value (const char *app_id, const char *key)
1288 {
1289         Application *application;
1290
1291         g_return_val_if_fail (app_id != NULL, NULL);
1292         g_return_val_if_fail (key != NULL, NULL);
1293
1294         maybe_reload ();
1295
1296         application = application_lookup (app_id);
1297         if (application == NULL)
1298                 return NULL;
1299
1300         return real_peek_value (application, key);
1301 }
1302
1303 /**
1304  * gnome_vfs_application_registry_get_bool_value
1305  * @app_id:  registry id of the application
1306  * @key: key to look up
1307  * @got_key: TRUE if a setting was dound, otherwise FALSE
1308  *
1309  * This will look up a key in the structure pointed to by app_id and return the
1310  * boolean value of that key.  It will return false if there are no
1311  * applications associated with the app_id.
1312  *
1313  * Returns: TRUE if @key is set to "true" or "yes" for @app_id, otherwise FALSE
1314  *
1315  */
1316
1317 gboolean
1318 gnome_vfs_application_registry_get_bool_value (const char *app_id, const char *key,
1319                                                gboolean *got_key)
1320 {
1321         Application *application;
1322
1323         g_return_val_if_fail (app_id != NULL, FALSE);
1324         g_return_val_if_fail (key != NULL, FALSE);
1325
1326         maybe_reload ();
1327
1328         application = application_lookup (app_id);
1329         if (application == NULL)
1330                 return FALSE;
1331
1332         return real_get_bool_value (application, key, got_key);
1333 }
1334
1335 /*
1336  * Setting stuff
1337  */
1338
1339 /**
1340  * gnome_vfs_application_registry_remove_application
1341  * @app_id:  registry id of the application
1342  *
1343  * Given the registry id this function will remove all applications that has
1344  * been set by the user.  You will need to call
1345  * gnome_vfs_application_registry_sync to save the changes.
1346  *
1347  */
1348
1349 void
1350 gnome_vfs_application_registry_remove_application (const char *app_id)
1351 {
1352         Application *application;
1353
1354         g_return_if_fail (app_id != NULL);
1355
1356         maybe_reload ();
1357
1358         application = application_lookup (app_id);
1359         if (application == NULL)
1360                 return;
1361
1362         /* Only remove the user_owned stuff */
1363         if (application->user_owned) {
1364                 application_remove (application);
1365                 user_file_dirty = TRUE;
1366         } else if (application->user_application != NULL) {
1367                 application_remove (application->user_application);
1368                 user_file_dirty = TRUE;
1369         }
1370 }
1371
1372 /**
1373  * gnome_vfs_application_registry_set_value
1374  * @app_id:  registry id of the application
1375  * @key: target key
1376  * @value: value to set the target key to
1377  *
1378  * This function will set values pertaining to registry entry pointed to by
1379  * app_id.  You will need to call gnome_vfs_application_registry_sync to
1380  * realize the changes.
1381  *
1382  */
1383
1384 void
1385 gnome_vfs_application_registry_set_value (const char *app_id,
1386                                           const char *key,
1387                                           const char *value)
1388 {
1389         Application *application;
1390
1391         g_return_if_fail (app_id != NULL);
1392         g_return_if_fail (key != NULL);
1393         g_return_if_fail (value != NULL);
1394
1395         maybe_reload ();
1396
1397         application = application_lookup_or_create (app_id, TRUE/*user_owned*/);
1398
1399         set_value (application, key, value);
1400
1401         user_file_dirty = TRUE;
1402 }
1403
1404 /**
1405  * gnome_vfs_application_registry_set_bool_value:
1406  * @app_id:  registry id of the application
1407  * @key: target key
1408  * @value: value you want to set the target key to
1409  *
1410  * This function will modify those registry values that are of type boolean to
1411  * a value specified by the user.  You will need to call
1412  * gnome_vfs_application_registry_sync to save your changes.
1413  *
1414  */
1415 void
1416 gnome_vfs_application_registry_set_bool_value (const char *app_id,
1417                                                const char *key,
1418                                                gboolean value)
1419 {
1420         Application *application;
1421
1422         g_return_if_fail (app_id != NULL);
1423         g_return_if_fail (key != NULL);
1424
1425         maybe_reload ();
1426
1427         application = application_lookup_or_create (app_id, TRUE/*user_owned*/);
1428
1429         set_bool_value (application, key, value);
1430
1431         user_file_dirty = TRUE;
1432 }
1433
1434 /**
1435  * gnome_vfs_application_registry_unset_key:
1436  * @app_id: registry id of the application
1437  * @key: search key
1438  *
1439  * This function given the application and the target will wipe the current
1440  * value that the key contains.
1441  * 
1442  */
1443
1444 void
1445 gnome_vfs_application_registry_unset_key (const char *app_id,
1446                                           const char *key)
1447 {
1448         Application *application;
1449
1450         g_return_if_fail (app_id != NULL);
1451         g_return_if_fail (key != NULL);
1452
1453         maybe_reload ();
1454
1455         application = application_lookup_or_create (app_id, TRUE/*user_owned*/);
1456
1457         unset_key (application, key);
1458
1459         user_file_dirty = TRUE;
1460 }
1461
1462 /*
1463  * Query functions
1464  */
1465
1466
1467 static void
1468 cb_application_collect (gpointer key, gpointer value, gpointer user_data)
1469 {
1470         Application  *application = value;
1471         GList       **list = user_data;
1472         *list = g_list_prepend (*list, application->app_id);
1473 }
1474
1475 /**
1476  * gnome_vfs_application_registry_get_applications
1477  * @mime_type:  mime type string
1478  *
1479  * This will return all applications from the registry that are associated with
1480  * the given mime type string, if NULL it returns all applications.
1481  *
1482  * Returns: a list of the application IDs for all applications which
1483  * support the given mime type.
1484  *
1485  */
1486
1487 GList *
1488 gnome_vfs_application_registry_get_applications (const char *mime_type)
1489 {
1490         GList *app_list, *app_list2, *retval, *li;
1491         char *supertype;
1492
1493         retval = NULL;
1494         app_list2 = NULL;
1495
1496         maybe_reload ();
1497
1498         if (mime_type == NULL) {
1499                 g_hash_table_foreach (global_applications, cb_application_collect, &retval);
1500                 return retval;
1501         }
1502
1503         if (gnome_vfs_mime_type_is_supertype (mime_type)) {
1504                 app_list = g_hash_table_lookup (generic_mime_types, mime_type);
1505         } else {
1506                 app_list = g_hash_table_lookup (specific_mime_types, mime_type);
1507
1508                 supertype = gnome_vfs_get_supertype_from_mime_type (mime_type);
1509                 if (supertype != NULL) {
1510                         app_list2 = g_hash_table_lookup (generic_mime_types, supertype);
1511                         g_free (supertype);
1512                 }
1513         }
1514
1515         for (li = app_list; li != NULL; li = li->next) {
1516                 Application *application = li->data;
1517                 /* Note that this list is sorted so to kill duplicates
1518                  * in app_list we only need to check the first entry */
1519                 if (retval == NULL ||
1520                     strcmp ((const char *) retval->data, application->app_id) != 0)
1521                         retval = g_list_prepend (retval, application->app_id);
1522         }
1523
1524         for (li = app_list2; li != NULL; li = li->next) {
1525                 Application *application = li->data;
1526                 if (g_list_find_custom (retval, application->app_id,
1527                                         (GCompareFunc) strcmp) == NULL)
1528                         retval = g_list_prepend (retval, application->app_id);
1529         }
1530
1531         return retval;
1532 }
1533
1534 /**
1535  * gnome_vfs_application_registry_get_mime_types
1536  * @app_id: registry id of application
1537  *
1538  * This function returns a list of strings that represent the mime
1539  * types that can be handled by an application.
1540  *
1541  * Returns: a list of the mime types supported
1542  *
1543  */
1544
1545 GList *
1546 gnome_vfs_application_registry_get_mime_types (const char *app_id)
1547 {
1548         Application *application;
1549         GList *retval;
1550
1551         g_return_val_if_fail (app_id != NULL, NULL);
1552
1553         maybe_reload ();
1554
1555         application = application_lookup (app_id);
1556         if (application == NULL)
1557                 return NULL;
1558
1559         retval = g_list_copy (application->mime_types);
1560
1561         /* merge in the mime types from the user_application,
1562          * if it exists */
1563         if (application->user_application) {
1564                 GList *li;
1565                 for (li = application->user_application->mime_types;
1566                      li != NULL;
1567                      li = li->next) {
1568                         Application *application = li->data;
1569                         if (g_list_find_custom (retval, application->app_id,
1570                                                 (GCompareFunc) strcmp) == NULL)
1571                                 retval = g_list_prepend (retval,
1572                                                          application->app_id);
1573                 }
1574         }
1575
1576         return retval;
1577 }
1578
1579 /**
1580  * gnome_vfs_application_registry_supports_uri_scheme
1581  * @app_id: registry id of application
1582  * @uri_scheme: uri schme string
1583  *
1584  * Given the id of the application this function will determine if the
1585  * uri scheme will given is supported.
1586  *
1587  * Returns: TRUE if @app_id supports @uri_scheme, otherwise FALSE
1588  *
1589  */
1590
1591 gboolean
1592 gnome_vfs_application_registry_supports_uri_scheme (const char *app_id,
1593                                                     const char *uri_scheme)
1594 {
1595         Application *application;
1596         gboolean uses_gnomevfs;
1597         
1598         g_return_val_if_fail (app_id != NULL, FALSE);
1599         g_return_val_if_fail (uri_scheme != NULL, FALSE);
1600
1601         maybe_reload ();
1602
1603         application = application_lookup (app_id);
1604         if (application == NULL)
1605                 return FALSE;
1606
1607         uses_gnomevfs = real_get_bool_value (application, GNOME_VFS_APPLICATION_REGISTRY_USES_GNOMEVFS, NULL);
1608
1609         if (strcmp (uri_scheme, "file") == 0 &&
1610             uses_gnomevfs == FALSE &&
1611             application->supported_uri_schemes == NULL &&
1612             application->user_application->supported_uri_schemes == NULL) {
1613                 return TRUE;
1614         }
1615
1616         /* check both the application and the user application
1617          * mime_types lists */
1618         /* FIXME: This method does not allow a user to override and remove
1619            uri schemes that an application can handle.  Is this an issue? */
1620
1621         if ((g_list_find_custom (application->supported_uri_schemes,
1622                                  /*glib is const incorrect*/(gpointer)uri_scheme,
1623                                 (GCompareFunc) strcmp) != NULL) ||
1624             (application->user_application &&
1625              g_list_find_custom (application->user_application->supported_uri_schemes,
1626                                  /*glib is const incorrect*/(gpointer) uri_scheme,
1627                                  (GCompareFunc) strcmp) != NULL)) {
1628                 return TRUE;
1629         }
1630         /* check in the list of uris supported by gnome-vfs if necessary */
1631         else if (uses_gnomevfs) {
1632                 GList *supported_uris;
1633                 gboolean res;
1634                 
1635                 supported_uris = _gnome_vfs_configuration_get_methods_list();
1636                 res = (g_list_find_custom(supported_uris,
1637                                           /*glib is const incorrect*/(gpointer) uri_scheme,
1638                                           (GCompareFunc) strcmp) != NULL);
1639
1640                 g_list_foreach(supported_uris, (GFunc) g_free, NULL);
1641                 g_list_free(supported_uris);
1642
1643                 return res;
1644         }
1645
1646         return FALSE;
1647 }
1648
1649 /**
1650  * gnome_vfs_application_registry_supports_mime_type
1651  * @app_id: registry id of application
1652  * @mime_type: mime type string
1653  *
1654  * Use this function to see if there is an application associated with a given
1655  * mime type.  The function will return true or false.
1656  *
1657  * Returns: TRUE if @app_id supports @mime_type, otherwise FALSE.
1658  *
1659  */
1660
1661 gboolean
1662 gnome_vfs_application_registry_supports_mime_type (const char *app_id,
1663                                                    const char *mime_type)
1664 {
1665         Application *application;
1666
1667         g_return_val_if_fail (app_id != NULL, FALSE);
1668         g_return_val_if_fail (mime_type != NULL, FALSE);
1669
1670         maybe_reload ();
1671
1672         application = application_lookup (app_id);
1673         if (application == NULL)
1674                 return FALSE;
1675
1676         /* check both the application and the user application
1677          * mime_types lists */
1678         /* FIXME: This method does not allow a user to override and remove
1679            mime types that an application can handle.  Is this an issue? */
1680         if ((g_list_find_custom (application->mime_types,
1681                                  /*glib is const incorrect*/(gpointer)mime_type,
1682                                 (GCompareFunc) strcmp) != NULL) ||
1683             (application->user_application &&
1684              g_list_find_custom (application->user_application->mime_types,
1685                                  /*glib is const incorrect*/(gpointer)mime_type,
1686                                  (GCompareFunc) strcmp) != NULL))
1687                 return TRUE;
1688         else
1689                 return FALSE;
1690 }
1691
1692
1693 /*
1694  * Mime type functions
1695  * Note that mime_type can be a specific (image/png) or generic (image/<star>) type
1696  */
1697
1698
1699 /**
1700  * gnome_vfs_application_registry_clear_mime_types
1701  * @app_id: Application id
1702  *
1703  * This function will remove the mime types associated with the application.
1704  * Changes are not realized until the  gnome_vfs_application_registry_sync
1705  * function is called to save the changes to the file.
1706  *
1707  */
1708
1709 void
1710 gnome_vfs_application_registry_clear_mime_types (const char *app_id)
1711 {
1712         Application *application;
1713
1714         g_return_if_fail (app_id != NULL);
1715
1716         maybe_reload ();
1717
1718         application = application_lookup_or_create (app_id, TRUE/*user_owned*/);
1719
1720         application_clear_mime_types (application);
1721
1722         user_file_dirty = TRUE;
1723 }
1724
1725 /**
1726  * gnome_vfs_application_registry_add_mime_type
1727  * @app_id: registry id of application
1728  * @mime_type: mime type string
1729  *
1730  * This function will associate a mime type with an application given the   
1731  * application registry id and the mime type.  Changes are not realized until
1732  * the gnome_vfs_application_registry_sync function is called to save the
1733  * changes to the file.
1734  *
1735  */
1736
1737 void
1738 gnome_vfs_application_registry_add_mime_type (const char *app_id,
1739                                               const char *mime_type)
1740 {
1741         Application *application;
1742
1743         g_return_if_fail (app_id != NULL);
1744         g_return_if_fail (mime_type != NULL);
1745
1746         maybe_reload ();
1747
1748         application = application_lookup_or_create (app_id, TRUE/*user_owned*/);
1749
1750         add_mime_type_to_application (application, mime_type);
1751
1752         user_file_dirty = TRUE;
1753 }
1754
1755 /**
1756  * gnome_vfs_application_registry_remove_mime_type
1757  * @app_id: registry id of the application
1758  * @mime_type: mime type string
1759  *
1760  * This function will de-associate a mime type from an application registry.  
1761  * Given the application registry id and the mime type.  Changes are not
1762  * realized until the gnome_vfs_application_registry_sync function is called to
1763  * save the changes to the file.
1764  *
1765  */
1766
1767 void
1768 gnome_vfs_application_registry_remove_mime_type (const char *app_id,
1769                                                  const char *mime_type)
1770 {
1771         Application *application;
1772
1773         g_return_if_fail (app_id != NULL);
1774
1775         maybe_reload ();
1776
1777         application = application_lookup_or_create (app_id, TRUE/*user_owned*/);
1778
1779         remove_mime_type_for_application (application, mime_type);
1780
1781         user_file_dirty = TRUE;
1782 }
1783
1784 /*
1785  * Syncing to disk
1786  */
1787
1788 static void
1789 application_sync_foreach (gpointer key, gpointer value, gpointer user_data)
1790 {
1791         Application *application = value;
1792         FILE *fp = user_data;
1793
1794         /* Only sync things that are user owned */
1795         if (application->user_owned)
1796                 application_sync (application, fp);
1797         else if (application->user_application)
1798                 application_sync (application->user_application, fp);
1799 }
1800
1801 /**
1802  * gnome_vfs_application_registry_sync
1803  *
1804  * This function will sync the registry.  Typically you would use this function
1805  * after a modification of the registry.  When you modify the registry a dirty
1806  * flag is set.  Calling this function will save your modifications to disk and
1807  * reset the flag.
1808  *
1809  * If successful, will return GNOME_VFS_OK
1810  *
1811  * Returns: GnomeVFSResult
1812  *
1813  */
1814
1815 GnomeVFSResult
1816 gnome_vfs_application_registry_sync (void)
1817 {
1818         FILE *fp;
1819         char *file;
1820         time_t curtime;
1821
1822         if ( ! user_file_dirty)
1823                 return GNOME_VFS_OK;
1824
1825         maybe_reload ();
1826
1827         file = g_strconcat (user_registry_dir.dirname, "/user.applications", NULL);
1828         fp = fopen (file, "w");
1829
1830         if ( ! fp) {
1831                 g_warning ("Cannot open '%s' for writing", file);
1832                 g_free (file);
1833                 return gnome_vfs_result_from_errno ();
1834         }
1835
1836         g_free (file);
1837
1838         time(&curtime);
1839
1840         fprintf (fp, "# This file is automatically generated by gnome-vfs "
1841                  "application registry\n"
1842                  "# Do NOT edit by hand\n# Generated: %s\n",
1843                  ctime (&curtime));
1844
1845         if (global_applications != NULL)
1846                 g_hash_table_foreach (global_applications, application_sync_foreach, fp);
1847
1848         fclose (fp);
1849
1850         user_file_dirty = FALSE;
1851
1852         return GNOME_VFS_OK;
1853 }
1854
1855
1856 /**
1857  * gnome_vfs_application_registry_get_mime_application
1858  * @app_id:  registry id of the application
1859  *
1860  * Returns a structure that contains the application that handles
1861  * the mime type associated by the application referred by app_id.
1862  *
1863  * Returns: GnomeVFSMimeApplication
1864  *
1865  */
1866
1867 GnomeVFSMimeApplication *
1868 gnome_vfs_application_registry_get_mime_application (const char *app_id)
1869 {
1870         Application *i_application;
1871         GnomeVFSMimeApplication *application;
1872         gboolean uses_gnomevfs = FALSE;
1873
1874         g_return_val_if_fail (app_id != NULL, NULL);
1875
1876         maybe_reload ();
1877
1878         i_application = application_lookup (app_id);
1879
1880         if (i_application == NULL)
1881                 return NULL;
1882
1883         application = g_new0 (GnomeVFSMimeApplication, 1);
1884
1885         application->id = g_strdup (app_id);
1886
1887         application->name =
1888                 g_strdup (real_peek_value
1889                           (i_application,
1890                            GNOME_VFS_APPLICATION_REGISTRY_NAME));
1891         application->command =
1892                 g_strdup (real_peek_value
1893                           (i_application,
1894                            GNOME_VFS_APPLICATION_REGISTRY_COMMAND));
1895
1896         application->can_open_multiple_files =
1897                 real_get_bool_value
1898                         (i_application,
1899                          GNOME_VFS_APPLICATION_REGISTRY_CAN_OPEN_MULTIPLE_FILES,
1900                          NULL);
1901         application->expects_uris = i_application->expects_uris;
1902         application->supported_uri_schemes = 
1903                 supported_uri_scheme_list_copy (i_application->supported_uri_schemes);
1904
1905         application->requires_terminal =
1906                 real_get_bool_value
1907                         (i_application,
1908                          GNOME_VFS_APPLICATION_REGISTRY_REQUIRES_TERMINAL,
1909                          NULL);
1910
1911         uses_gnomevfs = real_get_bool_value (i_application, GNOME_VFS_APPLICATION_REGISTRY_USES_GNOMEVFS, NULL);
1912
1913         if (uses_gnomevfs) {
1914                 GList *methods_list = 
1915                         _gnome_vfs_configuration_get_methods_list();
1916                 GList *l;
1917                 if (application->expects_uris == GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS) {
1918                         application->expects_uris = GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS;
1919                 }
1920                 for (l = methods_list; l != NULL; l = l->next) {
1921                         if (g_list_find_custom (application->supported_uri_schemes,
1922                                                 /*glib is const incorrect*/(gpointer) l->data,
1923                                                 (GCompareFunc) strcmp) == NULL) {
1924                                 application->supported_uri_schemes = 
1925                                         g_list_prepend(application->supported_uri_schemes, 
1926                                                        l->data);
1927                         } else {
1928                                 g_free(l->data);
1929                         }                       
1930                 }
1931                 g_list_free(methods_list);
1932         }       
1933
1934         return application;
1935 }
1936
1937 /**
1938  * gnome_vfs_application_registry_save_mime_application
1939  * @application: application associated with the mime type
1940  * 
1941  * This will save to the registry the application that will be associated with
1942  * a defined mime type.  The defined mime type is located within the
1943  * GnomeVFSMimeApplication structure.  Changes are not realized until the
1944  * gnome_vfs_application_registry_sync function is called.
1945  *
1946  */
1947
1948 void
1949 gnome_vfs_application_registry_save_mime_application (const GnomeVFSMimeApplication *application)
1950 {
1951         Application *i_application;
1952
1953         g_return_if_fail (application != NULL);
1954
1955         /* make us a new user application */
1956         i_application = application_lookup_or_create (application->id, TRUE);
1957
1958         application_ref (i_application);
1959
1960         set_value (i_application, GNOME_VFS_APPLICATION_REGISTRY_NAME,
1961                    application->name);
1962         set_value (i_application, GNOME_VFS_APPLICATION_REGISTRY_COMMAND,
1963                    application->command);
1964         set_bool_value (i_application, GNOME_VFS_APPLICATION_REGISTRY_CAN_OPEN_MULTIPLE_FILES,
1965                         application->can_open_multiple_files);
1966         i_application->expects_uris = application->expects_uris;
1967         set_bool_value (i_application, GNOME_VFS_APPLICATION_REGISTRY_REQUIRES_TERMINAL,
1968                         application->requires_terminal);
1969         /* FIXME: Need to save supported_uri_schemes information */
1970         user_file_dirty = TRUE;
1971 }
1972
1973 /**
1974  * gnome_vfs_application_is_user_owned_application
1975  * @application:  data structure of the mime application
1976  *
1977  * This function will determine if a mime application is user owned or not.  By
1978  * user ownered this means that the application is not a system application
1979  * located in the prerequisite /usr area but rather in the user's area.
1980  *
1981  * Returns: gboolean
1982  */
1983
1984 gboolean
1985 gnome_vfs_application_is_user_owned_application (const GnomeVFSMimeApplication *application)
1986 {
1987         Application *i_application;
1988
1989         g_return_val_if_fail (application != NULL, FALSE);
1990
1991         /* make us a new user application */
1992         i_application = g_hash_table_lookup (global_applications, application->id);
1993         if (i_application != NULL) {
1994                 return i_application->user_owned;
1995         }
1996         
1997         return FALSE;
1998 }
1999