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-mime-handlers.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-mime-handlers.c - Mime type handlers for the GNOME Virtual
3    File System.
4
5    Copyright (C) 2000 Eazel, Inc.
6
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11
12    The Gnome Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public
18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.
21
22    Author: Maciej Stachowiak <mjs@eazel.com> */
23
24 #include <config.h>
25 #include "gnome-vfs-mime-handlers.h"
26
27 #include "gnome-vfs-application-registry.h"
28 #include "gnome-vfs-mime-info.h"
29 #include "gnome-vfs-mime.h"
30 #include "gnome-vfs-result.h"
31 #include "gnome-vfs-private-utils.h"
32 #include "gnome-vfs-utils.h"
33 #include <bonobo-activation/bonobo-activation-activate.h>
34 #include <gconf/gconf-client.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #define GCONF_DEFAULT_VIEWER_EXEC_PATH   "/desktop/gnome/applications/component_viewer/exec"
39
40
41 static GnomeVFSResult expand_parameters                          (gpointer                  action,
42                                                                   GnomeVFSMimeActionType    type,
43                                                                   GList                    *uris,
44                                                                   int                      *argc,
45                                                                   char                   ***argv);
46 static GList *        Bonobo_ServerInfoList_to_ServerInfo_g_list (Bonobo_ServerInfoList    *info_list);
47 static GList *        copy_str_list                              (GList                    *string_list);
48 static GList *        comma_separated_str_to_str_list            (const char               *str);
49 static GList *        str_list_difference                        (GList                    *a,
50                                                                   GList                    *b);
51 static char *         str_list_to_comma_separated_str            (GList                    *list);
52 static void           g_list_free_deep                           (GList                    *list);
53 static GList *        prune_ids_for_nonexistent_applications     (GList                    *list);
54 static GnomeVFSResult gnome_vfs_mime_edit_user_file              (const char               *mime_type,
55                                                                   const char               *key,
56                                                                   const char               *value);
57 static gboolean       application_known_to_be_nonexistent        (const char               *application_id);
58 static const char    *gnome_vfs_mime_maybe_get_user_level_value  (const char               *mime_type,
59                                                                   const char               *key);
60
61
62 /**
63  * gnome_vfs_mime_get_description:
64  * @mime_type: the mime type
65  *
66  * Query the MIME database for a description of the specified MIME type.
67  *
68  * Return value: A description of MIME type @mime_type
69  */
70 const char *
71 gnome_vfs_mime_get_description (const char *mime_type)
72 {
73         return gnome_vfs_mime_get_value (mime_type, "description");
74 }
75
76 /**
77  * gnome_vfs_mime_set_description:
78  * @mime_type: A const char * containing a mime type
79  * @description: A description of this MIME type
80  * 
81  * Set the description of this MIME type in the MIME database. The description
82  * should be something like "Gnumeric spreadsheet".
83  * 
84  * Return value: GnomeVFSResult indicating the success of the operation or any
85  * errors that may have occurred.
86  **/
87 GnomeVFSResult
88 gnome_vfs_mime_set_description (const char *mime_type, const char *description)
89 {
90         return gnome_vfs_mime_edit_user_file
91                 (mime_type, "description", description);
92 }
93
94 /**
95  * gnome_vfs_mime_get_default_action_type:
96  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
97  * 
98  * Query the MIME database for the type of action to be performed on a particular MIME type by default.
99  * 
100  * Return value: The type of action to be performed on a file of 
101  * MIME type, @mime_type by default.
102  **/
103 GnomeVFSMimeActionType
104 gnome_vfs_mime_get_default_action_type (const char *mime_type)
105 {
106         const char *action_type_string;
107
108         action_type_string = gnome_vfs_mime_get_value (mime_type, "default_action_type");
109
110         if (action_type_string != NULL && g_ascii_strcasecmp (action_type_string, "application") == 0) {
111                 return GNOME_VFS_MIME_ACTION_TYPE_APPLICATION;
112         } else if (action_type_string != NULL && g_ascii_strcasecmp (action_type_string, "component") == 0) {
113                 return GNOME_VFS_MIME_ACTION_TYPE_COMPONENT;
114         } else {
115                 return GNOME_VFS_MIME_ACTION_TYPE_NONE;
116         }
117
118 }
119
120 /**
121  * gnome_vfs_mime_get_default_action:
122  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
123  * 
124  * Query the MIME database for default action associated with a particular MIME type @mime_type.
125  * 
126  * Return value: A GnomeVFSMimeAction representing the default action to perform upon
127  * file of type @mime_type.
128  **/
129 GnomeVFSMimeAction *
130 gnome_vfs_mime_get_default_action (const char *mime_type)
131 {
132         GnomeVFSMimeAction *action;
133
134         action = g_new0 (GnomeVFSMimeAction, 1);
135
136         action->action_type = gnome_vfs_mime_get_default_action_type (mime_type);
137
138         switch (action->action_type) {
139         case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION:
140                 action->action.application = 
141                         gnome_vfs_mime_get_default_application (mime_type);
142                 if (action->action.application == NULL) {
143                         g_free (action);
144                         action = NULL;
145                 }
146                 break;
147         case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT:
148                 action->action.component = 
149                         gnome_vfs_mime_get_default_component (mime_type);
150                 if (action->action.component == NULL) {
151                         g_free (action);
152                         action = NULL;
153                 }
154                 break;
155         case GNOME_VFS_MIME_ACTION_TYPE_NONE:
156                 g_free (action);
157                 action = NULL;
158                 break;
159         default:
160                 g_assert_not_reached ();
161         }
162
163         return action;
164 }
165
166 /**
167  * gnome_vfs_mime_get_default_application:
168  * @mime_type: A const char * containing a mime type, e.g. "image/png"
169  * 
170  * Query the MIME database for the application to be executed on files of MIME type
171  * @mime_type by default.
172  * 
173  * Return value: A GnomeVFSMimeApplication representing the default handler of @mime_type
174  **/
175 GnomeVFSMimeApplication *
176 gnome_vfs_mime_get_default_application (const char *mime_type)
177 {
178         const char *default_application_id;
179         GnomeVFSMimeApplication *default_application;
180         GList *short_list;
181
182         default_application = NULL;
183
184         /* First, try the default for the mime type */
185         default_application_id = gnome_vfs_mime_get_value
186                 (mime_type, "default_application_id");
187
188         if (default_application_id != NULL
189             && default_application_id[0] != '\0'
190             && !application_known_to_be_nonexistent (default_application_id)) {
191                 default_application =
192                         gnome_vfs_application_registry_get_mime_application (default_application_id);
193         }
194
195         if (default_application == NULL) {
196                 /* Failing that, try something from the short list */
197
198                 short_list = gnome_vfs_mime_get_short_list_applications (mime_type);
199
200                 if (short_list != NULL) {
201                         default_application = gnome_vfs_mime_application_copy
202                                 ((GnomeVFSMimeApplication *) (short_list->data));
203                         gnome_vfs_mime_application_list_free (short_list);
204                 }
205         }
206
207
208         return default_application;
209 }
210
211 /**
212  * gnome_vfs_mime_get_icon:
213  * @mime_type: A const char * containing a  MIME type
214  *
215  * Query the MIME database for an icon representing the specified MIME type.
216  *
217  * Return value: The filename of the icon as listed in the MIME database. This is
218  * usually a filename without path information, e.g. "i-chardev.png", and sometimes
219  * does not have an extension, e.g. "i-regular" if the icon is supposed to be image
220  * type agnostic between icon themes. Icons are generic, and not theme specific. These
221  * will not necessarily match with the icons a user sees in Nautilus, you have been warned.
222  */
223 const char *
224 gnome_vfs_mime_get_icon (const char *mime_type)
225 {
226         return gnome_vfs_mime_get_value (mime_type, "icon_filename");
227 }
228
229 /**
230  * gnome_vfs_mime_set_icon:
231  * @mime_type: A const char * containing a  MIME type
232  * @filename: a const char * containing an image filename
233  *
234  * Set the icon entry for a particular MIME type in the MIME database. Note that
235  * icon entries need not necessarily contain the full path, and do not necessarily need to
236  * specify an extension. So "i-regular", "my-special-icon.png", and "some-icon"
237  * are all valid icon filenames.
238  *
239  * Return value: A GnomeVFSResult indicating the success of the operation
240  * or any errors that may have occurred.
241  */
242 GnomeVFSResult
243 gnome_vfs_mime_set_icon (const char *mime_type, const char *filename)
244 {
245         return gnome_vfs_mime_edit_user_file
246                 (mime_type, "icon_filename", filename);
247 }
248
249
250 /**
251  * gnome_vfs_mime_can_be_executable:
252  * @mime_type: A const char * containing a mime type
253  * 
254  * Check whether files of this MIME type might conceivably be executable.
255  * Default for known types if FALSE. Default for unknown types is TRUE.
256  * 
257  * Return value: gboolean containing TRUE if some files of this MIME type
258  * are registered as being executable, and false otherwise.
259  **/
260 gboolean
261 gnome_vfs_mime_can_be_executable (const char *mime_type)
262 {
263         const char *result_as_string;
264         gboolean result;
265         
266         result_as_string = gnome_vfs_mime_get_value (mime_type, "can_be_executable");
267         if (result_as_string != NULL) {
268                 result = strcmp (result_as_string, "TRUE") == 0;
269         } else {
270                 /* If type is not known, we treat it as potentially executable.
271                  * If type is known, we use default value of not executable.
272                  */
273                 result = !gnome_vfs_mime_type_is_known (mime_type);
274         }
275
276         return result;
277 }
278
279 /**
280  * gnome_vfs_mime_set_can_be_executable:
281  * @mime_type: A const char * containing a mime type
282  * @new_value: A boolean value indicating whether @mime_type could be executable.
283  * 
284  * Set whether files of this MIME type might conceivably be executable.
285  * 
286  * Return value: GnomeVFSResult indicating the success of the operation or any
287  * errors that may have occurred.
288  **/
289 GnomeVFSResult
290 gnome_vfs_mime_set_can_be_executable (const char *mime_type, gboolean new_value)
291 {
292         return gnome_vfs_mime_edit_user_file
293                 (mime_type, "can_be_executable", new_value ? "TRUE" : "FALSE");
294 }
295
296 /**
297  * gnome_vfs_mime_get_default_component:
298  * @mime_type: A const char * containing a mime type, e.g. "image/png"
299  * 
300  * Query the MIME database for the default Bonobo component to be activated to 
301  * view files of MIME type @mime_type.
302  * 
303  * Return value: An Bonobo_ServerInfo * representing the OAF server to be activated
304  * to get a reference to the proper component.
305  **/
306 Bonobo_ServerInfo *
307 gnome_vfs_mime_get_default_component (const char *mime_type)
308 {
309         const char *default_component_iid;
310         Bonobo_ServerInfoList *info_list;
311         Bonobo_ServerInfo *default_component;
312         CORBA_Environment ev;
313         char *supertype;
314         char *query;
315         char *sort[6];
316         GList *short_list;
317         GList *p;
318         char *prev;
319
320         if (mime_type == NULL) {
321                 return NULL;
322         }
323
324         CORBA_exception_init (&ev);
325
326         supertype = gnome_vfs_get_supertype_from_mime_type (mime_type);
327
328         /* Find a component that supports either the exact mime type,
329            the supertype, or all mime types. */
330
331         /* First try the component specified in the mime database, if available. 
332            gnome_vfs_mime_get_value looks up the value for the mime type and the supertype.  */
333         default_component_iid = gnome_vfs_mime_get_value
334                 (mime_type, "default_component_iid");
335
336         query = g_strconcat ("bonobo:supported_mime_types.has_one (['", mime_type, 
337                              "', '", supertype,
338                              "', '*'])", NULL);
339
340
341         if (default_component_iid != NULL) {
342                 sort[0] = g_strconcat ("iid == '", default_component_iid, "'", NULL);
343         } else {
344                 sort[0] = g_strdup ("true");
345         }
346
347         short_list = gnome_vfs_mime_get_short_list_components (mime_type);
348         short_list = g_list_concat (short_list,
349                                     gnome_vfs_mime_get_short_list_components (supertype));
350         if (short_list != NULL) {
351                 sort[1] = g_strdup ("prefer_by_list_order(iid, ['");
352
353                 for (p = short_list; p != NULL; p = p->next) {
354                         prev = sort[1];
355                         
356                         if (p->next != NULL) {
357                                 sort[1] = g_strconcat (prev, ((Bonobo_ServerInfo *) (p->data))->iid, 
358                                                                     "','", NULL);
359                         } else {
360                                 sort[1] = g_strconcat (prev, ((Bonobo_ServerInfo *) (p->data))->iid, 
361                                                                     "'])", NULL);
362                         }
363                         g_free (prev);
364                 }
365                 gnome_vfs_mime_component_list_free (short_list);
366         } else {
367                 sort[1] = g_strdup ("true");
368         }
369
370
371         /* Prefer something that matches the exact type to something
372            that matches the supertype */
373         sort[2] = g_strconcat ("bonobo:supported_mime_types.has ('", mime_type, "')", NULL);
374
375         /* Prefer something that matches the supertype to something that matches `*' */
376         sort[3] = g_strconcat ("bonobo:supported_mime_types.has ('", supertype, "')", NULL);
377
378         sort[4] = g_strdup ("name");
379         sort[5] = NULL;
380
381         info_list = bonobo_activation_query (query, sort, &ev);
382         
383         default_component = NULL;
384         if (ev._major == CORBA_NO_EXCEPTION) {
385                 if (info_list != NULL && info_list->_length > 0) {
386                         default_component = Bonobo_ServerInfo_duplicate (&info_list->_buffer[0]);
387                 }
388                 CORBA_free (info_list);
389         }
390
391         g_free (supertype);
392         g_free (query);
393         g_free (sort[0]);
394         g_free (sort[1]);
395         g_free (sort[2]);
396         g_free (sort[3]);
397         g_free (sort[4]);
398
399         CORBA_exception_free (&ev);
400
401         return default_component;
402 }
403
404 static GList *
405 gnome_vfs_mime_str_list_merge (GList *a, 
406                                GList *b)
407 {
408         GList *pruned_b;
409         GList *extended_a;
410         GList *a_copy;
411
412         pruned_b = str_list_difference (b, a);
413
414         a_copy = g_list_copy (a);
415         extended_a = g_list_concat (a_copy, pruned_b);
416
417         /* No need to free a_copy or
418          * pruned_b since they were concat()ed into
419          * extended_a 
420          */
421
422         return extended_a;
423 }
424
425
426 static GList *
427 gnome_vfs_mime_str_list_apply_delta (GList *list_to_process, 
428                                      GList *additions, 
429                                      GList *removals)
430 {
431         GList *extended_original_list;
432         GList *processed_list;
433
434         extended_original_list = gnome_vfs_mime_str_list_merge (list_to_process, additions);
435
436         processed_list = str_list_difference (extended_original_list, removals);
437         
438         g_list_free (extended_original_list);
439
440         return processed_list;
441 }
442
443 static GList *
444 gnome_vfs_mime_do_short_list_processing (GList *short_list, 
445                                          GList *additions,
446                                          GList *removals, 
447                                          GList *supertype_short_list, 
448                                          GList *supertype_additions, 
449                                          GList *supertype_removals)
450 {
451         GList *processed_supertype_list;
452         GList *merged_system_and_supertype;
453         GList *final_list;
454
455         processed_supertype_list = gnome_vfs_mime_str_list_apply_delta (supertype_short_list,
456                                                                         supertype_additions,
457                                                                         supertype_removals);
458
459         merged_system_and_supertype = gnome_vfs_mime_str_list_merge (short_list,
460                                                                      processed_supertype_list);
461
462         final_list = gnome_vfs_mime_str_list_apply_delta (merged_system_and_supertype,
463                                                           additions,
464                                                           removals);
465         
466         g_list_free (processed_supertype_list);
467         g_list_free (merged_system_and_supertype);
468
469         return final_list;
470 }
471
472
473 /* sort_application_list
474  *
475  * Sort list alphabetically
476  */
477  
478 static int
479 sort_application_list (gconstpointer a, gconstpointer b)
480 {
481         GnomeVFSMimeApplication *application1, *application2;
482
483         application1 = (GnomeVFSMimeApplication *) a;
484         application2 = (GnomeVFSMimeApplication *) b;
485
486         return g_ascii_strcasecmp (application1->name, application2->name);
487 }
488
489 /**
490  * gnome_vfs_mime_get_short_list_applications:
491  * @mime_type: A const char * containing a mime type, e.g. "image/png"
492  * 
493  * Return an alphabetically sorted list of GnomeVFSMimeApplication
494  * data structures for the requested mime type. The short list contains
495  * "select" applications recommended for handling this MIME type, appropriate for
496  * display to the user.
497  * 
498  * Return value: A GList * where the elements are GnomeVFSMimeApplication *
499  * representing various applications to display in the short list for @mime_type.
500  **/ 
501 GList *
502 gnome_vfs_mime_get_short_list_applications (const char *mime_type)
503 {
504         GList *system_short_list;
505         GList *short_list_additions;
506         GList *short_list_removals;
507         char *supertype;
508         GList *supertype_short_list;
509         GList *supertype_additions;
510         GList *supertype_removals;
511         GList *id_list;
512         GList *p;
513         GnomeVFSMimeApplication *application;
514         GList *preferred_applications;
515
516         if (mime_type == NULL) {
517                 return NULL;
518         }
519
520
521         system_short_list = comma_separated_str_to_str_list (gnome_vfs_mime_maybe_get_user_level_value
522                                                              (mime_type, 
523                                                               "short_list_application_ids"));
524         system_short_list = prune_ids_for_nonexistent_applications
525                 (system_short_list);
526
527         /* get user short list delta (add list and remove list) */
528
529         short_list_additions = comma_separated_str_to_str_list (gnome_vfs_mime_get_value
530                                                                 (mime_type,
531                                                                  "short_list_application_user_additions"));
532         short_list_additions = prune_ids_for_nonexistent_applications (short_list_additions);
533         short_list_removals = comma_separated_str_to_str_list (gnome_vfs_mime_get_value
534                                                                (mime_type,
535                                                                 "short_list_application_user_removals"));
536
537         /* Only include the supertype in the short list if we came up empty with
538            the specific types */
539         supertype = gnome_vfs_get_supertype_from_mime_type (mime_type);
540
541         if (!gnome_vfs_mime_type_is_supertype (mime_type) && system_short_list == NULL) {
542                 supertype_short_list = comma_separated_str_to_str_list 
543                         (gnome_vfs_mime_maybe_get_user_level_value
544                          (supertype, 
545                           "short_list_application_ids"));
546                 supertype_short_list = prune_ids_for_nonexistent_applications
547                         (supertype_short_list);
548
549                 /* get supertype short list delta (add list and remove list) */
550
551                 supertype_additions = comma_separated_str_to_str_list 
552                         (gnome_vfs_mime_get_value
553                          (supertype,
554                           "short_list_application_user_additions"));
555                 supertype_removals = comma_separated_str_to_str_list 
556                         (gnome_vfs_mime_get_value
557                          (supertype,
558                           "short_list_application_user_removals"));
559         } else {
560                 supertype_short_list = NULL;
561                 supertype_additions = NULL;
562                 supertype_removals = NULL;
563         }
564         g_free (supertype);
565
566
567         /* compute list modified by delta */
568
569         id_list = gnome_vfs_mime_do_short_list_processing (system_short_list, 
570                                                            short_list_additions,
571                                                            short_list_removals, 
572                                                            supertype_short_list, 
573                                                            supertype_additions, 
574                                                            supertype_removals);
575
576         preferred_applications = NULL;
577
578         for (p = id_list; p != NULL; p = p->next) {
579                 application = gnome_vfs_application_registry_get_mime_application (p->data);
580                 if (application != NULL) {
581                         preferred_applications = g_list_prepend
582                                 (preferred_applications, application);
583                 }
584         }
585
586
587         preferred_applications = g_list_reverse (preferred_applications);
588
589         g_list_free_deep (system_short_list);
590         g_list_free_deep (short_list_additions);
591         g_list_free_deep (short_list_removals);
592         g_list_free_deep (supertype_short_list);
593         g_list_free_deep (supertype_additions);
594         g_list_free_deep (supertype_removals);
595         g_list_free (id_list);
596
597         /* Sort list alphabetically by application name */
598         preferred_applications = g_list_sort (preferred_applications, sort_application_list);
599         
600         return preferred_applications;
601 }
602
603
604
605 static char *
606 join_str_list (const char *separator, GList *list)
607 {
608         char **strv;
609         GList *p;
610         int i;
611         char *retval;
612
613         /* Convert to a strv so we can use g_strjoinv.
614          * Saves code but could be made faster if we want.
615          */
616         strv = g_new0 (char *, g_list_length (list) + 1);
617         for (p = list, i = 0; p != NULL; p = p->next, i++) {
618                 strv[i] = (char *) p->data;
619         }
620         strv[i] = NULL;
621
622         retval = g_strjoinv (separator, strv);
623
624         g_free (strv);
625
626         return retval;
627 }
628
629
630 /**
631  * gnome_vfs_mime_get_short_list_components:
632  * @mime_type: A const char * containing a mime type, e.g. "image/png"
633  * 
634  * Return an unsorted sorted list of Bonobo_ServerInfo *
635  * data structures for the requested mime type. The short list contains
636  * "select" components recommended for handling this MIME type, appropriate for
637  * display to the user.
638  * 
639  * Return value: A GList * where the elements are Bonobo_ServerInfo *
640  * representing various components to display in the short list for @mime_type.
641  **/ 
642 GList *
643 gnome_vfs_mime_get_short_list_components (const char *mime_type)
644 {
645         GList *system_short_list;
646         GList *short_list_additions;
647         GList *short_list_removals;
648         char *supertype;
649         GList *supertype_short_list;
650         GList *supertype_additions;
651         GList *supertype_removals;
652         GList *iid_list;
653         char *query;
654         char *sort[2];
655         char *iids_delimited;
656         CORBA_Environment ev;
657         Bonobo_ServerInfoList *info_list;
658         GList *preferred_components;
659
660         if (mime_type == NULL) {
661                 return NULL;
662         }
663
664
665         /* get short list IIDs for that user level */
666         system_short_list = comma_separated_str_to_str_list (gnome_vfs_mime_get_value
667                                                              (mime_type, 
668                                                               "short_list_component_iids"));
669
670         /* get user short list delta (add list and remove list) */
671
672         short_list_additions = comma_separated_str_to_str_list (gnome_vfs_mime_get_value
673                                                                 (mime_type,
674                                                                  "short_list_component_user_additions"));
675         
676         short_list_removals = comma_separated_str_to_str_list (gnome_vfs_mime_get_value
677                                                                (mime_type,
678                                                                 "short_list_component_user_removals"));
679
680
681         supertype = gnome_vfs_get_supertype_from_mime_type (mime_type);
682         
683         if (strcmp (supertype, mime_type) != 0) {
684                 supertype_short_list = comma_separated_str_to_str_list 
685                         (gnome_vfs_mime_get_value
686                          (supertype, 
687                           "short_list_component_iids"));
688
689                 /* get supertype short list delta (add list and remove list) */
690
691                 supertype_additions = comma_separated_str_to_str_list 
692                         (gnome_vfs_mime_get_value
693                          (supertype,
694                           "short_list_component_user_additions"));
695                 supertype_removals = comma_separated_str_to_str_list 
696                         (gnome_vfs_mime_get_value
697                          (supertype,
698                           "short_list_component_user_removals"));
699         } else {
700                 supertype_short_list = NULL;
701                 supertype_additions = NULL;
702                 supertype_removals = NULL;
703         }
704
705         /* compute list modified by delta */
706
707         iid_list = gnome_vfs_mime_do_short_list_processing (system_short_list, 
708                                                             short_list_additions,
709                                                             short_list_removals, 
710                                                             supertype_short_list, 
711                                                             supertype_additions, 
712                                                             supertype_removals);
713
714
715
716         /* Do usual query but requiring that IIDs be one of the ones
717            in the short list IID list. */
718         
719         preferred_components = NULL;
720         if (iid_list != NULL) {
721                 CORBA_exception_init (&ev);
722
723                 iids_delimited = join_str_list ("','", iid_list);
724
725                 query = g_strconcat ("bonobo:supported_mime_types.has_one (['", mime_type, 
726                                      "', '", supertype,
727                                      "', '*'])",
728                                      " AND has(['", iids_delimited, "'], iid)", NULL);
729                 
730                 sort[0] = g_strconcat ("prefer_by_list_order(iid, ['", iids_delimited, "'])", NULL);
731                 sort[1] = NULL;
732                 
733                 info_list = bonobo_activation_query (query, sort, &ev);
734                 
735                 if (ev._major == CORBA_NO_EXCEPTION) {
736                         preferred_components = Bonobo_ServerInfoList_to_ServerInfo_g_list (info_list);
737                         CORBA_free (info_list);
738                 }
739
740                 g_free (iids_delimited);
741                 g_free (query);
742                 g_free (sort[0]);
743
744                 CORBA_exception_free (&ev);
745         }
746
747         g_free (supertype);
748         g_list_free_deep (system_short_list);
749         g_list_free_deep (short_list_additions);
750         g_list_free_deep (short_list_removals);
751         g_list_free_deep (supertype_short_list);
752         g_list_free_deep (supertype_additions);
753         g_list_free_deep (supertype_removals);
754         g_list_free (iid_list);
755
756         return preferred_components;
757 }
758
759
760 /**
761  * gnome_vfs_mime_get_all_applications:
762  * @mime_type: A const char * containing a mime type, e.g. "image/png"
763  * 
764  * Return an alphabetically sorted list of GnomeVFSMimeApplication
765  * data structures representing all applications in the MIME database registered
766  * to handle files of MIME type @mime_type (and supertypes).
767  * 
768  * Return value: A GList * where the elements are GnomeVFSMimeApplication *
769  * representing applications that handle MIME type @mime_type.
770  **/ 
771 GList *
772 gnome_vfs_mime_get_all_applications (const char *mime_type)
773 {
774         GList *applications, *node, *next;
775         char *application_id;
776         GnomeVFSMimeApplication *application;
777
778         g_return_val_if_fail (mime_type != NULL, NULL);
779
780         applications = gnome_vfs_application_registry_get_applications (mime_type);
781
782         /* We get back a list of const char *, but the prune function
783          * wants a list of strings that we own.
784          */
785         for (node = applications; node != NULL; node = node->next) {
786                 node->data = g_strdup (node->data);
787         }
788
789         /* Remove application ids representing nonexistent (not in path) applications */
790         applications = prune_ids_for_nonexistent_applications (applications);
791
792         /* Convert to GnomeVFSMimeApplication's (leaving out NULLs) */
793         for (node = applications; node != NULL; node = next) {
794                 next = node->next;
795
796                 application_id = node->data;
797                 application = gnome_vfs_application_registry_get_mime_application (application_id);
798
799                 /* Replace the application ID with the application */
800                 if (application == NULL) {
801                         applications = g_list_remove_link (applications, node);
802                         g_list_free_1 (node);
803                 } else {
804                         node->data = application;
805                 }
806
807                 g_free (application_id);
808         }
809
810         return applications;
811 }
812
813 /**
814  * gnome_vfs_mime_get_all_components:
815  * @mime_type: A const char * containing a mime type, e.g. "image/png"
816  * 
817  * Return an alphabetically sorted list of Bonobo_ServerInfo
818  * data structures representing all Bonobo components registered
819  * to handle files of MIME type @mime_type (and supertypes).
820  * 
821  * Return value: A GList * where the elements are Bonobo_ServerInfo *
822  * representing components that can handle MIME type @mime_type.
823  **/ 
824 GList *
825 gnome_vfs_mime_get_all_components (const char *mime_type)
826 {
827         Bonobo_ServerInfoList *info_list;
828         GList *components_list;
829         CORBA_Environment ev;
830         char *supertype;
831         char *query;
832         char *sort[2];
833
834         if (mime_type == NULL) {
835                 return NULL;
836         }
837
838         CORBA_exception_init (&ev);
839
840         /* Find a component that supports either the exact mime type,
841            the supertype, or all mime types. */
842
843         /* FIXME bugzilla.eazel.com 1142: should probably check for
844            the right interfaces too. Also slightly semantically
845            different from nautilus in other tiny ways.
846         */
847         supertype = gnome_vfs_get_supertype_from_mime_type (mime_type);
848         query = g_strconcat ("bonobo:supported_mime_types.has_one (['", mime_type, 
849                              "', '", supertype,
850                              "', '*'])", NULL);
851         g_free (supertype);
852         
853         /* Alphebetize by name, for the sake of consistency */
854         sort[0] = g_strdup ("name");
855         sort[1] = NULL;
856
857         info_list = bonobo_activation_query (query, sort, &ev);
858         
859         if (ev._major == CORBA_NO_EXCEPTION) {
860                 components_list = Bonobo_ServerInfoList_to_ServerInfo_g_list (info_list);
861                 CORBA_free (info_list);
862         } else {
863                 components_list = NULL;
864         }
865
866         g_free (query);
867         g_free (sort[0]);
868
869         CORBA_exception_free (&ev);
870
871         return components_list;
872 }
873
874 static GnomeVFSResult
875 gnome_vfs_mime_edit_user_file_full (const char *mime_type, GList *keys, GList *values)
876 {
877         GnomeVFSResult result;
878         GList *p, *q;
879         const char *key, *value;
880
881         if (mime_type == NULL) {
882                 return GNOME_VFS_OK;
883         }
884
885         result = GNOME_VFS_OK;
886
887         gnome_vfs_mime_freeze ();
888         for (p = keys, q = values; p != NULL && q != NULL; p = p->next, q = q->next) {
889                 key = p->data;
890                 value = q->data;
891                 if (value == NULL) {
892                         value = "";
893                 }
894                 gnome_vfs_mime_set_value (mime_type, key, value);
895         }
896         gnome_vfs_mime_thaw ();
897
898         return result;
899 }
900
901 static GnomeVFSResult
902 gnome_vfs_mime_edit_user_file_args (const char *mime_type, va_list args)
903 {
904         GList *keys, *values;
905         char *key, *value;
906         GnomeVFSResult result;
907
908         keys = NULL;
909         values = NULL;
910         for (;;) {
911                 key = va_arg (args, char *);
912                 if (key == NULL) {
913                         break;
914                 }
915                 value = va_arg (args, char *);
916                 keys = g_list_prepend (keys, key);
917                 values = g_list_prepend (values, value);
918         }
919
920         result = gnome_vfs_mime_edit_user_file_full (mime_type, keys, values);
921
922         g_list_free (keys);
923         g_list_free (values);
924
925         return result;
926 }
927
928 static GnomeVFSResult
929 gnome_vfs_mime_edit_user_file_multiple (const char *mime_type, ...)
930 {
931         va_list args;
932         GnomeVFSResult result;
933
934         va_start (args, mime_type);
935         result = gnome_vfs_mime_edit_user_file_args (mime_type, args);
936         va_end (args);
937
938         return result;
939 }
940
941 static GnomeVFSResult
942 gnome_vfs_mime_edit_user_file (const char *mime_type, const char *key, const char *value)
943 {
944         g_return_val_if_fail (key != NULL, GNOME_VFS_OK);
945         return gnome_vfs_mime_edit_user_file_multiple (mime_type, key, value, NULL);
946 }
947
948 /**
949  * gnome_vfs_mime_set_default_action_type:
950  * @mime_type: A const char * containing a mime type, e.g. "image/png"
951  * @action_type: A GnomeVFSMimeActionType containing the action to perform by default
952  * 
953  * Sets the default action type to be performed on files of MIME type @mime_type.
954  * 
955  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
956  * any errors encountered.
957  **/
958 GnomeVFSResult
959 gnome_vfs_mime_set_default_action_type (const char *mime_type,
960                                         GnomeVFSMimeActionType action_type)
961 {
962         const char *action_string;
963
964         switch (action_type) {
965         case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION:
966                 action_string = "application";
967                 break;          
968         case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT:
969                 action_string = "component";
970                 break;
971         case GNOME_VFS_MIME_ACTION_TYPE_NONE:
972         default:
973                 action_string = "none";
974         }
975
976         return gnome_vfs_mime_edit_user_file
977                 (mime_type, "default_action_type", action_string);
978 }
979
980 /**
981  * gnome_vfs_mime_set_default_application:
982  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
983  * @application_id: A key representing an application in the MIME database 
984  * (GnomeVFSMimeApplication->id, for example)
985  * 
986  * Sets the default application to be run on files of MIME type @mime_type.
987  * 
988  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
989  * any errors encountered.
990  **/
991 GnomeVFSResult
992 gnome_vfs_mime_set_default_application (const char *mime_type,
993                                         const char *application_id)
994 {
995         GnomeVFSResult result;
996
997         result = gnome_vfs_mime_edit_user_file
998                 (mime_type, "default_application_id", application_id);
999
1000         /* If there's no default action type, set it to match this. */
1001         if (result == GNOME_VFS_OK
1002             && application_id != NULL
1003             && gnome_vfs_mime_get_default_action_type (mime_type) == GNOME_VFS_MIME_ACTION_TYPE_NONE) {
1004                 result = gnome_vfs_mime_set_default_action_type (mime_type, GNOME_VFS_MIME_ACTION_TYPE_APPLICATION);
1005         }
1006
1007         return result;
1008 }
1009
1010 /**
1011  * gnome_vfs_mime_set_default_component:
1012  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1013  * @component_iid: The OAFIID of a component
1014  * 
1015  * Sets the default component to be activated for files of MIME type @mime_type.
1016  * 
1017  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1018  * any errors encountered.
1019  **/
1020 GnomeVFSResult
1021 gnome_vfs_mime_set_default_component (const char *mime_type,
1022                                       const char *component_iid)
1023 {
1024         GnomeVFSResult result;
1025
1026         result = gnome_vfs_mime_edit_user_file
1027                 (mime_type, "default_component_iid", component_iid);
1028
1029         /* If there's no default action type, set it to match this. */
1030         if (result == GNOME_VFS_OK
1031             && component_iid != NULL
1032             && gnome_vfs_mime_get_default_action_type (mime_type) == GNOME_VFS_MIME_ACTION_TYPE_NONE) {
1033                 gnome_vfs_mime_set_default_action_type (mime_type, GNOME_VFS_MIME_ACTION_TYPE_COMPONENT);
1034         }
1035
1036         return result;
1037 }
1038
1039 /**
1040  * gnome_vfs_mime_set_short_list_applications:
1041  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1042  * @application_ids: GList of const char * application ids
1043  * 
1044  * Set the short list of applications for the specified MIME type. The short list
1045  * contains applications recommended for possible selection by the user.
1046  * 
1047  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1048  * any errors encountered.
1049  **/
1050 GnomeVFSResult
1051 gnome_vfs_mime_set_short_list_applications (const char *mime_type,
1052                                             GList *application_ids)
1053 {
1054         char *addition_string, *removal_string;
1055         GList *short_list_id_list;
1056         GList *short_list_addition_list;
1057         GList *short_list_removal_list;
1058         GnomeVFSResult result;
1059         GList *it;
1060
1061         /* Get base list. */
1062         short_list_id_list = comma_separated_str_to_str_list
1063                 (gnome_vfs_mime_maybe_get_user_level_value (mime_type, "short_list_application_ids"));
1064
1065         /* Compute delta. */
1066         short_list_addition_list = str_list_difference (application_ids, short_list_id_list);
1067         short_list_removal_list = str_list_difference (short_list_id_list, application_ids);
1068         addition_string = str_list_to_comma_separated_str (short_list_addition_list);
1069         removal_string = str_list_to_comma_separated_str (short_list_removal_list);
1070
1071         /* Make sure the newly added app_ids are already associated to this 
1072          * mime type in the application registry
1073          */
1074         for (it = short_list_addition_list; it != NULL; it = it->next) {
1075                 /* add_mime_type won't do anything if mime_type is already
1076                  * associated with it->data
1077                  */
1078                 gnome_vfs_application_registry_add_mime_type (it->data, mime_type);             
1079         }
1080         gnome_vfs_application_registry_sync ();
1081
1082         g_list_free_deep (short_list_id_list);
1083         g_list_free (short_list_addition_list);
1084         g_list_free (short_list_removal_list);
1085
1086         /* Write it. */
1087         result = gnome_vfs_mime_edit_user_file_multiple
1088                 (mime_type,
1089                  "short_list_application_user_additions", addition_string,
1090                  "short_list_application_user_removals", removal_string,
1091                  NULL);
1092
1093         g_free (addition_string);
1094         g_free (removal_string);
1095
1096         return result;
1097 }
1098
1099 /**
1100  * gnome_vfs_mime_set_short_list_components:
1101  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1102  * @component_iids: GList of const char * OAF IIDs
1103  * 
1104  * Set the short list of components for the specified MIME type. The short list
1105  * contains companents recommended for possible selection by the user. * 
1106  * 
1107  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1108  * any errors encountered.
1109  **/
1110 GnomeVFSResult
1111 gnome_vfs_mime_set_short_list_components (const char *mime_type,
1112                                           GList *component_iids)
1113 {
1114         char *addition_string, *removal_string;
1115         GList *short_list_id_list;
1116         GList *short_list_addition_list;
1117         GList *short_list_removal_list;
1118         GnomeVFSResult result;
1119
1120         short_list_id_list = comma_separated_str_to_str_list
1121                 (gnome_vfs_mime_get_value (mime_type, "short_list_component_iids"));
1122
1123         /* Compute delta. */
1124         short_list_addition_list = str_list_difference (component_iids, short_list_id_list);
1125         short_list_removal_list = str_list_difference (short_list_id_list, component_iids);
1126         addition_string = str_list_to_comma_separated_str (short_list_addition_list);
1127         removal_string = str_list_to_comma_separated_str (short_list_removal_list);
1128         g_list_free (short_list_addition_list);
1129         g_list_free (short_list_removal_list);
1130
1131         /* Write it. */
1132         result = gnome_vfs_mime_edit_user_file_multiple
1133                 (mime_type,
1134                  "short_list_component_user_additions", addition_string,
1135                  "short_list_component_user_removals", removal_string,
1136                  NULL);
1137
1138         g_free (addition_string);
1139         g_free (removal_string);
1140
1141         return result;
1142 }
1143
1144 /* FIXME bugzilla.eazel.com 1148: 
1145  * The next set of helper functions are all replicated in nautilus-mime-actions.c.
1146  * Need to refactor so they can share code.
1147  */
1148 static gint
1149 gnome_vfs_mime_application_has_id (GnomeVFSMimeApplication *application, const char *id)
1150 {
1151         return strcmp (application->id, id);
1152 }
1153
1154 static gint
1155 gnome_vfs_mime_id_matches_application (const char *id, GnomeVFSMimeApplication *application)
1156 {
1157         return gnome_vfs_mime_application_has_id (application, id);
1158 }
1159
1160 static gint
1161 gnome_vfs_mime_id_matches_component (const char *iid, Bonobo_ServerInfo *component)
1162 {
1163         return strcmp (component->iid, iid);
1164 }
1165
1166 static gint 
1167 gnome_vfs_mime_application_matches_id (GnomeVFSMimeApplication *application, const char *id)
1168 {
1169         return gnome_vfs_mime_id_matches_application (id, application);
1170 }
1171
1172 static gint 
1173 gnome_vfs_mime_component_matches_id (Bonobo_ServerInfo *component, const char *iid)
1174 {
1175         return gnome_vfs_mime_id_matches_component (iid, component);
1176 }
1177
1178 /**
1179  * gnome_vfs_mime_id_in_application_list:
1180  * @id: An application id.
1181  * @applications: A GList * whose nodes are GnomeVFSMimeApplications, such as the
1182  * result of gnome_vfs_mime_get_short_list_applications().
1183  * 
1184  * Check whether an application id is in a list of GnomeVFSMimeApplications.
1185  * 
1186  * Return value: TRUE if an application whose id matches @id is in @applications.
1187  */
1188 gboolean
1189 gnome_vfs_mime_id_in_application_list (const char *id, GList *applications)
1190 {
1191         return g_list_find_custom
1192                 (applications, (gpointer) id,
1193                  (GCompareFunc) gnome_vfs_mime_application_matches_id) != NULL;
1194 }
1195
1196 /**
1197  * gnome_vfs_mime_id_in_component_list:
1198  * @iid: A component iid.
1199  * @components: A GList * whose nodes are Bonobo_ServerInfos, such as the
1200  * result of gnome_vfs_mime_get_short_list_components().
1201  * 
1202  * Check whether a component iid is in a list of Bonobo_ServerInfos.
1203  * 
1204  * Return value: TRUE if a component whose iid matches @iid is in @components.
1205  */
1206 gboolean
1207 gnome_vfs_mime_id_in_component_list (const char *iid, GList *components)
1208 {
1209         return g_list_find_custom
1210                 (components, (gpointer) iid,
1211                  (GCompareFunc) gnome_vfs_mime_component_matches_id) != NULL;
1212         return FALSE;
1213 }
1214
1215 /**
1216  * gnome_vfs_mime_id_list_from_application_list:
1217  * @applications: A GList * whose nodes are GnomeVFSMimeApplications, such as the
1218  * result of gnome_vfs_mime_get_short_list_applications().
1219  * 
1220  * Create a list of application ids from a list of GnomeVFSMimeApplications.
1221  * 
1222  * Return value: A new list where each GnomeVFSMimeApplication in the original
1223  * list is replaced by a char * with the application's id. The original list is
1224  * not modified.
1225  */
1226 GList *
1227 gnome_vfs_mime_id_list_from_application_list (GList *applications)
1228 {
1229         GList *result;
1230         GList *node;
1231
1232         result = NULL;
1233         
1234         for (node = applications; node != NULL; node = node->next) {
1235                 result = g_list_append 
1236                         (result, g_strdup (((GnomeVFSMimeApplication *)node->data)->id));
1237         }
1238
1239         return result;
1240 }
1241
1242 /**
1243  * gnome_vfs_mime_id_list_from_component_list:
1244  * @components: A GList * whose nodes are Bonobo_ServerInfos, such as the
1245  * result of gnome_vfs_mime_get_short_list_components().
1246  * 
1247  * Create a list of component iids from a list of Bonobo_ServerInfos.
1248  * 
1249  * Return value: A new list where each Bonobo_ServerInfo in the original
1250  * list is replaced by a char * with the component's iid. The original list is
1251  * not modified.
1252  */
1253 GList *
1254 gnome_vfs_mime_id_list_from_component_list (GList *components)
1255 {
1256         GList *list = NULL;
1257         GList *node;
1258
1259         for (node = components; node != NULL; node = node->next) {
1260                 list = g_list_prepend 
1261                         (list, g_strdup (((Bonobo_ServerInfo *)node->data)->iid));
1262         }
1263         return g_list_reverse (list);
1264 }
1265
1266 static void
1267 g_list_free_deep (GList *list)
1268 {
1269         g_list_foreach (list, (GFunc) g_free, NULL);
1270         g_list_free (list);
1271 }
1272
1273 /**
1274  * gnome_vfs_mime_add_application_to_short_list:
1275  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1276  * @application_id: const char * containing the application's id in the MIME database
1277  * 
1278  * Add an application to the short list for MIME type @mime_type. The short list contains
1279  * applications recommended for display as choices to the user for a particular MIME type.
1280  * 
1281  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1282  * any errors encountered.
1283  **/
1284 GnomeVFSResult
1285 gnome_vfs_mime_add_application_to_short_list (const char *mime_type,
1286                                               const char *application_id)
1287 {
1288         GList *old_list, *new_list;
1289         GnomeVFSResult result;
1290
1291         old_list = gnome_vfs_mime_get_short_list_applications (mime_type);
1292
1293         if (gnome_vfs_mime_id_in_application_list (application_id, old_list)) {
1294                 result = GNOME_VFS_OK;
1295         } else {
1296                 new_list = g_list_append (gnome_vfs_mime_id_list_from_application_list (old_list), 
1297                                           g_strdup (application_id));
1298                 result = gnome_vfs_mime_set_short_list_applications (mime_type, new_list);
1299                 g_list_free_deep (new_list);
1300         }
1301
1302         gnome_vfs_mime_application_list_free (old_list);
1303
1304         return result;
1305 }
1306
1307 /**
1308  * gnome_vfs_mime_remove_application_from_list:
1309  * @applications: A GList * whose nodes are GnomeVFSMimeApplications, such as the
1310  * result of gnome_vfs_mime_get_short_list_applications().
1311  * @application_id: The id of an application to remove from @applications.
1312  * @did_remove: If non-NULL, this is filled in with TRUE if the application
1313  * was found in the list, FALSE otherwise.
1314  * 
1315  * Remove an application specified by id from a list of GnomeVFSMimeApplications.
1316  * 
1317  * Return value: The modified list. If the application is not found, the list will 
1318  * be unchanged.
1319  */
1320 GList *
1321 gnome_vfs_mime_remove_application_from_list (GList *applications, 
1322                                              const char *application_id,
1323                                              gboolean *did_remove)
1324 {
1325         GList *matching_node;
1326         
1327         matching_node = g_list_find_custom 
1328                 (applications, (gpointer)application_id,
1329                  (GCompareFunc) gnome_vfs_mime_application_matches_id);
1330         if (matching_node != NULL) {
1331                 applications = g_list_remove_link (applications, matching_node);
1332                 gnome_vfs_mime_application_list_free (matching_node);
1333         }
1334
1335         if (did_remove != NULL) {
1336                 *did_remove = matching_node != NULL;
1337         }
1338
1339         return applications;
1340 }
1341
1342 /**
1343  * gnome_vfs_mime_remove_application_from_short_list:
1344  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1345  * @application_id: const char * containing the application's id in the MIME database
1346  * 
1347  * Remove an application from the short list for MIME type @mime_type. The short list contains
1348  * applications recommended for display as choices to the user for a particular MIME type.
1349  * 
1350  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1351  * any errors encountered.
1352  **/
1353 GnomeVFSResult
1354 gnome_vfs_mime_remove_application_from_short_list (const char *mime_type,
1355                                                    const char *application_id)
1356 {
1357         GnomeVFSResult result;
1358         GList *old_list, *new_list;
1359         gboolean was_in_list;
1360
1361         old_list = gnome_vfs_mime_get_short_list_applications (mime_type);
1362         old_list = gnome_vfs_mime_remove_application_from_list 
1363                 (old_list, application_id, &was_in_list);
1364
1365         if (!was_in_list) {
1366                 result = GNOME_VFS_OK;
1367         } else {
1368                 new_list = gnome_vfs_mime_id_list_from_application_list (old_list);
1369                 result = gnome_vfs_mime_set_short_list_applications (mime_type, new_list);
1370                 g_list_free_deep (new_list);
1371         }
1372
1373         gnome_vfs_mime_application_list_free (old_list);
1374
1375         return result;
1376 }
1377
1378 /**
1379  * gnome_vfs_mime_add_component_to_short_list:
1380  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1381  * @iid: const char * containing the component's OAF IID
1382  * 
1383  * Add a component to the short list for MIME type @mime_type. The short list contains
1384  * components recommended for display as choices to the user for a particular MIME type.
1385  * 
1386  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1387  * any errors encountered.
1388  **/
1389 GnomeVFSResult
1390 gnome_vfs_mime_add_component_to_short_list (const char *mime_type,
1391                                             const char *iid)
1392 {
1393         GnomeVFSResult result;
1394         GList *old_list, *new_list;
1395
1396         old_list = gnome_vfs_mime_get_short_list_components (mime_type);
1397
1398         if (gnome_vfs_mime_id_in_component_list (iid, old_list)) {
1399                 result = GNOME_VFS_OK;
1400         } else {
1401                 new_list = g_list_append (gnome_vfs_mime_id_list_from_component_list (old_list), 
1402                                           g_strdup (iid));
1403                 result = gnome_vfs_mime_set_short_list_components (mime_type, new_list);
1404                 g_list_free_deep (new_list);
1405         }
1406
1407         gnome_vfs_mime_component_list_free (old_list);
1408
1409         return result;
1410 }
1411
1412 /**
1413  * gnome_vfs_mime_remove_component_from_list:
1414  * @components: A GList * whose nodes are Bonobo_ServerInfos, such as the
1415  * result of gnome_vfs_mime_get_short_list_components().
1416  * @iid: The iid of a component to remove from @components.
1417  * @did_remove: If non-NULL, this is filled in with TRUE if the component
1418  * was found in the list, FALSE otherwise.
1419  * 
1420  * Remove a component specified by iid from a list of Bonobo_ServerInfos.
1421  * 
1422  * Return value: The modified list. If the component is not found, the list will 
1423  * be unchanged.
1424  */
1425 GList *
1426 gnome_vfs_mime_remove_component_from_list (GList *components, 
1427                                            const char *iid,
1428                                            gboolean *did_remove)
1429 {
1430         GList *matching_node;
1431         
1432         matching_node = g_list_find_custom 
1433                 (components, (gpointer)iid,
1434                  (GCompareFunc) gnome_vfs_mime_component_matches_id);
1435         if (matching_node != NULL) {
1436                 components = g_list_remove_link (components, matching_node);
1437                 gnome_vfs_mime_component_list_free (matching_node);
1438         }
1439
1440         if (did_remove != NULL) {
1441                 *did_remove = matching_node != NULL;
1442         }
1443         return components;
1444 }
1445
1446 /**
1447  * gnome_vfs_mime_remove_component_from_short_list:
1448  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1449  * @iid: const char * containing the component's OAF IID
1450  * 
1451  * Remove a component from the short list for MIME type @mime_type. The short list contains
1452  * components recommended for display as choices to the user for a particular MIME type.
1453  * 
1454  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1455  * any errors encountered.
1456  **/
1457 GnomeVFSResult
1458 gnome_vfs_mime_remove_component_from_short_list (const char *mime_type,
1459                                                  const char *iid)
1460 {
1461         GnomeVFSResult result;
1462         GList *old_list, *new_list;
1463         gboolean was_in_list;
1464
1465         old_list = gnome_vfs_mime_get_short_list_components (mime_type);
1466         old_list = gnome_vfs_mime_remove_component_from_list 
1467                 (old_list, iid, &was_in_list);
1468
1469         if (!was_in_list) {
1470                 result = GNOME_VFS_OK;
1471         } else {
1472                 new_list = gnome_vfs_mime_id_list_from_component_list (old_list);
1473                 result = gnome_vfs_mime_set_short_list_components (mime_type, new_list);
1474                 g_list_free_deep (new_list);
1475         }
1476
1477         gnome_vfs_mime_component_list_free (old_list);
1478
1479         return result;
1480 }
1481
1482 /**
1483  * gnome_vfs_mime_add_extension:
1484  * @extension: The extension to add (e.g. "txt")
1485  * @mime_type: The mime type to add the mapping to.
1486  * 
1487  * Add a file extension to the specificed MIME type in the MIME database.
1488  * 
1489  * Return value: GnomeVFSResult indicating the success of the operation or any
1490  * errors that may have occurred.
1491  **/
1492 GnomeVFSResult
1493 gnome_vfs_mime_add_extension (const char *mime_type, const char *extension)
1494 {
1495         GnomeVFSResult result;
1496         GList *list, *element;
1497         gchar *extensions, *old_extensions;
1498
1499         extensions = NULL;
1500         old_extensions = NULL;
1501
1502         result = GNOME_VFS_OK;
1503         
1504         list = gnome_vfs_mime_get_extensions_list (mime_type);  
1505         if (list == NULL) {
1506                 /* List is NULL. This means there are no current registered extensions. 
1507                  * Add the new extension and it will cause the list to be created next time.
1508                  */
1509                 result = gnome_vfs_mime_set_registered_type_key (mime_type, "ext", extension);
1510                 return result;
1511         }
1512
1513         /* Check for duplicates */
1514         for (element = list; element != NULL; element = element->next) {
1515                 if (strcmp (extension, (char *)element->data) == 0) {                                   
1516                         gnome_vfs_mime_extensions_list_free (list);
1517                         return result;
1518                 }
1519         }
1520
1521         /* Add new extension to list */
1522         for (element = list; element != NULL; element = element->next) {                
1523                 if (extensions != NULL) {
1524                         old_extensions = extensions;
1525                         extensions = g_strdup_printf ("%s %s", old_extensions, (char *)element->data);
1526                         g_free (old_extensions);
1527                 } else {
1528                         extensions = g_strdup_printf ("%s", (char *)element->data);
1529                 }
1530         }
1531         
1532         if (extensions != NULL) {
1533                 old_extensions = extensions;
1534                 extensions = g_strdup_printf ("%s %s", old_extensions, extension);
1535                 g_free (old_extensions);
1536
1537                 /* Add extensions to hash table and flush into the file. */
1538                 gnome_vfs_mime_set_registered_type_key (mime_type, "ext", extensions);
1539         }
1540         
1541         gnome_vfs_mime_extensions_list_free (list);
1542
1543         return result;
1544 }
1545
1546 /**
1547  * gnome_vfs_mime_remove_extension:
1548  * @extension: The extension to remove
1549  * @mime_type: The mime type to remove the extension from
1550  * 
1551  * Removes a file extension from the specificed MIME type in the MIME database.
1552  * 
1553  * Return value: GnomeVFSResult indicating the success of the operation or any
1554  * errors that may have occurred.
1555  **/
1556 GnomeVFSResult
1557 gnome_vfs_mime_remove_extension (const char *mime_type, const char *extension)
1558 {
1559         GList *list, *element;
1560         gchar *extensions, *old_extensions;
1561         gboolean in_list;
1562         GnomeVFSResult result;
1563
1564         result = GNOME_VFS_OK;
1565         extensions = NULL;
1566         old_extensions = NULL;
1567         in_list = FALSE;
1568         
1569         list = gnome_vfs_mime_get_extensions_list (mime_type);  
1570         if (list == NULL) {
1571                 return result;
1572         }
1573
1574         /* See if extension is in list */
1575         for (element = list; element != NULL; element = element->next) {
1576                 if (strcmp (extension, (char *)element->data) == 0) {                                   
1577                         /* Remove extension from list */                        
1578                         in_list = TRUE;
1579                         list = g_list_remove (list, element->data);
1580                         g_free (element->data);
1581                         element = NULL;
1582                 }
1583
1584                 if (in_list) {
1585                         break;
1586                 }
1587         }
1588
1589         /* Exit if we found no match */
1590         if (!in_list) {
1591                 gnome_vfs_mime_extensions_list_free (list);
1592                 return result;
1593         }
1594         
1595         /* Create new extension list */
1596         for (element = list; element != NULL; element = element->next) {                
1597                 if (extensions != NULL) {
1598                         old_extensions = extensions;
1599                         extensions = g_strdup_printf ("%s %s", old_extensions, (char *)element->data);
1600                         g_free (old_extensions);
1601                 } else {
1602                         extensions = g_strdup_printf ("%s", (char *)element->data);
1603                 }
1604         }
1605         
1606         if (extensions != NULL) {
1607                 /* Add extensions to hash table and flush into the file */
1608                 gnome_vfs_mime_set_registered_type_key (mime_type, "ext", extensions);
1609         } else {
1610                 gnome_vfs_mime_set_registered_type_key (mime_type, "ext", "");
1611         }
1612         
1613         gnome_vfs_mime_extensions_list_free (list);
1614
1615         return result;
1616 }
1617
1618 /**
1619  * gnome_vfs_mime_extend_all_applications:
1620  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1621  * @application_ids: a GList of const char * containing application ids
1622  * 
1623  * Register @mime_type as being handled by all applications list in @application_ids.
1624  * 
1625  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1626  * any errors encountered.
1627  **/
1628 GnomeVFSResult
1629 gnome_vfs_mime_extend_all_applications (const char *mime_type,
1630                                         GList *application_ids)
1631 {
1632         GList *li;
1633
1634         g_return_val_if_fail (mime_type != NULL, GNOME_VFS_ERROR_INTERNAL);
1635
1636         for (li = application_ids; li != NULL; li = li->next) {
1637                 const char *application_id = li->data;
1638                 gnome_vfs_application_registry_add_mime_type (application_id,
1639                                                               mime_type);
1640         }
1641
1642         return gnome_vfs_application_registry_sync ();
1643 }
1644
1645 /**
1646  * gnome_vfs_mime_remove_from_all_applications:
1647  * @mime_type: A const char * containing a mime type, e.g. "application/x-php"
1648  * @application_ids: a GList of const char * containing application ids
1649  * 
1650  * Remove @mime_type as a handled type from every application in @application_ids
1651  * 
1652  * Return value: A GnomeVFSResult indicating the success of the operation or reporting 
1653  * any errors encountered.
1654  **/
1655 GnomeVFSResult
1656 gnome_vfs_mime_remove_from_all_applications (const char *mime_type,
1657                                              GList *application_ids)
1658 {
1659         GList *li;
1660
1661         g_return_val_if_fail (mime_type != NULL, GNOME_VFS_ERROR_INTERNAL);
1662
1663         for (li = application_ids; li != NULL; li = li->next) {
1664                 const char *application_id = li->data;
1665                 gnome_vfs_application_registry_remove_mime_type (application_id,
1666                                                                  mime_type);
1667         }
1668
1669         return gnome_vfs_application_registry_sync ();
1670 }
1671
1672
1673 /**
1674  * gnome_vfs_mime_application_copy:
1675  * @application: The GnomeVFSMimeApplication to be duplicated.
1676  * 
1677  * Creates a newly referenced copy of a GnomeVFSMimeApplication object.
1678  * 
1679  * Return value: A copy of @application
1680  **/
1681 GnomeVFSMimeApplication *
1682 gnome_vfs_mime_application_copy (GnomeVFSMimeApplication *application)
1683 {
1684         GnomeVFSMimeApplication *result;
1685
1686         if (application == NULL) {
1687                 return NULL;
1688         }
1689         
1690         result = g_new0 (GnomeVFSMimeApplication, 1);
1691         result->id = g_strdup (application->id);
1692         result->name = g_strdup (application->name);
1693         result->command = g_strdup (application->command);
1694         result->can_open_multiple_files = application->can_open_multiple_files;
1695         result->expects_uris = application->expects_uris;
1696         result->supported_uri_schemes = copy_str_list (application->supported_uri_schemes);
1697         result->requires_terminal = application->requires_terminal;
1698
1699         return result;
1700 }
1701
1702 /**
1703  * gnome_vfs_mime_application_free:
1704  * @application: The GnomeVFSMimeApplication to be freed
1705  * 
1706  * Frees a GnomeVFSMimeApplication *.
1707  * 
1708  **/
1709 void
1710 gnome_vfs_mime_application_free (GnomeVFSMimeApplication *application) 
1711 {
1712         if (application != NULL) {
1713                 g_free (application->name);
1714                 g_free (application->command);
1715                 g_list_foreach (application->supported_uri_schemes,
1716                                 (GFunc) g_free,
1717                                 NULL);
1718                 g_list_free (application->supported_uri_schemes);
1719                 g_free (application->id);
1720                 g_free (application);
1721         }
1722 }
1723
1724 /**
1725  * gnome_vfs_mime_action_free:
1726  * @action: The GnomeVFSMimeAction to be freed
1727  * 
1728  * Frees a GnomeVFSMimeAction *.
1729  * 
1730  **/
1731 void
1732 gnome_vfs_mime_action_free (GnomeVFSMimeAction *action) 
1733 {
1734         switch (action->action_type) {
1735         case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION:
1736                 gnome_vfs_mime_application_free (action->action.application);
1737                 break;
1738         case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT:
1739                 CORBA_free (action->action.component);
1740                 break;
1741         default:
1742                 g_assert_not_reached ();
1743         }
1744
1745         g_free (action);
1746 }
1747
1748 /**
1749  * gnome_vfs_mime_application_list_free:
1750  * @list: a GList of GnomeVFSApplication * to be freed
1751  * 
1752  * Frees lists of GnomeVFSApplications, as returned from functions such
1753  * as gnome_vfs_get_all_applications().
1754  * 
1755  **/
1756 void
1757 gnome_vfs_mime_application_list_free (GList *list)
1758 {
1759         g_list_foreach (list, (GFunc) gnome_vfs_mime_application_free, NULL);
1760         g_list_free (list);
1761 }
1762
1763 /**
1764  * gnome_vfs_mime_component_list_free:
1765  * @list: a GList of Bonobo_ServerInfo * to be freed
1766  * 
1767  * Frees lists of Bonobo_ServerInfo * (as returned from functions such
1768  * as @gnome_vfs_get_all_components)
1769  * 
1770  **/
1771 void
1772 gnome_vfs_mime_component_list_free (GList *list)
1773 {
1774         g_list_foreach (list, (GFunc) CORBA_free, NULL);
1775         g_list_free (list);
1776 }
1777
1778 /**
1779  * gnome_vfs_mime_application_new_from_id:
1780  * @id: A const char * containing an application id
1781  * 
1782  * Fetches the GnomeVFSMimeApplication associated with the specified
1783  * application ID from the MIME database.
1784  *
1785  * Return value: GnomeVFSMimeApplication * corresponding to @id
1786  **/
1787 GnomeVFSMimeApplication *
1788 gnome_vfs_mime_application_new_from_id (const char *id)
1789 {
1790         return gnome_vfs_application_registry_get_mime_application (id);
1791 }
1792
1793 /**
1794  * gnome_vfs_mime_action_launch:
1795  * @action: the GnomeVFSMimeAction to launch
1796  * @uris: parameters for the GnomeVFSMimeAction
1797  *
1798  * Launches the given mime action with the given parameters. If 
1799  * the action is an application the command line parameters will
1800  * be expanded as required by the application. The application
1801  * will also be launched in a terminal if that is required. If the
1802  * application only supports one argument per instance then multile
1803  * instances of the application will be launched.
1804  *
1805  * If the default action is a component it will be launched with
1806  * the component viewer application defined using the gconf value:
1807  * /desktop/gnome/application/component_viewer/exec. The parameters
1808  * %s and %c in the command line will be replaced with the list of
1809  * parameters and the default component IID respectively.
1810  *
1811  * Return value: GNOME_VFS_OK if the action was launched,
1812  * GNOME_VFS_ERROR_BAD_PARAMETERS for an invalid action.
1813  * GNOME_VFS_ERROR_NOT_SUPPORTED if the uri protocol is
1814  * not supported by the action.
1815  * GNOME_VFS_ERROR_PARSE if the action command can not be parsed.
1816  * GNOME_VFS_ERROR_LAUNCH if the action command can not be launched.
1817  * GNOME_VFS_ERROR_INTERNAL for other internal and GConf errors.
1818  *
1819  * Since: 2.4
1820  */
1821 GnomeVFSResult
1822 gnome_vfs_mime_action_launch (GnomeVFSMimeAction *action,
1823                               GList              *uris)
1824 {
1825         return gnome_vfs_mime_action_launch_with_env (action, uris, NULL);
1826 }
1827
1828 /**
1829  * gnome_vfs_mime_action_launch_with_env:
1830  *
1831  * Same as gnome_vfs_mime_action_launch except that the
1832  * application or component viewer will be launched with
1833  * the given environment.
1834  *
1835  * Return value: same as gnome_vfs_mime_action_launch
1836  *
1837  * Since: 2.4
1838  */
1839 GnomeVFSResult
1840 gnome_vfs_mime_action_launch_with_env (GnomeVFSMimeAction *action,
1841                                        GList              *uris,
1842                                        char              **envp)
1843 {
1844         GnomeVFSResult result;
1845         char **argv;
1846         int argc;
1847
1848         g_return_val_if_fail (action != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
1849         g_return_val_if_fail (uris != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
1850
1851         switch (action->action_type) {
1852         case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION:
1853         
1854                 return gnome_vfs_mime_application_launch_with_env
1855                                         (action->action.application,
1856                                          uris, envp);
1857                                          
1858         case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT:
1859         
1860                 result = expand_parameters (action->action.component, action->action_type,
1861                                             uris, &argc, &argv);
1862                                             
1863                 if (result != GNOME_VFS_OK) {
1864                         return result;
1865                 }
1866                 
1867                 if (!g_spawn_async (NULL /* working directory */,
1868                                     argv,
1869                                     envp,
1870                                     G_SPAWN_SEARCH_PATH /* flags */,
1871                                     NULL /* child_setup */,
1872                                     NULL /* data */,
1873                                     NULL /* child_pid */,
1874                                     NULL /* error */)) {
1875                         g_strfreev (argv);
1876                         return GNOME_VFS_ERROR_LAUNCH;
1877                 }
1878                 g_strfreev (argv);
1879                 
1880                 return GNOME_VFS_OK;            
1881         
1882         default:
1883                 g_assert_not_reached ();
1884         }
1885         
1886         return GNOME_VFS_ERROR_BAD_PARAMETERS;
1887 }
1888
1889 /**
1890  * gnome_vfs_mime_application_launch:
1891  * @app: the GnomeVFSMimeApplication to launch
1892  * @uris: parameters for the GnomeVFSMimeApplication
1893  *
1894  * Launches the given mime application with the given parameters.
1895  * Command line parameters will be expanded as required by the
1896  * application. The application will also be launched in a terminal
1897  * if that is required. If the application only supports one argument 
1898  * per instance then multiple instances of the application will be 
1899  * launched.
1900  *
1901  * Return value: 
1902  * GNOME_VFS_OK if the application was launched.
1903  * GNOME_VFS_ERROR_NOT_SUPPORTED if the uri protocol is not
1904  * supported by the application.
1905  * GNOME_VFS_ERROR_PARSE if the application command can not
1906  * be parsed.
1907  * GNOME_VFS_ERROR_LAUNCH if the application command can not
1908  * be launched.
1909  * GNOME_VFS_ERROR_INTERNAL for other internal and GConf errors.
1910  *
1911  * Since: 2.4
1912  */
1913 GnomeVFSResult
1914 gnome_vfs_mime_application_launch (GnomeVFSMimeApplication *app,
1915                                    GList                   *uris)
1916 {
1917         return gnome_vfs_mime_application_launch_with_env (app, uris, NULL);
1918 }
1919
1920 /**
1921  * gnome_vfs_mime_application_launch_with_env:
1922  *
1923  * Same as gnome_vfs_mime_application_launch except that
1924  * the application will be launched with the given environment.
1925  *
1926  * Return value: same as gnome_vfs_mime_application_launch
1927  *
1928  * Since: 2.4
1929  */
1930 GnomeVFSResult 
1931 gnome_vfs_mime_application_launch_with_env (GnomeVFSMimeApplication *app,
1932                                             GList                   *uris,
1933                                             char                   **envp)
1934 {
1935         GnomeVFSResult result;
1936         GList *u;
1937         char *scheme;
1938         char **argv;
1939         int argc;
1940         
1941         g_return_val_if_fail (app != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
1942         g_return_val_if_fail (uris != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
1943         
1944         /* check that all uri schemes are supported */
1945         if (app->supported_uri_schemes != NULL) {
1946                 for (u = uris; u != NULL; u = u->next) {
1947
1948                         scheme = gnome_vfs_get_uri_scheme (u->data);
1949                 
1950                         if (!g_list_find_custom (app->supported_uri_schemes,
1951                                                  scheme, (GCompareFunc) strcmp)) {
1952                                 g_free (scheme);
1953                                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
1954                         }
1955                         
1956                         g_free (scheme);
1957                 }
1958         }
1959
1960         while (uris != NULL) {
1961                 
1962                 result = expand_parameters (app, GNOME_VFS_MIME_ACTION_TYPE_APPLICATION,
1963                                             uris, &argc, &argv);
1964                 
1965                 if (result != GNOME_VFS_OK) {
1966                         return result;
1967                 }
1968
1969                 if (app->requires_terminal) {
1970                         if (!_gnome_vfs_prepend_terminal_to_vector (&argc, &argv)) {
1971                                 g_strfreev (argv);
1972                                 return GNOME_VFS_ERROR_INTERNAL;
1973                         }
1974                 }
1975                 
1976                 if (!g_spawn_async (NULL /* working directory */,
1977                                     argv,
1978                                     envp,
1979                                     G_SPAWN_SEARCH_PATH /* flags */,
1980                                     NULL /* child_setup */,
1981                                     NULL /* data */,
1982                                     NULL /* child_pid */,
1983                                     NULL /* error */)) {
1984                         g_strfreev (argv);
1985                         return GNOME_VFS_ERROR_LAUNCH;
1986                 }
1987                 
1988                 g_strfreev (argv);
1989                 uris = uris->next;
1990                 
1991                 if (app->can_open_multiple_files) {
1992                         break;
1993                 }
1994         }
1995         
1996         return GNOME_VFS_OK;            
1997 }
1998
1999 static GnomeVFSResult
2000 expand_parameters (gpointer                 action,
2001                    GnomeVFSMimeActionType   type,
2002                    GList                   *uris,
2003                    int                     *argc,
2004                    char                  ***argv)                  
2005 {
2006         GnomeVFSMimeApplication *app = NULL;
2007         Bonobo_ServerInfo *server = NULL;
2008         GConfClient *client;
2009         char *path;
2010         char *command = NULL;
2011         char **c_argv, **r_argv;
2012         int c_argc, max_r_argc;
2013         int i, c;
2014         gboolean added_arg;
2015
2016         /* figure out what command to parse */  
2017         switch (type) {
2018         case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION:
2019                 app = (GnomeVFSMimeApplication *) action;
2020                 command = app->command;
2021                 break;
2022                 
2023         case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT:
2024                 if (!gconf_is_initialized ()) {
2025                         if (!gconf_init (0, NULL, NULL)) {
2026                                 return GNOME_VFS_ERROR_INTERNAL;
2027                         }
2028                 }
2029         
2030                 client = gconf_client_get_default ();
2031                 g_return_val_if_fail (client != NULL, GNOME_VFS_ERROR_INTERNAL);
2032         
2033                 command = gconf_client_get_string (client, GCONF_DEFAULT_VIEWER_EXEC_PATH, NULL);
2034                 g_object_unref (client);
2035                 
2036                 if (command != NULL) {
2037                         g_warning ("No default component viewer set\n");
2038                         return GNOME_VFS_ERROR_INTERNAL;
2039                 }
2040                 
2041                 server = (Bonobo_ServerInfo *) action;
2042                 
2043                 break;
2044
2045         default:
2046                 g_assert_not_reached ();
2047         }
2048
2049         if (!g_shell_parse_argv (command,
2050                                  &c_argc,
2051                                  &c_argv,
2052                                  NULL)) {
2053                 return GNOME_VFS_ERROR_PARSE;
2054         }
2055         
2056         /* figure out how many parameters we can max have */
2057         max_r_argc = g_list_length (uris) + c_argc + 1;
2058         r_argv = g_new0 (char *, max_r_argc + 1);
2059
2060         added_arg = FALSE;
2061         i = 0;
2062         for (c = 0; c < c_argc; c++) {
2063                 /* replace %s with the uri parameters */
2064                 if (strcmp (c_argv[c], "%s") == 0) {
2065                         while (uris != NULL) {
2066                                 if (app != NULL) {
2067                                         switch (app->expects_uris) {
2068                                         case GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS:
2069                                                 r_argv[i] = g_strdup (uris->data);
2070                                                 break;
2071         
2072                                         case GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS:
2073                                                 r_argv[i] = gnome_vfs_get_local_path_from_uri (uris->data);
2074                                                 if (r_argv[i] == NULL) {
2075                                                         g_strfreev (c_argv);
2076                                                         g_strfreev (r_argv);
2077                                                         return GNOME_VFS_ERROR_NOT_SUPPORTED;
2078                                                 }
2079                                                 break;
2080                         
2081                                         case GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS_FOR_NON_FILES:
2082                                                 /* this really means use URIs for non local files */
2083                                                 path = gnome_vfs_get_local_path_from_uri (uris->data);
2084                         
2085                                                 if (path != NULL) {
2086                                                         r_argv[i] = path;
2087                                                 } else {
2088                                                         r_argv[i] = g_strdup (uris->data);
2089                                                 }                               
2090                         
2091                                                 break;
2092                                 
2093                                         default:
2094                                                 g_assert_not_reached ();
2095                                         }       
2096                                 } else {
2097                                         r_argv[i] = g_strdup (uris->data);
2098                                 }
2099                                 i++;
2100                                 uris = uris->next;
2101                                 
2102                                 if (app != NULL && !app->can_open_multiple_files) {
2103                                         break;
2104                                 }
2105                         }
2106                         added_arg = TRUE;
2107                         
2108                 /* replace %c with the component iid */
2109                 } else if (server != NULL && strcmp (c_argv[c], "%c") == 0) {
2110                         r_argv[i++] = g_strdup (server->iid);
2111                         added_arg = TRUE;
2112                         
2113                 /* otherwise take arg from command */
2114                 } else {
2115                         r_argv[i++] = g_strdup (c_argv[c]);
2116                 }
2117         }
2118         g_strfreev (c_argv);
2119         
2120         /* if there is no %s or %c, append the parameters to the end */
2121         if (!added_arg) {
2122                 while (uris != NULL) {
2123                         if (app != NULL) {
2124                                 switch (app->expects_uris) {
2125                                 case GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS:
2126                                         r_argv[i] = g_strdup (uris->data);
2127                                         break;
2128
2129                                 case GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS:
2130                                         r_argv[i] = gnome_vfs_get_local_path_from_uri (uris->data);
2131                                         if (r_argv[i] == NULL) {
2132                                                 g_strfreev (r_argv);
2133                                                 return GNOME_VFS_ERROR_NOT_SUPPORTED;
2134                                         }
2135                                         break;
2136                 
2137                                 case GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS_FOR_NON_FILES:
2138                                         /* this really means use URIs for non local files */
2139                                         path = gnome_vfs_get_local_path_from_uri (uris->data);
2140                 
2141                                         if (path != NULL) {
2142                                                 r_argv[i] = path;
2143                                         } else {
2144                                                 r_argv[i] = g_strdup (uris->data);
2145                                         }                               
2146                 
2147                                         break;
2148                         
2149                                 default:
2150                                         g_assert_not_reached ();
2151                                 }
2152                         } else {
2153                                 r_argv[i] = g_strdup (uris->data);
2154                         }
2155                         i++;
2156                         uris = uris->next;
2157                         
2158                         if (app != NULL && !app->can_open_multiple_files) {
2159                                 break;
2160                         }
2161                 }
2162         }               
2163         
2164         *argv = r_argv;
2165         *argc = i;
2166
2167         return GNOME_VFS_OK;
2168 }
2169
2170 static gboolean
2171 application_known_to_be_nonexistent (const char *application_id)
2172 {
2173         const char *command;
2174
2175         g_return_val_if_fail (application_id != NULL, FALSE);
2176
2177         command = gnome_vfs_application_registry_peek_value
2178                 (application_id,
2179                  GNOME_VFS_APPLICATION_REGISTRY_COMMAND);
2180
2181         if (command == NULL) {
2182                 return TRUE;
2183         }
2184
2185         return !gnome_vfs_is_executable_command_string (command);
2186 }
2187
2188 static GList *
2189 prune_ids_for_nonexistent_applications (GList *list)
2190 {
2191         GList *p, *next;
2192
2193         for (p = list; p != NULL; p = next) {
2194                 next = p->next;
2195
2196                 if (application_known_to_be_nonexistent (p->data)) {
2197                         list = g_list_remove_link (list, p);
2198                         g_free (p->data);
2199                         g_list_free_1 (p);
2200                 }
2201         }
2202
2203         return list;
2204 }
2205
2206 static GList *
2207 Bonobo_ServerInfoList_to_ServerInfo_g_list (Bonobo_ServerInfoList *info_list)
2208 {
2209         GList *retval;
2210         int i;
2211         
2212         retval = NULL;
2213         if (info_list != NULL && info_list->_length > 0) {
2214                 for (i = 0; i < info_list->_length; i++) {
2215                         retval = g_list_prepend (retval, Bonobo_ServerInfo_duplicate (&info_list->_buffer[i]));
2216                 }
2217                 retval = g_list_reverse (retval);
2218         }
2219
2220         return retval;
2221 }
2222
2223 static char **
2224 strsplit_handle_null (const char *str, const char *delim, int max)
2225 {
2226         return g_strsplit ((str == NULL ? "" : str), delim, max);
2227 }
2228
2229 static GList *
2230 strsplit_to_list (const char *str, const char *delim, int max)
2231 {
2232         char **strv;
2233         GList *retval;
2234         int i;
2235
2236         strv = strsplit_handle_null (str, delim, max);
2237
2238         retval = NULL;
2239
2240         for (i = 0; strv[i] != NULL; i++) {
2241                 retval = g_list_prepend (retval, strv[i]);
2242         }
2243
2244         retval = g_list_reverse (retval);
2245         
2246         /* Don't strfreev, since we didn't copy the individual strings. */
2247         g_free (strv);
2248
2249         return retval;
2250 }
2251
2252 static char *
2253 strjoin_from_list (const char *separator, GList *list)
2254 {
2255         char **strv;
2256         int i;
2257         GList *p;
2258         char *retval;
2259
2260         strv = g_new0 (char *, (g_list_length (list) + 1));
2261
2262         for (p = list, i = 0; p != NULL; p = p->next, i++) {
2263                 strv[i] = p->data;
2264         }
2265
2266         retval = g_strjoinv (separator, strv);
2267
2268         g_free (strv);
2269
2270         return retval;
2271 }
2272
2273 static GList *
2274 comma_separated_str_to_str_list (const char *str)
2275 {
2276         return strsplit_to_list (str, ",", 0);
2277 }
2278
2279 static char *
2280 str_list_to_comma_separated_str (GList *list)
2281 {
2282         return strjoin_from_list (",", list);
2283 }
2284
2285 static GList *
2286 str_list_difference (GList *a, GList *b)
2287 {
2288         GList *node, *result;
2289
2290         /* Uses an N^2 algorithm rather than a more efficient one
2291          * that sorts or creates a hash table.
2292          */
2293
2294         result = NULL;
2295
2296         for (node = a; node != NULL; node = node->next) {
2297                 if (g_list_find_custom (b, node->data, (GCompareFunc) strcmp) == NULL) {
2298                         result = g_list_prepend (result, node->data);
2299                 }
2300         }
2301
2302         return g_list_reverse (result);
2303 }
2304
2305 static GList *
2306 copy_str_list (GList *string_list)
2307 {
2308         GList *copy, *node;
2309        
2310         copy = NULL;
2311         for (node = string_list; node != NULL; node = node->next) {
2312                 copy = g_list_prepend (copy, 
2313                                        g_strdup ((char *) node->data));
2314                                        }
2315         return g_list_reverse (copy);
2316 }
2317
2318 static const char *
2319 gnome_vfs_mime_maybe_get_user_level_value (const char *mime_type, const char *key)
2320 {
2321         const char *return_value;
2322         char *new_key;
2323         
2324         /* This function checks for a key and falls back
2325          * to the "advanced" user level preference if the key
2326          * doesn't exist.
2327          */
2328
2329         return_value = gnome_vfs_mime_get_value (mime_type, key);
2330
2331         if (return_value != NULL) {
2332                 return return_value;
2333         }
2334
2335         new_key = g_strconcat (key, "_for_advanced_user_level", NULL);
2336         return_value = gnome_vfs_mime_get_value (mime_type, new_key);
2337         g_free (new_key);
2338         
2339         return return_value;
2340 }