1 /* GIO - GLib Input, Output and Streaming Library 2 * 3 * Copyright (C) 2006-2007 Red Hat, Inc. 4 * Copyright 2007 Ryan Lortie 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General 17 * Public License along with this library; if not, write to the 18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 19 * Boston, MA 02111-1307, USA. 20 * 21 * Author: Alexander Larsson <alexl (at) redhat.com> 22 */ 23 24 #include "config.h" 25 26 #include <errno.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <sys/wait.h> 30 31 #ifdef HAVE_CRT_EXTERNS_H 32 #include <crt_externs.h> 33 #endif 34 35 #include "gcontenttypeprivate.h" 36 #include "gdesktopappinfo.h" 37 #include "gfile.h" 38 #include "gioerror.h" 39 #include "gthemedicon.h" 40 #include "gfileicon.h" 41 #include <glib/gstdio.h> 42 #include "glibintl.h" 43 #include "giomodule-priv.h" 44 #include "gappinfo.h" 45 46 #include "gioalias.h" 47 48 /** 49 * SECTION:gdesktopappinfo 50 * @short_description: Application information from desktop files 51 * @include: gio/gdesktopappinfo.h 52 * 53 * #GDesktopAppInfo is an implementation of #GAppInfo based on 54 * desktop files. 55 * 56 * Note that <filename><gio/gdesktopappinfo.h></filename> belongs to 57 * the UNIX-specific GIO interfaces, thus you have to use the 58 * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it. 59 */ 60 61 #define DEFAULT_APPLICATIONS_GROUP "Default Applications" 62 #define ADDED_ASSOCIATIONS_GROUP "Added Associations" 63 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations" 64 #define MIME_CACHE_GROUP "MIME Cache" 65 66 static void g_desktop_app_info_iface_init (GAppInfoIface *iface); 67 static GList * get_all_desktop_entries_for_mime_type (const char *base_mime_type, 68 const char **except); 69 static void mime_info_cache_reload (const char *dir); 70 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info, 71 GError **error); 72 73 /** 74 * GDesktopAppInfo: 75 * 76 * Information about an installed application from a desktop file. 77 */ 78 struct _GDesktopAppInfo 79 { 80 GObject parent_instance; 81 82 char *desktop_id; 83 char *filename; 84 85 char *name; 86 /* FIXME: what about GenericName ? */ 87 char *comment; 88 char *icon_name; 89 GIcon *icon; 90 char **only_show_in; 91 char **not_show_in; 92 char *try_exec; 93 char *exec; 94 char *binary; 95 char *path; 96 97 guint nodisplay : 1; 98 guint hidden : 1; 99 guint terminal : 1; 100 guint startup_notify : 1; 101 guint no_fuse : 1; 102 /* FIXME: what about StartupWMClass ? */ 103 }; 104 105 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT, 106 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO, 107 g_desktop_app_info_iface_init)) 108 109 static gpointer 110 search_path_init (gpointer data) 111 { 112 char **args = NULL; 113 const char * const *data_dirs; 114 const char *user_data_dir; 115 int i, length, j; 116 117 data_dirs = g_get_system_data_dirs (); 118 length = g_strv_length ((char **) data_dirs); 119 120 args = g_new (char *, length + 2); 121 122 j = 0; 123 user_data_dir = g_get_user_data_dir (); 124 args[j++] = g_build_filename (user_data_dir, "applications", NULL); 125 for (i = 0; i < length; i++) 126 args[j++] = g_build_filename (data_dirs[i], 127 "applications", NULL); 128 args[j++] = NULL; 129 130 return args; 131 } 132 133 static const char * const * 134 get_applications_search_path (void) 135 { 136 static GOnce once_init = G_ONCE_INIT; 137 return g_once (&once_init, search_path_init, NULL); 138 } 139 140 static void 141 g_desktop_app_info_finalize (GObject *object) 142 { 143 GDesktopAppInfo *info; 144 145 info = G_DESKTOP_APP_INFO (object); 146 147 g_free (info->desktop_id); 148 g_free (info->filename); 149 g_free (info->name); 150 g_free (info->comment); 151 g_free (info->icon_name); 152 if (info->icon) 153 g_object_unref (info->icon); 154 g_strfreev (info->only_show_in); 155 g_strfreev (info->not_show_in); 156 g_free (info->try_exec); 157 g_free (info->exec); 158 g_free (info->binary); 159 g_free (info->path); 160 161 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object); 162 } 163 164 static void 165 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass) 166 { 167 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 168 169 gobject_class->finalize = g_desktop_app_info_finalize; 170 } 171 172 static void 173 g_desktop_app_info_init (GDesktopAppInfo *local) 174 { 175 } 176 177 static char * 178 binary_from_exec (const char *exec) 179 { 180 const char *p, *start; 181 182 p = exec; 183 while (*p == ' ') 184 p++; 185 start = p; 186 while (*p != ' ' && *p != 0) 187 p++; 188 189 return g_strndup (start, p - start); 190 191 } 192 193 /** 194 * g_desktop_app_info_new_from_keyfile: 195 * @key_file: an opened #GKeyFile 196 * 197 * Creates a new #GDesktopAppInfo. 198 * 199 * Returns: a new #GDesktopAppInfo or %NULL on error. 200 * 201 * Since: 2.18 202 **/ 203 GDesktopAppInfo * 204 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file) 205 { 206 GDesktopAppInfo *info; 207 char *start_group; 208 char *type; 209 char *try_exec; 210 211 start_group = g_key_file_get_start_group (key_file); 212 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0) 213 { 214 g_free (start_group); 215 return NULL; 216 } 217 g_free (start_group); 218 219 type = g_key_file_get_string (key_file, 220 G_KEY_FILE_DESKTOP_GROUP, 221 G_KEY_FILE_DESKTOP_KEY_TYPE, 222 NULL); 223 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0) 224 { 225 g_free (type); 226 return NULL; 227 } 228 g_free (type); 229 230 try_exec = g_key_file_get_string (key_file, 231 G_KEY_FILE_DESKTOP_GROUP, 232 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, 233 NULL); 234 if (try_exec && try_exec[0] != '\0') 235 { 236 char *t; 237 t = g_find_program_in_path (try_exec); 238 if (t == NULL) 239 { 240 g_free (try_exec); 241 return NULL; 242 } 243 g_free (t); 244 } 245 246 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL); 247 info->filename = NULL; 248 249 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL); 250 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL); 251 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE; 252 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL); 253 info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL); 254 info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL); 255 info->try_exec = try_exec; 256 info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL); 257 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL); 258 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE; 259 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE; 260 info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE; 261 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE; 262 263 info->icon = NULL; 264 if (info->icon_name) 265 { 266 if (g_path_is_absolute (info->icon_name)) 267 { 268 GFile *file; 269 270 file = g_file_new_for_path (info->icon_name); 271 info->icon = g_file_icon_new (file); 272 g_object_unref (file); 273 } 274 else 275 { 276 char *p; 277 278 /* Work around a common mistake in desktop files */ 279 if ((p = strrchr (info->icon_name, '.')) != NULL && 280 (strcmp (p, ".png") == 0 || 281 strcmp (p, ".xpm") == 0 || 282 strcmp (p, ".svg") == 0)) 283 *p = 0; 284 285 info->icon = g_themed_icon_new (info->icon_name); 286 } 287 } 288 289 if (info->exec) 290 info->binary = binary_from_exec (info->exec); 291 292 if (info->path && info->path[0] == '\0') 293 { 294 g_free (info->path); 295 info->path = NULL; 296 } 297 298 return info; 299 } 300 301 /** 302 * g_desktop_app_info_new_from_filename: 303 * @filename: the path of a desktop file, in the GLib filename encoding 304 * 305 * Creates a new #GDesktopAppInfo. 306 * 307 * Returns: a new #GDesktopAppInfo or %NULL on error. 308 **/ 309 GDesktopAppInfo * 310 g_desktop_app_info_new_from_filename (const char *filename) 311 { 312 GKeyFile *key_file; 313 GDesktopAppInfo *info = NULL; 314 315 key_file = g_key_file_new (); 316 317 if (g_key_file_load_from_file (key_file, 318 filename, 319 G_KEY_FILE_NONE, 320 NULL)) 321 { 322 info = g_desktop_app_info_new_from_keyfile (key_file); 323 if (info) 324 info->filename = g_strdup (filename); 325 } 326 327 g_key_file_free (key_file); 328 329 return info; 330 } 331 332 /** 333 * g_desktop_app_info_new: 334 * @desktop_id: the desktop file id 335 * 336 * Creates a new #GDesktopAppInfo based on a desktop file id. 337 * 338 * A desktop file id is the basename of the desktop file, including the 339 * .desktop extension. GIO is looking for a desktop file with this name 340 * in the <filename>applications</filename> subdirectories of the XDG data 341 * directories (i.e. the directories specified in the 342 * <envar>XDG_DATA_HOME</envar> and <envar>XDG_DATA_DIRS</envar> environment 343 * variables). GIO also supports the prefix-to-subdirectory mapping that is 344 * described in the <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Menu Spec</ulink> 345 * (i.e. a desktop id of kde-foo.desktop will match 346 * <filename>/usr/share/applications/kde/foo.desktop</filename>). 347 * 348 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id 349 */ 350 GDesktopAppInfo * 351 g_desktop_app_info_new (const char *desktop_id) 352 { 353 GDesktopAppInfo *appinfo; 354 const char * const *dirs; 355 char *basename; 356 int i; 357 358 dirs = get_applications_search_path (); 359 360 basename = g_strdup (desktop_id); 361 362 for (i = 0; dirs[i] != NULL; i++) 363 { 364 char *filename; 365 char *p; 366 367 filename = g_build_filename (dirs[i], desktop_id, NULL); 368 appinfo = g_desktop_app_info_new_from_filename (filename); 369 g_free (filename); 370 if (appinfo != NULL) 371 goto found; 372 373 p = basename; 374 while ((p = strchr (p, '-')) != NULL) 375 { 376 *p = '/'; 377 378 filename = g_build_filename (dirs[i], basename, NULL); 379 appinfo = g_desktop_app_info_new_from_filename (filename); 380 g_free (filename); 381 if (appinfo != NULL) 382 goto found; 383 *p = '-'; 384 p++; 385 } 386 } 387 388 g_free (basename); 389 return NULL; 390 391 found: 392 g_free (basename); 393 394 appinfo->desktop_id = g_strdup (desktop_id); 395 396 if (g_desktop_app_info_get_is_hidden (appinfo)) 397 { 398 g_object_unref (appinfo); 399 appinfo = NULL; 400 } 401 402 return appinfo; 403 } 404 405 static GAppInfo * 406 g_desktop_app_info_dup (GAppInfo *appinfo) 407 { 408 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 409 GDesktopAppInfo *new_info; 410 411 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL); 412 413 new_info->filename = g_strdup (info->filename); 414 new_info->desktop_id = g_strdup (info->desktop_id); 415 416 new_info->name = g_strdup (info->name); 417 new_info->comment = g_strdup (info->comment); 418 new_info->nodisplay = info->nodisplay; 419 new_info->icon_name = g_strdup (info->icon_name); 420 if (info->icon) 421 new_info->icon = g_object_ref (info->icon); 422 new_info->only_show_in = g_strdupv (info->only_show_in); 423 new_info->not_show_in = g_strdupv (info->not_show_in); 424 new_info->try_exec = g_strdup (info->try_exec); 425 new_info->exec = g_strdup (info->exec); 426 new_info->binary = g_strdup (info->binary); 427 new_info->path = g_strdup (info->path); 428 new_info->hidden = info->hidden; 429 new_info->terminal = info->terminal; 430 new_info->startup_notify = info->startup_notify; 431 432 return G_APP_INFO (new_info); 433 } 434 435 static gboolean 436 g_desktop_app_info_equal (GAppInfo *appinfo1, 437 GAppInfo *appinfo2) 438 { 439 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1); 440 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2); 441 442 if (info1->desktop_id == NULL || 443 info2->desktop_id == NULL) 444 return info1 == info2; 445 446 return strcmp (info1->desktop_id, info2->desktop_id) == 0; 447 } 448 449 static const char * 450 g_desktop_app_info_get_id (GAppInfo *appinfo) 451 { 452 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 453 454 return info->desktop_id; 455 } 456 457 static const char * 458 g_desktop_app_info_get_name (GAppInfo *appinfo) 459 { 460 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 461 462 if (info->name == NULL) 463 return _("Unnamed"); 464 return info->name; 465 } 466 467 /** 468 * g_desktop_app_info_get_is_hidden: 469 * @info: a #GDesktopAppInfo. 470 * 471 * A desktop file is hidden if the Hidden key in it is 472 * set to True. 473 * 474 * Returns: %TRUE if hidden, %FALSE otherwise. 475 **/ 476 gboolean 477 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info) 478 { 479 return info->hidden; 480 } 481 482 static const char * 483 g_desktop_app_info_get_description (GAppInfo *appinfo) 484 { 485 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 486 487 return info->comment; 488 } 489 490 static const char * 491 g_desktop_app_info_get_executable (GAppInfo *appinfo) 492 { 493 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 494 495 return info->binary; 496 } 497 498 static const char * 499 g_desktop_app_info_get_commandline (GAppInfo *appinfo) 500 { 501 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 502 503 return info->exec; 504 } 505 506 static GIcon * 507 g_desktop_app_info_get_icon (GAppInfo *appinfo) 508 { 509 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 510 511 return info->icon; 512 } 513 514 static char * 515 expand_macro_single (char macro, char *uri) 516 { 517 GFile *file; 518 char *result = NULL; 519 char *path, *name; 520 521 file = g_file_new_for_uri (uri); 522 path = g_file_get_path (file); 523 g_object_unref (file); 524 525 switch (macro) 526 { 527 case 'u': 528 case 'U': 529 result = g_shell_quote (uri); 530 break; 531 case 'f': 532 case 'F': 533 if (path) 534 result = g_shell_quote (path); 535 break; 536 case 'd': 537 case 'D': 538 if (path) 539 { 540 name = g_path_get_dirname (path); 541 result = g_shell_quote (name); 542 g_free (name); 543 } 544 break; 545 case 'n': 546 case 'N': 547 if (path) 548 { 549 name = g_path_get_basename (path); 550 result = g_shell_quote (name); 551 g_free (name); 552 } 553 break; 554 } 555 556 g_free (path); 557 558 return result; 559 } 560 561 static void 562 expand_macro (char macro, 563 GString *exec, 564 GDesktopAppInfo *info, 565 GList **uri_list) 566 { 567 GList *uris = *uri_list; 568 char *expanded; 569 gboolean force_file_uri; 570 char force_file_uri_macro; 571 char *uri; 572 573 g_return_if_fail (exec != NULL); 574 575 /* On %u and %U, pass POSIX file path pointing to the URI via 576 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't 577 * running or the URI doesn't have a POSIX file path via FUSE 578 * we'll just pass the URI. 579 */ 580 force_file_uri_macro = macro; 581 force_file_uri = FALSE; 582 if (!info->no_fuse) 583 { 584 switch (macro) 585 { 586 case 'u': 587 force_file_uri_macro = 'f'; 588 force_file_uri = TRUE; 589 break; 590 case 'U': 591 force_file_uri_macro = 'F'; 592 force_file_uri = TRUE; 593 break; 594 default: 595 break; 596 } 597 } 598 599 switch (macro) 600 { 601 case 'u': 602 case 'f': 603 case 'd': 604 case 'n': 605 if (uris) 606 { 607 uri = uris->data; 608 if (!force_file_uri || 609 /* Pass URI if it contains an anchor */ 610 strchr (uri, '#') != NULL) 611 { 612 expanded = expand_macro_single (macro, uri); 613 } 614 else 615 { 616 expanded = expand_macro_single (force_file_uri_macro, uri); 617 if (expanded == NULL) 618 expanded = expand_macro_single (macro, uri); 619 } 620 621 if (expanded) 622 { 623 g_string_append (exec, expanded); 624 g_free (expanded); 625 } 626 uris = uris->next; 627 } 628 629 break; 630 631 case 'U': 632 case 'F': 633 case 'D': 634 case 'N': 635 while (uris) 636 { 637 uri = uris->data; 638 639 if (!force_file_uri || 640 /* Pass URI if it contains an anchor */ 641 strchr (uri, '#') != NULL) 642 { 643 expanded = expand_macro_single (macro, uri); 644 } 645 else 646 { 647 expanded = expand_macro_single (force_file_uri_macro, uri); 648 if (expanded == NULL) 649 expanded = expand_macro_single (macro, uri); 650 } 651 652 if (expanded) 653 { 654 g_string_append (exec, expanded); 655 g_free (expanded); 656 } 657 658 uris = uris->next; 659 660 if (uris != NULL && expanded) 661 g_string_append_c (exec, ' '); 662 } 663 664 break; 665 666 case 'i': 667 if (info->icon_name) 668 { 669 g_string_append (exec, "--icon "); 670 g_string_append (exec, info->icon_name); 671 } 672 break; 673 674 case 'c': 675 if (info->name) 676 g_string_append (exec, info->name); 677 break; 678 679 case 'k': 680 if (info->filename) 681 g_string_append (exec, info->filename); 682 break; 683 684 case 'm': /* deprecated */ 685 break; 686 687 case '%': 688 g_string_append_c (exec, '%'); 689 break; 690 } 691 692 *uri_list = uris; 693 } 694 695 static gboolean 696 expand_application_parameters (GDesktopAppInfo *info, 697 GList **uris, 698 int *argc, 699 char ***argv, 700 GError **error) 701 { 702 GList *uri_list = *uris; 703 const char *p = info->exec; 704 GString *expanded_exec = g_string_new (NULL); 705 gboolean res; 706 707 if (info->exec == NULL) 708 { 709 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, 710 _("Desktop file didn't specify Exec field")); 711 return FALSE; 712 } 713 714 while (*p) 715 { 716 if (p[0] == '%' && p[1] != '\0') 717 { 718 expand_macro (p[1], expanded_exec, info, uris); 719 p++; 720 } 721 else 722 g_string_append_c (expanded_exec, *p); 723 724 p++; 725 } 726 727 /* No file substitutions */ 728 if (uri_list == *uris && uri_list != NULL) 729 { 730 /* If there is no macro default to %f. This is also what KDE does */ 731 g_string_append_c (expanded_exec, ' '); 732 expand_macro ('f', expanded_exec, info, uris); 733 } 734 735 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error); 736 g_string_free (expanded_exec, TRUE); 737 return res; 738 } 739 740 static gboolean 741 prepend_terminal_to_vector (int *argc, 742 char ***argv) 743 { 744 #ifndef G_OS_WIN32 745 char **real_argv; 746 int real_argc; 747 int i, j; 748 char **term_argv = NULL; 749 int term_argc = 0; 750 char *check; 751 char **the_argv; 752 753 g_return_val_if_fail (argc != NULL, FALSE); 754 g_return_val_if_fail (argv != NULL, FALSE); 755 756 /* sanity */ 757 if(*argv == NULL) 758 *argc = 0; 759 760 the_argv = *argv; 761 762 /* compute size if not given */ 763 if (*argc < 0) 764 { 765 for (i = 0; the_argv[i] != NULL; i++) 766 ; 767 *argc = i; 768 } 769 770 term_argc = 2; 771 term_argv = g_new0 (char *, 3); 772 773 check = g_find_program_in_path ("gnome-terminal"); 774 if (check != NULL) 775 { 776 term_argv[0] = check; 777 /* Note that gnome-terminal takes -x and 778 * as -e in gnome-terminal is broken we use that. */ 779 term_argv[1] = g_strdup ("-x"); 780 } 781 else 782 { 783 if (check == NULL) 784 check = g_find_program_in_path ("nxterm"); 785 if (check == NULL) 786 check = g_find_program_in_path ("color-xterm"); 787 if (check == NULL) 788 check = g_find_program_in_path ("rxvt"); 789 if (check == NULL) 790 check = g_find_program_in_path ("xterm"); 791 if (check == NULL) 792 check = g_find_program_in_path ("dtterm"); 793 if (check == NULL) 794 { 795 check = g_strdup ("xterm"); 796 g_warning ("couldn't find a terminal, falling back to xterm"); 797 } 798 term_argv[0] = check; 799 term_argv[1] = g_strdup ("-e"); 800 } 801 802 real_argc = term_argc + *argc; 803 real_argv = g_new (char *, real_argc + 1); 804 805 for (i = 0; i < term_argc; i++) 806 real_argv[i] = term_argv[i]; 807 808 for (j = 0; j < *argc; j++, i++) 809 real_argv[i] = (char *)the_argv[j]; 810 811 real_argv[i] = NULL; 812 813 g_free (*argv); 814 *argv = real_argv; 815 *argc = real_argc; 816 817 /* we use g_free here as we sucked all the inner strings 818 * out from it into real_argv */ 819 g_free (term_argv); 820 return TRUE; 821 #else 822 return FALSE; 823 #endif /* G_OS_WIN32 */ 824 } 825 826 /* '=' is the new '\0'. 827 * DO NOT CALL unless at least one string ends with '=' 828 */ 829 static gboolean 830 is_env (const char *a, 831 const char *b) 832 { 833 while (*a == *b) 834 { 835 if (*a == 0 || *b == 0) 836 return FALSE; 837 838 if (*a == '=') 839 return TRUE; 840 841 a++; 842 b++; 843 } 844 845 return FALSE; 846 } 847 848 /* free with g_strfreev */ 849 static char ** 850 replace_env_var (char **old_environ, 851 const char *env_var, 852 const char *new_value) 853 { 854 int length, new_length; 855 int index, new_index; 856 char **new_environ; 857 int i, new_i; 858 859 /* do two things at once: 860 * - discover the length of the environment ('length') 861 * - find the location (if any) of the env var ('index') 862 */ 863 index = -1; 864 for (length = 0; old_environ[length]; length++) 865 { 866 /* if we already have it in our environment, replace */ 867 if (is_env (old_environ[length], env_var)) 868 index = length; 869 } 870 871 872 /* no current env var, no desired env value. 873 * this is easy :) 874 */ 875 if (new_value == NULL && index == -1) 876 return old_environ; 877 878 /* in all cases now, we will be using a modified environment. 879 * determine its length and allocated it. 880 * 881 * after this block: 882 * new_index = location to insert, if any 883 * new_length = length of the new array 884 * new_environ = the pointer array for the new environment 885 */ 886 887 if (new_value == NULL && index >= 0) 888 { 889 /* in this case, we will be removing an entry */ 890 new_length = length - 1; 891 new_index = -1; 892 } 893 else if (new_value != NULL && index < 0) 894 { 895 /* in this case, we will be adding an entry to the end */ 896 new_length = length + 1; 897 new_index = length; 898 } 899 else 900 /* in this case, we will be replacing the existing entry */ 901 { 902 new_length = length; 903 new_index = index; 904 } 905 906 new_environ = g_malloc (sizeof (char *) * (new_length + 1)); 907 new_environ[new_length] = NULL; 908 909 /* now we do the copying. 910 * for each entry in the new environment, we decide what to do 911 */ 912 913 i = 0; 914 for (new_i = 0; new_i < new_length; new_i++) 915 { 916 if (new_i == new_index) 917 { 918 /* insert our new item */ 919 new_environ[new_i] = g_strconcat (env_var, 920 "=", 921 new_value, 922 NULL); 923 924 /* if we had an old entry, skip it now */ 925 if (index >= 0) 926 i++; 927 } 928 else 929 { 930 /* if this is the old DESKTOP_STARTUP_ID, skip it */ 931 if (i == index) 932 i++; 933 934 /* copy an old item */ 935 new_environ[new_i] = g_strdup (old_environ[i]); 936 i++; 937 } 938 } 939 940 g_strfreev (old_environ); 941 942 return new_environ; 943 } 944 945 static GList * 946 uri_list_segment_to_files (GList *start, 947 GList *end) 948 { 949 GList *res; 950 GFile *file; 951 952 res = NULL; 953 while (start != NULL && start != end) 954 { 955 file = g_file_new_for_uri ((char *)start->data); 956 res = g_list_prepend (res, file); 957 start = start->next; 958 } 959 960 return g_list_reverse (res); 961 } 962 963 #ifdef HAVE__NSGETENVIRON 964 #define environ (*_NSGetEnviron()) 965 #elif !defined(G_OS_WIN32) 966 967 /* According to the Single Unix Specification, environ is not in 968 * * any system header, although unistd.h often declares it. 969 * */ 970 extern char **environ; 971 #endif 972 973 static gboolean 974 g_desktop_app_info_launch_uris (GAppInfo *appinfo, 975 GList *uris, 976 GAppLaunchContext *launch_context, 977 GError **error) 978 { 979 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 980 gboolean completed = FALSE; 981 GList *old_uris; 982 GList *launched_files; 983 char **envp; 984 char **argv; 985 int argc; 986 char *display; 987 char *sn_id; 988 989 g_return_val_if_fail (appinfo != NULL, FALSE); 990 991 argv = NULL; 992 envp = NULL; 993 994 do 995 { 996 old_uris = uris; 997 if (!expand_application_parameters (info, &uris, 998 &argc, &argv, error)) 999 goto out; 1000 1001 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv)) 1002 { 1003 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, 1004 _("Unable to find terminal required for application")); 1005 goto out; 1006 } 1007 1008 sn_id = NULL; 1009 if (launch_context) 1010 { 1011 launched_files = uri_list_segment_to_files (old_uris, uris); 1012 1013 display = g_app_launch_context_get_display (launch_context, 1014 appinfo, 1015 launched_files); 1016 1017 sn_id = NULL; 1018 if (info->startup_notify) 1019 sn_id = g_app_launch_context_get_startup_notify_id (launch_context, 1020 appinfo, 1021 launched_files); 1022 1023 if (display || sn_id) 1024 { 1025 #ifdef G_OS_WIN32 1026 /* FIXME */ 1027 envp = g_new0 (char *, 1); 1028 #else 1029 envp = g_strdupv (environ); 1030 #endif 1031 1032 if (display) 1033 envp = replace_env_var (envp, 1034 "DISPLAY", 1035 display); 1036 1037 if (sn_id) 1038 envp = replace_env_var (envp, 1039 "DESKTOP_STARTUP_ID", 1040 sn_id); 1041 } 1042 1043 g_free (display); 1044 1045 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL); 1046 g_list_free (launched_files); 1047 } 1048 1049 if (!g_spawn_async (info->path, /* working directory */ 1050 argv, 1051 envp, 1052 G_SPAWN_SEARCH_PATH /* flags */, 1053 NULL /* child_setup */, 1054 NULL /* data */, 1055 NULL /* child_pid */, 1056 error)) 1057 { 1058 if (sn_id) 1059 { 1060 g_app_launch_context_launch_failed (launch_context, sn_id); 1061 g_free (sn_id); 1062 } 1063 goto out; 1064 } 1065 1066 1067 g_free (sn_id); 1068 1069 g_strfreev (envp); 1070 g_strfreev (argv); 1071 envp = NULL; 1072 argv = NULL; 1073 } 1074 while (uris != NULL); 1075 1076 completed = TRUE; 1077 1078 out: 1079 g_strfreev (argv); 1080 g_strfreev (envp); 1081 1082 return completed; 1083 } 1084 1085 static gboolean 1086 g_desktop_app_info_supports_uris (GAppInfo *appinfo) 1087 { 1088 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1089 1090 return info->exec && 1091 ((strstr (info->exec, "%u") != NULL) || 1092 (strstr (info->exec, "%U") != NULL)); 1093 } 1094 1095 static gboolean 1096 g_desktop_app_info_supports_files (GAppInfo *appinfo) 1097 { 1098 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1099 1100 return info->exec && 1101 ((strstr (info->exec, "%f") != NULL) || 1102 (strstr (info->exec, "%F") != NULL)); 1103 } 1104 1105 static gboolean 1106 g_desktop_app_info_launch (GAppInfo *appinfo, 1107 GList *files, 1108 GAppLaunchContext *launch_context, 1109 GError **error) 1110 { 1111 GList *uris; 1112 char *uri; 1113 gboolean res; 1114 1115 uris = NULL; 1116 while (files) 1117 { 1118 uri = g_file_get_uri (files->data); 1119 uris = g_list_prepend (uris, uri); 1120 files = files->next; 1121 } 1122 1123 uris = g_list_reverse (uris); 1124 1125 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error); 1126 1127 g_list_foreach (uris, (GFunc)g_free, NULL); 1128 g_list_free (uris); 1129 1130 return res; 1131 } 1132 1133 G_LOCK_DEFINE_STATIC (g_desktop_env); 1134 static gchar *g_desktop_env = NULL; 1135 1136 /** 1137 * g_desktop_app_info_set_desktop_env: 1138 * @desktop_env: a string specifying what desktop this is 1139 * 1140 * Sets the name of the desktop that the application is running in. 1141 * This is used by g_app_info_should_show() to evaluate the 1142 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal> 1143 * desktop entry fields. 1144 * 1145 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop 1146 * Menu specification</ulink> recognizes the following: 1147 * <simplelist> 1148 * <member>GNOME</member> 1149 * <member>KDE</member> 1150 * <member>ROX</member> 1151 * <member>XFCE</member> 1152 * <member>Old</member> 1153 * </simplelist> 1154 * 1155 * Should be called only once; subsequent calls are ignored. 1156 */ 1157 void 1158 g_desktop_app_info_set_desktop_env (const gchar *desktop_env) 1159 { 1160 G_LOCK (g_desktop_env); 1161 if (!g_desktop_env) 1162 g_desktop_env = g_strdup (desktop_env); 1163 G_UNLOCK (g_desktop_env); 1164 } 1165 1166 static gboolean 1167 g_desktop_app_info_should_show (GAppInfo *appinfo) 1168 { 1169 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1170 gboolean found; 1171 const gchar *desktop_env; 1172 int i; 1173 1174 if (info->nodisplay) 1175 return FALSE; 1176 1177 G_LOCK (g_desktop_env); 1178 desktop_env = g_desktop_env; 1179 G_UNLOCK (g_desktop_env); 1180 1181 if (info->only_show_in) 1182 { 1183 if (desktop_env == NULL) 1184 return FALSE; 1185 1186 found = FALSE; 1187 for (i = 0; info->only_show_in[i] != NULL; i++) 1188 { 1189 if (strcmp (info->only_show_in[i], desktop_env) == 0) 1190 { 1191 found = TRUE; 1192 break; 1193 } 1194 } 1195 if (!found) 1196 return FALSE; 1197 } 1198 1199 if (info->not_show_in && desktop_env) 1200 { 1201 for (i = 0; info->not_show_in[i] != NULL; i++) 1202 { 1203 if (strcmp (info->not_show_in[i], desktop_env) == 0) 1204 return FALSE; 1205 } 1206 } 1207 1208 return TRUE; 1209 } 1210 1211 typedef enum { 1212 APP_DIR, 1213 MIMETYPE_DIR 1214 } DirType; 1215 1216 static char * 1217 ensure_dir (DirType type, 1218 GError **error) 1219 { 1220 char *path, *display_name; 1221 int errsv; 1222 1223 if (type == APP_DIR) 1224 path = g_build_filename (g_get_user_data_dir (), "applications", NULL); 1225 else 1226 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL); 1227 1228 errno = 0; 1229 if (g_mkdir_with_parents (path, 0700) == 0) 1230 return path; 1231 1232 errsv = errno; 1233 display_name = g_filename_display_name (path); 1234 if (type == APP_DIR) 1235 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), 1236 _("Can't create user application configuration folder %s: %s"), 1237 display_name, g_strerror (errsv)); 1238 else 1239 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), 1240 _("Can't create user MIME configuration folder %s: %s"), 1241 display_name, g_strerror (errsv)); 1242 1243 g_free (display_name); 1244 g_free (path); 1245 1246 return NULL; 1247 } 1248 1249 static gboolean 1250 update_mimeapps_list (const char *desktop_id, 1251 const char *content_type, 1252 gboolean add_as_default, 1253 gboolean add_non_default, 1254 gboolean remove, 1255 GError **error) 1256 { 1257 char *dirname, *filename; 1258 GKeyFile *key_file; 1259 gboolean load_succeeded, res; 1260 char **old_list, **list; 1261 GList *system_list, *l; 1262 gsize length, data_size; 1263 char *data; 1264 int i, j, k; 1265 char **content_types; 1266 1267 /* Don't add both at start and end */ 1268 g_assert (!(add_as_default && add_non_default)); 1269 1270 dirname = ensure_dir (APP_DIR, error); 1271 if (!dirname) 1272 return FALSE; 1273 1274 filename = g_build_filename (dirname, "mimeapps.list", NULL); 1275 g_free (dirname); 1276 1277 key_file = g_key_file_new (); 1278 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL); 1279 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP)) 1280 { 1281 g_key_file_free (key_file); 1282 key_file = g_key_file_new (); 1283 } 1284 1285 if (content_type) 1286 { 1287 content_types = g_new (char *, 2); 1288 content_types[0] = g_strdup (content_type); 1289 content_types[1] = NULL; 1290 } 1291 else 1292 { 1293 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL); 1294 } 1295 1296 for (k = 0; content_types && content_types[k]; k++) 1297 { 1298 /* Add to the right place in the list */ 1299 1300 length = 0; 1301 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP, 1302 content_types[k], &length, NULL); 1303 1304 list = g_new (char *, 1 + length + 1); 1305 1306 i = 0; 1307 if (add_as_default) 1308 list[i++] = g_strdup (desktop_id); 1309 if (old_list) 1310 { 1311 for (j = 0; old_list[j] != NULL; j++) 1312 { 1313 if (g_strcmp0 (old_list[j], desktop_id) != 0) 1314 list[i++] = g_strdup (old_list[j]); 1315 else if (add_non_default) 1316 { 1317 /* If adding as non-default, and it's already in, 1318 don't change order of desktop ids */ 1319 add_non_default = FALSE; 1320 list[i++] = g_strdup (old_list[j]); 1321 } 1322 } 1323 } 1324 1325 if (add_non_default) 1326 { 1327 /* We're adding as non-default, and it wasn't already in the list, 1328 so we add at the end. But to avoid listing the app before the 1329 current system default (thus changing the default) we have to 1330 add the current list of (not yet listed) apps before it. */ 1331 1332 list[i] = NULL; /* Terminate current list so we can use it */ 1333 system_list = get_all_desktop_entries_for_mime_type (content_type, (const char **)list); 1334 1335 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1); 1336 1337 for (l = system_list; l != NULL; l = l->next) 1338 { 1339 list[i++] = l->data; /* no strdup, taking ownership */ 1340 if (g_strcmp0 (l->data, desktop_id) == 0) 1341 add_non_default = FALSE; 1342 } 1343 g_list_free (system_list); 1344 1345 if (add_non_default) 1346 list[i++] = g_strdup (desktop_id); 1347 } 1348 1349 list[i] = NULL; 1350 1351 g_strfreev (old_list); 1352 1353 if (list[0] == NULL || desktop_id == NULL) 1354 g_key_file_remove_key (key_file, 1355 ADDED_ASSOCIATIONS_GROUP, 1356 content_types[k], 1357 NULL); 1358 else 1359 g_key_file_set_string_list (key_file, 1360 ADDED_ASSOCIATIONS_GROUP, 1361 content_types[k], 1362 (const char * const *)list, i); 1363 1364 g_strfreev (list); 1365 } 1366 1367 if (content_type) 1368 { 1369 /* reuse the list from above */ 1370 } 1371 else 1372 { 1373 g_strfreev (content_types); 1374 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL); 1375 } 1376 1377 for (k = 0; content_types && content_types[k]; k++) 1378 { 1379 /* Remove from removed associations group (unless remove) */ 1380 1381 length = 0; 1382 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP, 1383 content_types[k], &length, NULL); 1384 1385 list = g_new (char *, 1 + length + 1); 1386 1387 i = 0; 1388 if (remove) 1389 list[i++] = g_strdup (desktop_id); 1390 if (old_list) 1391 { 1392 for (j = 0; old_list[j] != NULL; j++) 1393 { 1394 if (g_strcmp0 (old_list[j], desktop_id) != 0) 1395 list[i++] = g_strdup (old_list[j]); 1396 } 1397 } 1398 list[i] = NULL; 1399 1400 g_strfreev (old_list); 1401 1402 if (list[0] == NULL || desktop_id == NULL) 1403 g_key_file_remove_key (key_file, 1404 REMOVED_ASSOCIATIONS_GROUP, 1405 content_types[k], 1406 NULL); 1407 else 1408 g_key_file_set_string_list (key_file, 1409 REMOVED_ASSOCIATIONS_GROUP, 1410 content_types[k], 1411 (const char * const *)list, i); 1412 1413 g_strfreev (list); 1414 } 1415 1416 g_strfreev (content_types); 1417 1418 data = g_key_file_to_data (key_file, &data_size, error); 1419 g_key_file_free (key_file); 1420 1421 res = g_file_set_contents (filename, data, data_size, error); 1422 1423 mime_info_cache_reload (NULL); 1424 1425 g_free (filename); 1426 g_free (data); 1427 1428 return res; 1429 } 1430 1431 static gboolean 1432 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo, 1433 const char *content_type, 1434 GError **error) 1435 { 1436 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1437 1438 if (!g_desktop_app_info_ensure_saved (info, error)) 1439 return FALSE; 1440 1441 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error); 1442 } 1443 1444 static void 1445 update_program_done (GPid pid, 1446 gint status, 1447 gpointer data) 1448 { 1449 /* Did the application exit correctly */ 1450 if (WIFEXITED (status) && 1451 WEXITSTATUS (status) == 0) 1452 { 1453 /* Here we could clean out any caches in use */ 1454 } 1455 } 1456 1457 static void 1458 run_update_command (char *command, 1459 char *subdir) 1460 { 1461 char *argv[3] = { 1462 NULL, 1463 NULL, 1464 NULL, 1465 }; 1466 GPid pid = 0; 1467 GError *error = NULL; 1468 1469 argv[0] = command; 1470 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL); 1471 1472 if (g_spawn_async ("/", argv, 1473 NULL, /* envp */ 1474 G_SPAWN_SEARCH_PATH | 1475 G_SPAWN_STDOUT_TO_DEV_NULL | 1476 G_SPAWN_STDERR_TO_DEV_NULL | 1477 G_SPAWN_DO_NOT_REAP_CHILD, 1478 NULL, NULL, /* No setup function */ 1479 &pid, 1480 &error)) 1481 g_child_watch_add (pid, update_program_done, NULL); 1482 else 1483 { 1484 /* If we get an error at this point, it's quite likely the user doesn't 1485 * have an installed copy of either 'update-mime-database' or 1486 * 'update-desktop-database'. I don't think we want to popup an error 1487 * dialog at this point, so we just do a g_warning to give the user a 1488 * chance of debugging it. 1489 */ 1490 g_warning ("%s", error->message); 1491 } 1492 1493 g_free (argv[1]); 1494 } 1495 1496 static gboolean 1497 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo, 1498 const char *extension, 1499 GError **error) 1500 { 1501 char *filename, *basename, *mimetype; 1502 char *dirname; 1503 gboolean res; 1504 1505 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error)) 1506 return FALSE; 1507 1508 dirname = ensure_dir (MIMETYPE_DIR, error); 1509 if (!dirname) 1510 return FALSE; 1511 1512 basename = g_strdup_printf ("user-extension-%s.xml", extension); 1513 filename = g_build_filename (dirname, basename, NULL); 1514 g_free (basename); 1515 g_free (dirname); 1516 1517 mimetype = g_strdup_printf ("application/x-extension-%s", extension); 1518 1519 if (!g_file_test (filename, G_FILE_TEST_EXISTS)) 1520 { 1521 char *contents; 1522 1523 contents = 1524 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 1525 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n" 1526 " <mime-type type=\"%s\">\n" 1527 " <comment>%s document</comment>\n" 1528 " <glob pattern=\"*.%s\"/>\n" 1529 " </mime-type>\n" 1530 "</mime-info>\n", mimetype, extension, extension); 1531 1532 g_file_set_contents (filename, contents, -1, NULL); 1533 g_free (contents); 1534 1535 run_update_command ("update-mime-database", "mime"); 1536 } 1537 g_free (filename); 1538 1539 res = g_desktop_app_info_set_as_default_for_type (appinfo, 1540 mimetype, 1541 error); 1542 1543 g_free (mimetype); 1544 1545 return res; 1546 } 1547 1548 static gboolean 1549 g_desktop_app_info_add_supports_type (GAppInfo *appinfo, 1550 const char *content_type, 1551 GError **error) 1552 { 1553 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1554 1555 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error)) 1556 return FALSE; 1557 1558 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error); 1559 } 1560 1561 static gboolean 1562 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo) 1563 { 1564 return TRUE; 1565 } 1566 1567 static gboolean 1568 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo, 1569 const char *content_type, 1570 GError **error) 1571 { 1572 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1573 1574 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error)) 1575 return FALSE; 1576 1577 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error); 1578 } 1579 1580 static gboolean 1581 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info, 1582 GError **error) 1583 { 1584 GKeyFile *key_file; 1585 char *dirname; 1586 char *filename; 1587 char *data, *desktop_id; 1588 gsize data_size; 1589 int fd; 1590 gboolean res; 1591 1592 if (info->filename != NULL) 1593 return TRUE; 1594 1595 /* This is only used for object created with 1596 * g_app_info_create_from_commandline. All other 1597 * object should have a filename 1598 */ 1599 1600 dirname = ensure_dir (APP_DIR, error); 1601 if (!dirname) 1602 return FALSE; 1603 1604 key_file = g_key_file_new (); 1605 1606 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, 1607 "Encoding", "UTF-8"); 1608 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, 1609 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0"); 1610 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, 1611 G_KEY_FILE_DESKTOP_KEY_TYPE, 1612 G_KEY_FILE_DESKTOP_TYPE_APPLICATION); 1613 if (info->terminal) 1614 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, 1615 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE); 1616 1617 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, 1618 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec); 1619 1620 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, 1621 G_KEY_FILE_DESKTOP_KEY_NAME, info->name); 1622 1623 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, 1624 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment); 1625 1626 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, 1627 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE); 1628 1629 data = g_key_file_to_data (key_file, &data_size, NULL); 1630 g_key_file_free (key_file); 1631 1632 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name); 1633 filename = g_build_filename (dirname, desktop_id, NULL); 1634 g_free (desktop_id); 1635 g_free (dirname); 1636 1637 fd = g_mkstemp (filename); 1638 if (fd == -1) 1639 { 1640 char *display_name; 1641 1642 display_name = g_filename_display_name (filename); 1643 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, 1644 _("Can't create user desktop file %s"), display_name); 1645 g_free (display_name); 1646 g_free (filename); 1647 g_free (data); 1648 return FALSE; 1649 } 1650 1651 desktop_id = g_path_get_basename (filename); 1652 1653 close (fd); 1654 1655 res = g_file_set_contents (filename, data, data_size, error); 1656 if (!res) 1657 { 1658 g_free (desktop_id); 1659 g_free (filename); 1660 return FALSE; 1661 } 1662 1663 info->filename = filename; 1664 info->desktop_id = desktop_id; 1665 1666 run_update_command ("update-desktop-database", "applications"); 1667 1668 return TRUE; 1669 } 1670 1671 static gboolean 1672 g_desktop_app_info_can_delete (GAppInfo *appinfo) 1673 { 1674 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1675 1676 if (info->filename) 1677 { 1678 if (strstr (info->filename, "/userapp-")) 1679 return g_access (info->filename, W_OK) == 0; 1680 } 1681 1682 return FALSE; 1683 } 1684 1685 static gboolean 1686 g_desktop_app_info_delete (GAppInfo *appinfo) 1687 { 1688 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); 1689 1690 if (info->filename) 1691 { 1692 if (g_remove (info->filename) == 0) 1693 { 1694 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL); 1695 1696 g_free (info->filename); 1697 info->filename = NULL; 1698 g_free (info->desktop_id); 1699 info->desktop_id = NULL; 1700 1701 return TRUE; 1702 } 1703 } 1704 1705 return FALSE; 1706 } 1707 1708 /** 1709 * g_app_info_create_from_commandline: 1710 * @commandline: the commandline to use 1711 * @application_name: the application name, or %NULL to use @commandline 1712 * @flags: flags that can specify details of the created #GAppInfo 1713 * @error: a #GError location to store the error occuring, %NULL to ignore. 1714 * 1715 * Creates a new #GAppInfo from the given information. 1716 * 1717 * Returns: new #GAppInfo for given command. 1718 **/ 1719 GAppInfo * 1720 g_app_info_create_from_commandline (const char *commandline, 1721 const char *application_name, 1722 GAppInfoCreateFlags flags, 1723 GError **error) 1724 { 1725 char **split; 1726 char *basename; 1727 GDesktopAppInfo *info; 1728 1729 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL); 1730 1731 info->filename = NULL; 1732 info->desktop_id = NULL; 1733 1734 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL; 1735 info->startup_notify = FALSE; 1736 info->hidden = FALSE; 1737 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS) 1738 info->exec = g_strconcat (commandline, " %u", NULL); 1739 else 1740 info->exec = g_strconcat (commandline, " %f", NULL); 1741 info->nodisplay = TRUE; 1742 info->binary = binary_from_exec (info->exec); 1743 1744 if (application_name) 1745 info->name = g_strdup (application_name); 1746 else 1747 { 1748 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */ 1749 split = g_strsplit (commandline, " ", 2); 1750 basename = g_path_get_basename (split[0]); 1751 g_strfreev (split); 1752 info->name = basename; 1753 if (info->name == NULL) 1754 info->name = g_strdup ("custom"); 1755 } 1756 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name); 1757 1758 return G_APP_INFO (info); 1759 } 1760 1761 static void 1762 g_desktop_app_info_iface_init (GAppInfoIface *iface) 1763 { 1764 iface->dup = g_desktop_app_info_dup; 1765 iface->equal = g_desktop_app_info_equal; 1766 iface->get_id = g_desktop_app_info_get_id; 1767 iface->get_name = g_desktop_app_info_get_name; 1768 iface->get_description = g_desktop_app_info_get_description; 1769 iface->get_executable = g_desktop_app_info_get_executable; 1770 iface->get_icon = g_desktop_app_info_get_icon; 1771 iface->launch = g_desktop_app_info_launch; 1772 iface->supports_uris = g_desktop_app_info_supports_uris; 1773 iface->supports_files = g_desktop_app_info_supports_files; 1774 iface->launch_uris = g_desktop_app_info_launch_uris; 1775 iface->should_show = g_desktop_app_info_should_show; 1776 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type; 1777 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension; 1778 iface->add_supports_type = g_desktop_app_info_add_supports_type; 1779 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type; 1780 iface->remove_supports_type = g_desktop_app_info_remove_supports_type; 1781 iface->can_delete = g_desktop_app_info_can_delete; 1782 iface->do_delete = g_desktop_app_info_delete; 1783 iface->get_commandline = g_desktop_app_info_get_commandline; 1784 } 1785 1786 static gboolean 1787 app_info_in_list (GAppInfo *info, 1788 GList *list) 1789 { 1790 while (list != NULL) 1791 { 1792 if (g_app_info_equal (info, list->data)) 1793 return TRUE; 1794 list = list->next; 1795 } 1796 return FALSE; 1797 } 1798 1799 1800 /** 1801 * g_app_info_get_all_for_type: 1802 * @content_type: the content type to find a #GAppInfo for 1803 * 1804 * Gets a list of all #GAppInfo s for a given content type. 1805 * 1806 * Returns: #GList of #GAppInfo s for given @content_type 1807 * or %NULL on error. 1808 **/ 1809 GList * 1810 g_app_info_get_all_for_type (const char *content_type) 1811 { 1812 GList *desktop_entries, *l; 1813 GList *infos; 1814 GDesktopAppInfo *info; 1815 1816 g_return_val_if_fail (content_type != NULL, NULL); 1817 1818 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL); 1819 1820 infos = NULL; 1821 for (l = desktop_entries; l != NULL; l = l->next) 1822 { 1823 char *desktop_entry = l->data; 1824 1825 info = g_desktop_app_info_new (desktop_entry); 1826 if (info) 1827 { 1828 if (app_info_in_list (G_APP_INFO (info), infos)) 1829 g_object_unref (info); 1830 else 1831 infos = g_list_prepend (infos, info); 1832 } 1833 g_free (desktop_entry); 1834 } 1835 1836 g_list_free (desktop_entries); 1837 1838 return g_list_reverse (infos); 1839 } 1840 1841 /** 1842 * g_app_info_reset_type_associations: 1843 * @content_type: a content type 1844 * 1845 * Removes all changes to the type associations done by 1846 * g_app_info_set_as_default_for_type(), 1847 * g_app_info_set_as_default_for_extension(), 1848 * g_app_info_add_supports_type() of g_app_info_remove_supports_type(). 1849 * 1850 * Since: 2.20 1851 */ 1852 void 1853 g_app_info_reset_type_associations (const char *content_type) 1854 { 1855 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL); 1856 } 1857 1858 /** 1859 * g_app_info_get_default_for_type: 1860 * @content_type: the content type to find a #GAppInfo for 1861 * @must_support_uris: if %TRUE, the #GAppInfo is expected to 1862 * support URIs 1863 * 1864 * Gets the #GAppInfo that correspond to a given content type. 1865 * 1866 * Returns: #GAppInfo for given @content_type or %NULL on error. 1867 **/ 1868 GAppInfo * 1869 g_app_info_get_default_for_type (const char *content_type, 1870 gboolean must_support_uris) 1871 { 1872 GList *desktop_entries, *l; 1873 GAppInfo *info; 1874 1875 g_return_val_if_fail (content_type != NULL, NULL); 1876 1877 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL); 1878 1879 info = NULL; 1880 for (l = desktop_entries; l != NULL; l = l->next) 1881 { 1882 char *desktop_entry = l->data; 1883 1884 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry); 1885 if (info) 1886 { 1887 if (must_support_uris && !g_app_info_supports_uris (info)) 1888 { 1889 g_object_unref (info); 1890 info = NULL; 1891 } 1892 else 1893 break; 1894 } 1895 } 1896 1897 g_list_foreach (desktop_entries, (GFunc)g_free, NULL); 1898 g_list_free (desktop_entries); 1899 1900 return info; 1901 } 1902 1903 /** 1904 * g_app_info_get_default_for_uri_scheme: 1905 * @uri_scheme: a string containing a URI scheme. 1906 * 1907 * Gets the default application for launching applications 1908 * using this URI scheme. A URI scheme is the initial part 1909 * of the URI, up to but not including the ':', e.g. "http", 1910 * "ftp" or "sip". 1911 * 1912 * Returns: #GAppInfo for given @uri_scheme or %NULL on error. 1913 **/ 1914 GAppInfo * 1915 g_app_info_get_default_for_uri_scheme (const char *uri_scheme) 1916 { 1917 static gsize lookup = 0; 1918 1919 if (g_once_init_enter (&lookup)) 1920 { 1921 gsize setup_value = 1; 1922 GDesktopAppInfoLookup *lookup_instance; 1923 const char *use_this; 1924 GIOExtensionPoint *ep; 1925 GIOExtension *extension; 1926 GList *l; 1927 1928 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION"); 1929 1930 /* Ensure vfs in modules loaded */ 1931 _g_io_modules_ensure_loaded (); 1932 1933 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME); 1934 1935 lookup_instance = NULL; 1936 if (use_this) 1937 { 1938 extension = g_io_extension_point_get_extension_by_name (ep, use_this); 1939 if (extension) 1940 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL); 1941 } 1942 1943 if (lookup_instance == NULL) 1944 { 1945 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next) 1946 { 1947 extension = l->data; 1948 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL); 1949 if (lookup_instance != NULL) 1950 break; 1951 } 1952 } 1953 1954 if (lookup_instance != NULL) 1955 setup_value = (gsize)lookup_instance; 1956 1957 g_once_init_leave (&lookup, setup_value); 1958 } 1959 1960 if (lookup == 1) 1961 return NULL; 1962 1963 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup), 1964 uri_scheme); 1965 } 1966 1967 1968 static void 1969 get_apps_from_dir (GHashTable *apps, 1970 const char *dirname, 1971 const char *prefix) 1972 { 1973 GDir *dir; 1974 const char *basename; 1975 char *filename, *subprefix, *desktop_id; 1976 gboolean hidden; 1977 GDesktopAppInfo *appinfo; 1978 1979 dir = g_dir_open (dirname, 0, NULL); 1980 if (dir) 1981 { 1982 while ((basename = g_dir_read_name (dir)) != NULL) 1983 { 1984 filename = g_build_filename (dirname, basename, NULL); 1985 if (g_str_has_suffix (basename, ".desktop")) 1986 { 1987 desktop_id = g_strconcat (prefix, basename, NULL); 1988 1989 /* Use _extended so we catch NULLs too (hidden) */ 1990 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL)) 1991 { 1992 appinfo = g_desktop_app_info_new_from_filename (filename); 1993 1994 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo)) 1995 { 1996 g_object_unref (appinfo); 1997 appinfo = NULL; 1998 hidden = TRUE; 1999 } 2000 2001 if (appinfo || hidden) 2002 { 2003 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo); 2004 2005 if (appinfo) 2006 { 2007 /* Reuse instead of strdup here */ 2008 appinfo->desktop_id = desktop_id; 2009 desktop_id = NULL; 2010 } 2011 } 2012 } 2013 g_free (desktop_id); 2014 } 2015 else 2016 { 2017 if (g_file_test (filename, G_FILE_TEST_IS_DIR)) 2018 { 2019 subprefix = g_strconcat (prefix, basename, "-", NULL); 2020 get_apps_from_dir (apps, filename, subprefix); 2021 g_free (subprefix); 2022 } 2023 } 2024 g_free (filename); 2025 } 2026 g_dir_close (dir); 2027 } 2028 } 2029 2030 2031 /** 2032 * g_app_info_get_all: 2033 * 2034 * Gets a list of all of the applications currently registered 2035 * on this system. 2036 * 2037 * For desktop files, this includes applications that have 2038 * <literal>NoDisplay=true</literal> set or are excluded from 2039 * display by means of <literal>OnlyShowIn</literal> or 2040 * <literal>NotShowIn</literal>. See g_app_info_should_show(). 2041 * The returned list does not include applications which have 2042 * the <literal>Hidden</literal> key set. 2043 * 2044 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s. 2045 **/ 2046 GList * 2047 g_app_info_get_all (void) 2048 { 2049 const char * const *dirs; 2050 GHashTable *apps; 2051 GHashTableIter iter; 2052 gpointer value; 2053 int i; 2054 GList *infos; 2055 2056 dirs = get_applications_search_path (); 2057 2058 apps = g_hash_table_new_full (g_str_hash, g_str_equal, 2059 g_free, NULL); 2060 2061 2062 for (i = 0; dirs[i] != NULL; i++) 2063 get_apps_from_dir (apps, dirs[i], ""); 2064 2065 2066 infos = NULL; 2067 g_hash_table_iter_init (&iter, apps); 2068 while (g_hash_table_iter_next (&iter, NULL, &value)) 2069 { 2070 if (value) 2071 infos = g_list_prepend (infos, value); 2072 } 2073 2074 g_hash_table_destroy (apps); 2075 2076 return g_list_reverse (infos); 2077 } 2078 2079 /* Cacheing of mimeinfo.cache and defaults.list files */ 2080 2081 typedef struct { 2082 char *path; 2083 GHashTable *mime_info_cache_map; 2084 GHashTable *defaults_list_map; 2085 GHashTable *mimeapps_list_added_map; 2086 GHashTable *mimeapps_list_removed_map; 2087 time_t mime_info_cache_timestamp; 2088 time_t defaults_list_timestamp; 2089 time_t mimeapps_list_timestamp; 2090 } MimeInfoCacheDir; 2091 2092 typedef struct { 2093 GList *dirs; /* mimeinfo.cache and defaults.list */ 2094 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */ 2095 time_t last_stat_time; 2096 guint should_ping_mime_monitor : 1; 2097 } MimeInfoCache; 2098 2099 static MimeInfoCache *mime_info_cache = NULL; 2100 G_LOCK_DEFINE_STATIC (mime_info_cache); 2101 2102 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir, 2103 const char *mime_type, 2104 char **new_desktop_file_ids); 2105 2106 static MimeInfoCache * mime_info_cache_new (void); 2107 2108 static void 2109 destroy_info_cache_value (gpointer key, 2110 GList *value, 2111 gpointer data) 2112 { 2113 g_list_foreach (value, (GFunc)g_free, NULL); 2114 g_list_free (value); 2115 } 2116 2117 static void 2118 destroy_info_cache_map (GHashTable *info_cache_map) 2119 { 2120 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL); 2121 g_hash_table_destroy (info_cache_map); 2122 } 2123 2124 static gboolean 2125 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir, 2126 const char *cache_file, 2127 time_t *timestamp) 2128 { 2129 struct stat buf; 2130 char *filename; 2131 2132 filename = g_build_filename (dir->path, cache_file, NULL); 2133 2134 if (g_stat (filename, &buf) < 0) 2135 { 2136 g_free (filename); 2137 return TRUE; 2138 } 2139 g_free (filename); 2140 2141 if (buf.st_mtime != *timestamp) 2142 return TRUE; 2143 2144 return FALSE; 2145 } 2146 2147 /* Call with lock held */ 2148 static gboolean 2149 remove_all (gpointer key, 2150 gpointer value, 2151 gpointer user_data) 2152 { 2153 return TRUE; 2154 } 2155 2156 2157 static void 2158 mime_info_cache_blow_global_cache (void) 2159 { 2160 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache, 2161 remove_all, NULL); 2162 } 2163 2164 static void 2165 mime_info_cache_dir_init (MimeInfoCacheDir *dir) 2166 { 2167 GError *load_error; 2168 GKeyFile *key_file; 2169 gchar *filename, **mime_types; 2170 int i; 2171 struct stat buf; 2172 2173 load_error = NULL; 2174 mime_types = NULL; 2175 2176 if (dir->mime_info_cache_map != NULL && 2177 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache", 2178 &dir->mime_info_cache_timestamp)) 2179 return; 2180 2181 if (dir->mime_info_cache_map != NULL) 2182 destroy_info_cache_map (dir->mime_info_cache_map); 2183 2184 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal, 2185 (GDestroyNotify) g_free, 2186 NULL); 2187 2188 key_file = g_key_file_new (); 2189 2190 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL); 2191 2192 if (g_stat (filename, &buf) < 0) 2193 goto error; 2194 2195 if (dir->mime_info_cache_timestamp > 0) 2196 mime_info_cache->should_ping_mime_monitor = TRUE; 2197 2198 dir->mime_info_cache_timestamp = buf.st_mtime; 2199 2200 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error); 2201 2202 g_free (filename); 2203 filename = NULL; 2204 2205 if (load_error != NULL) 2206 goto error; 2207 2208 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP, 2209 NULL, &load_error); 2210 2211 if (load_error != NULL) 2212 goto error; 2213 2214 for (i = 0; mime_types[i] != NULL; i++) 2215 { 2216 gchar **desktop_file_ids; 2217 char *unaliased_type; 2218 desktop_file_ids = g_key_file_get_string_list (key_file, 2219 MIME_CACHE_GROUP, 2220 mime_types[i], 2221 NULL, 2222 NULL); 2223 2224 if (desktop_file_ids == NULL) 2225 continue; 2226 2227 unaliased_type = _g_unix_content_type_unalias (mime_types[i]); 2228 mime_info_cache_dir_add_desktop_entries (dir, 2229 unaliased_type, 2230 desktop_file_ids); 2231 g_free (unaliased_type); 2232 2233 g_strfreev (desktop_file_ids); 2234 } 2235 2236 g_strfreev (mime_types); 2237 g_key_file_free (key_file); 2238 2239 return; 2240 error: 2241 g_free (filename); 2242 g_key_file_free (key_file); 2243 2244 if (mime_types != NULL) 2245 g_strfreev (mime_types); 2246 2247 if (load_error) 2248 g_error_free (load_error); 2249 } 2250 2251 static void 2252 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir) 2253 { 2254 GKeyFile *key_file; 2255 GError *load_error; 2256 gchar *filename, **mime_types; 2257 char *unaliased_type; 2258 char **desktop_file_ids; 2259 int i; 2260 struct stat buf; 2261 2262 load_error = NULL; 2263 mime_types = NULL; 2264 2265 if (dir->defaults_list_map != NULL && 2266 !mime_info_cache_dir_out_of_date (dir, "defaults.list", 2267 &dir->defaults_list_timestamp)) 2268 return; 2269 2270 if (dir->defaults_list_map != NULL) 2271 g_hash_table_destroy (dir->defaults_list_map); 2272 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal, 2273 g_free, (GDestroyNotify)g_strfreev); 2274 2275 2276 key_file = g_key_file_new (); 2277 2278 filename = g_build_filename (dir->path, "defaults.list", NULL); 2279 if (g_stat (filename, &buf) < 0) 2280 goto error; 2281 2282 if (dir->defaults_list_timestamp > 0) 2283 mime_info_cache->should_ping_mime_monitor = TRUE; 2284 2285 dir->defaults_list_timestamp = buf.st_mtime; 2286 2287 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error); 2288 g_free (filename); 2289 filename = NULL; 2290 2291 if (load_error != NULL) 2292 goto error; 2293 2294 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP, 2295 NULL, NULL); 2296 if (mime_types != NULL) 2297 { 2298 for (i = 0; mime_types[i] != NULL; i++) 2299 { 2300 desktop_file_ids = g_key_file_get_string_list (key_file, 2301 DEFAULT_APPLICATIONS_GROUP, 2302 mime_types[i], 2303 NULL, 2304 NULL); 2305 if (desktop_file_ids == NULL) 2306 continue; 2307 2308 unaliased_type = _g_unix_content_type_unalias (mime_types[i]); 2309 g_hash_table_replace (dir->defaults_list_map, 2310 unaliased_type, 2311 desktop_file_ids); 2312 } 2313 2314 g_strfreev (mime_types); 2315 } 2316 2317 g_key_file_free (key_file); 2318 return; 2319 2320 error: 2321 g_free (filename); 2322 g_key_file_free (key_file); 2323 2324 if (mime_types != NULL) 2325 g_strfreev (mime_types); 2326 2327 if (load_error) 2328 g_error_free (load_error); 2329 } 2330 2331 static void 2332 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir) 2333 { 2334 GKeyFile *key_file; 2335 GError *load_error; 2336 gchar *filename, **mime_types; 2337 char *unaliased_type; 2338 char **desktop_file_ids; 2339 int i; 2340 struct stat buf; 2341 2342 load_error = NULL; 2343 mime_types = NULL; 2344 2345 if (dir->mimeapps_list_added_map != NULL && 2346 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list", 2347 &dir->mimeapps_list_timestamp)) 2348 return; 2349 2350 if (dir->mimeapps_list_added_map != NULL) 2351 g_hash_table_destroy (dir->mimeapps_list_added_map); 2352 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal, 2353 g_free, (GDestroyNotify)g_strfreev); 2354 2355 if (dir->mimeapps_list_removed_map != NULL) 2356 g_hash_table_destroy (dir->mimeapps_list_removed_map); 2357 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal, 2358 g_free, (GDestroyNotify)g_strfreev); 2359 2360 key_file = g_key_file_new (); 2361 2362 filename = g_build_filename (dir->path, "mimeapps.list", NULL); 2363 if (g_stat (filename, &buf) < 0) 2364 goto error; 2365 2366 if (dir->mimeapps_list_timestamp > 0) 2367 mime_info_cache->should_ping_mime_monitor = TRUE; 2368 2369 dir->mimeapps_list_timestamp = buf.st_mtime; 2370 2371 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error); 2372 g_free (filename); 2373 filename = NULL; 2374 2375 if (load_error != NULL) 2376 goto error; 2377 2378 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, 2379 NULL, NULL); 2380 if (mime_types != NULL) 2381 { 2382 for (i = 0; mime_types[i] != NULL; i++) 2383 { 2384 desktop_file_ids = g_key_file_get_string_list (key_file, 2385 ADDED_ASSOCIATIONS_GROUP, 2386 mime_types[i], 2387 NULL, 2388 NULL); 2389 if (desktop_file_ids == NULL) 2390 continue; 2391 2392 unaliased_type = _g_unix_content_type_unalias (mime_types[i]); 2393 g_hash_table_replace (dir->mimeapps_list_added_map, 2394 unaliased_type, 2395 desktop_file_ids); 2396 } 2397 2398 g_strfreev (mime_types); 2399 } 2400 2401 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, 2402 NULL, NULL); 2403 if (mime_types != NULL) 2404 { 2405 for (i = 0; mime_types[i] != NULL; i++) 2406 { 2407 desktop_file_ids = g_key_file_get_string_list (key_file, 2408 REMOVED_ASSOCIATIONS_GROUP, 2409 mime_types[i], 2410 NULL, 2411 NULL); 2412 if (desktop_file_ids == NULL) 2413 continue; 2414 2415 unaliased_type = _g_unix_content_type_unalias (mime_types[i]); 2416 g_hash_table_replace (dir->mimeapps_list_removed_map, 2417 unaliased_type, 2418 desktop_file_ids); 2419 } 2420 2421 g_strfreev (mime_types); 2422 } 2423 2424 g_key_file_free (key_file); 2425 return; 2426 2427 error: 2428 g_free (filename); 2429 g_key_file_free (key_file); 2430 2431 if (mime_types != NULL) 2432 g_strfreev (mime_types); 2433 2434 if (load_error) 2435 g_error_free (load_error); 2436 } 2437 2438 static MimeInfoCacheDir * 2439 mime_info_cache_dir_new (const char *path) 2440 { 2441 MimeInfoCacheDir *dir; 2442 2443 dir = g_new0 (MimeInfoCacheDir, 1); 2444 dir->path = g_strdup (path); 2445 2446 return dir; 2447 } 2448 2449 static void 2450 mime_info_cache_dir_free (MimeInfoCacheDir *dir) 2451 { 2452 if (dir == NULL) 2453 return; 2454 2455 if (dir->mime_info_cache_map != NULL) 2456 { 2457 destroy_info_cache_map (dir->mime_info_cache_map); 2458 dir->mime_info_cache_map = NULL; 2459 2460 } 2461 2462 if (dir->defaults_list_map != NULL) 2463 { 2464 g_hash_table_destroy (dir->defaults_list_map); 2465 dir->defaults_list_map = NULL; 2466 } 2467 2468 if (dir->mimeapps_list_added_map != NULL) 2469 { 2470 g_hash_table_destroy (dir->mimeapps_list_added_map); 2471 dir->mimeapps_list_added_map = NULL; 2472 } 2473 2474 if (dir->mimeapps_list_removed_map != NULL) 2475 { 2476 g_hash_table_destroy (dir->mimeapps_list_removed_map); 2477 dir->mimeapps_list_removed_map = NULL; 2478 } 2479 2480 g_free (dir); 2481 } 2482 2483 static void 2484 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir, 2485 const char *mime_type, 2486 char **new_desktop_file_ids) 2487 { 2488 GList *desktop_file_ids; 2489 int i; 2490 2491 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map, 2492 mime_type); 2493 2494 for (i = 0; new_desktop_file_ids[i] != NULL; i++) 2495 { 2496 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i])) 2497 desktop_file_ids = g_list_append (desktop_file_ids, 2498 g_strdup (new_desktop_file_ids[i])); 2499 } 2500 2501 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids); 2502 } 2503 2504 static void 2505 mime_info_cache_init_dir_lists (void) 2506 { 2507 const char * const *dirs; 2508 int i; 2509 2510 mime_info_cache = mime_info_cache_new (); 2511 2512 dirs = get_applications_search_path (); 2513 2514 for (i = 0; dirs[i] != NULL; i++) 2515 { 2516 MimeInfoCacheDir *dir; 2517 2518 dir = mime_info_cache_dir_new (dirs[i]); 2519 2520 if (dir != NULL) 2521 { 2522 mime_info_cache_dir_init (dir); 2523 mime_info_cache_dir_init_defaults_list (dir); 2524 mime_info_cache_dir_init_mimeapps_list (dir); 2525 2526 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir); 2527 } 2528 } 2529 } 2530 2531 static void 2532 mime_info_cache_update_dir_lists (void) 2533 { 2534 GList *tmp; 2535 2536 tmp = mime_info_cache->dirs; 2537 2538 while (tmp != NULL) 2539 { 2540 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data; 2541 2542 /* No need to do this if we had file monitors... */ 2543 mime_info_cache_blow_global_cache (); 2544 mime_info_cache_dir_init (dir); 2545 mime_info_cache_dir_init_defaults_list (dir); 2546 mime_info_cache_dir_init_mimeapps_list (dir); 2547 2548 tmp = tmp->next; 2549 } 2550 } 2551 2552 static void 2553 mime_info_cache_init (void) 2554 { 2555 G_LOCK (mime_info_cache); 2556 if (mime_info_cache == NULL) 2557 mime_info_cache_init_dir_lists (); 2558 else 2559 { 2560 time_t now; 2561 2562 time (&now); 2563 if (now >= mime_info_cache->last_stat_time + 10) 2564 { 2565 mime_info_cache_update_dir_lists (); 2566 mime_info_cache->last_stat_time = now; 2567 } 2568 } 2569 2570 if (mime_info_cache->should_ping_mime_monitor) 2571 { 2572 /* g_idle_add (emit_mime_changed, NULL); */ 2573 mime_info_cache->should_ping_mime_monitor = FALSE; 2574 } 2575 2576 G_UNLOCK (mime_info_cache); 2577 } 2578 2579 static MimeInfoCache * 2580 mime_info_cache_new (void) 2581 { 2582 MimeInfoCache *cache; 2583 2584 cache = g_new0 (MimeInfoCache, 1); 2585 2586 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal, 2587 (GDestroyNotify) g_free, 2588 (GDestroyNotify) g_free); 2589 return cache; 2590 } 2591 2592 static void 2593 mime_info_cache_free (MimeInfoCache *cache) 2594 { 2595 if (cache == NULL) 2596 return; 2597 2598 g_list_foreach (cache->dirs, 2599 (GFunc) mime_info_cache_dir_free, 2600 NULL); 2601 g_list_free (cache->dirs); 2602 g_hash_table_destroy (cache->global_defaults_cache); 2603 g_free (cache); 2604 } 2605 2606 /** 2607 * mime_info_cache_reload: 2608 * @dir: directory path which needs reloading. 2609 * 2610 * Reload the mime information for the @dir. 2611 */ 2612 static void 2613 mime_info_cache_reload (const char *dir) 2614 { 2615 /* FIXME: just reload the dir that needs reloading, 2616 * don't blow the whole cache 2617 */ 2618 if (mime_info_cache != NULL) 2619 { 2620 G_LOCK (mime_info_cache); 2621 mime_info_cache_free (mime_info_cache); 2622 mime_info_cache = NULL; 2623 G_UNLOCK (mime_info_cache); 2624 } 2625 } 2626 2627 static GList * 2628 append_desktop_entry (GList *list, 2629 const char *desktop_entry, 2630 GList *removed_entries) 2631 { 2632 /* Add if not already in list, and valid */ 2633 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) && 2634 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp)) 2635 list = g_list_prepend (list, g_strdup (desktop_entry)); 2636 2637 return list; 2638 } 2639 2640 /** 2641 * get_all_desktop_entries_for_mime_type: 2642 * @mime_type: a mime type. 2643 * @except: NULL or a strv list 2644 * 2645 * Returns all the desktop ids for @mime_type. The desktop files 2646 * are listed in an order so that default applications are listed before 2647 * non-default ones, and handlers for inherited mimetypes are listed 2648 * after the base ones. 2649 * 2650 * Optionally doesn't list the desktop ids given in the @except 2651 * 2652 * Return value: a #GList containing the desktop ids which claim 2653 * to handle @mime_type. 2654 */ 2655 static GList * 2656 get_all_desktop_entries_for_mime_type (const char *base_mime_type, 2657 const char **except) 2658 { 2659 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp; 2660 MimeInfoCacheDir *dir; 2661 char *mime_type; 2662 char **mime_types; 2663 char **default_entries; 2664 char **removed_associations; 2665 int i, j, k; 2666 GPtrArray *array; 2667 char **anc; 2668 2669 mime_info_cache_init (); 2670 2671 /* collect all ancestors */ 2672 mime_types = _g_unix_content_type_get_parents (base_mime_type); 2673 array = g_ptr_array_new (); 2674 for (i = 0; mime_types[i]; i++) 2675 g_ptr_array_add (array, mime_types[i]); 2676 g_free (mime_types); 2677 for (i = 0; i < array->len; i++) 2678 { 2679 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i)); 2680 for (j = 0; anc[j]; j++) 2681 { 2682 for (k = 0; k < array->len; k++) 2683 { 2684 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0) 2685 break; 2686 } 2687 if (k == array->len) /* not found */ 2688 g_ptr_array_add (array, g_strdup (anc[j])); 2689 } 2690 g_strfreev (anc); 2691 } 2692 g_ptr_array_add (array, NULL); 2693 mime_types = (char **)g_ptr_array_free (array, FALSE); 2694 2695 G_LOCK (mime_info_cache); 2696 2697 removed_entries = NULL; 2698 desktop_entries = NULL; 2699 2700 for (i = 0; except != NULL && except[i] != NULL; i++) 2701 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i])); 2702 2703 for (i = 0; mime_types[i] != NULL; i++) 2704 { 2705 mime_type = mime_types[i]; 2706 2707 /* Go through all apps listed as defaults */ 2708 for (dir_list = mime_info_cache->dirs; 2709 dir_list != NULL; 2710 dir_list = dir_list->next) 2711 { 2712 dir = dir_list->data; 2713 2714 /* First added associations from mimeapps.list */ 2715 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type); 2716 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++) 2717 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries); 2718 2719 /* Then removed associations from mimeapps.list */ 2720 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type); 2721 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++) 2722 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL); 2723 2724 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */ 2725 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type); 2726 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++) 2727 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries); 2728 } 2729 2730 /* Go through all entries that support the mimetype */ 2731 for (dir_list = mime_info_cache->dirs; 2732 dir_list != NULL; 2733 dir_list = dir_list->next) 2734 { 2735 dir = dir_list->data; 2736 2737 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type); 2738 for (tmp = list; tmp != NULL; tmp = tmp->next) 2739 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries); 2740 } 2741 } 2742 2743 G_UNLOCK (mime_info_cache); 2744 2745 g_strfreev (mime_types); 2746 2747 g_list_foreach (removed_entries, (GFunc)g_free, NULL); 2748 g_list_free (removed_entries); 2749 2750 desktop_entries = g_list_reverse (desktop_entries); 2751 2752 return desktop_entries; 2753 } 2754 2755 /* GDesktopAppInfoLookup interface: */ 2756 2757 static void g_desktop_app_info_lookup_base_init (gpointer g_class); 2758 static void g_desktop_app_info_lookup_class_init (gpointer g_class, 2759 gpointer class_data); 2760 2761 GType 2762 g_desktop_app_info_lookup_get_type (void) 2763 { 2764 static volatile gsize g_define_type_id__volatile = 0; 2765 2766 if (g_once_init_enter (&g_define_type_id__volatile)) 2767 { 2768 const GTypeInfo desktop_app_info_lookup_info = 2769 { 2770 sizeof (GDesktopAppInfoLookupIface), /* class_size */ 2771 g_desktop_app_info_lookup_base_init, /* base_init */ 2772 NULL, /* base_finalize */ 2773 g_desktop_app_info_lookup_class_init, 2774 NULL, /* class_finalize */ 2775 NULL, /* class_data */ 2776 0, 2777 0, /* n_preallocs */ 2778 NULL 2779 }; 2780 GType g_define_type_id = 2781 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"), 2782 &desktop_app_info_lookup_info, 0); 2783 2784 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT); 2785 2786 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); 2787 } 2788 2789 return g_define_type_id__volatile; 2790 } 2791 2792 static void 2793 g_desktop_app_info_lookup_class_init (gpointer g_class, 2794 gpointer class_data) 2795 { 2796 } 2797 2798 static void 2799 g_desktop_app_info_lookup_base_init (gpointer g_class) 2800 { 2801 } 2802 2803 /** 2804 * g_desktop_app_info_lookup_get_default_for_uri_scheme: 2805 * @lookup: a #GDesktopAppInfoLookup 2806 * @uri_scheme: a string containing a URI scheme. 2807 * 2808 * Gets the default application for launching applications 2809 * using this URI scheme for a particular GDesktopAppInfoLookup 2810 * implementation. 2811 * 2812 * The GDesktopAppInfoLookup interface and this function is used 2813 * to implement g_app_info_get_default_for_uri_scheme() backends 2814 * in a GIO module. There is no reason for applications to use it 2815 * directly. Applications should use g_app_info_get_default_for_uri_scheme(). 2816 * 2817 * Returns: #GAppInfo for given @uri_scheme or %NULL on error. 2818 */ 2819 GAppInfo * 2820 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup, 2821 const char *uri_scheme) 2822 { 2823 GDesktopAppInfoLookupIface *iface; 2824 2825 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL); 2826 2827 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup); 2828 2829 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme); 2830 } 2831 2832 #define __G_DESKTOP_APP_INFO_C__ 2833 #include "gioaliasdef.c" 2834