1#!/bin/sh
2#---------------------------------------------
3#   xdg-mime
4#
5#   Utility script to manipulate MIME related information
6#   on XDG compliant systems.
7#
8#   Refer to the usage() function below for usage.
9#
10#   Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org>
11#   Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org>
12#   Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
13#   Copyright 2006, Jeremy White <jwhite@codeweavers.com>
14#
15#   LICENSE:
16#
17#   Permission is hereby granted, free of charge, to any person obtaining a
18#   copy of this software and associated documentation files (the "Software"),
19#   to deal in the Software without restriction, including without limitation
20#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
21#   and/or sell copies of the Software, and to permit persons to whom the
22#   Software is furnished to do so, subject to the following conditions:
23#
24#   The above copyright notice and this permission notice shall be included
25#   in all copies or substantial portions of the Software.
26#
27#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33#   OTHER DEALINGS IN THE SOFTWARE.
34#
35#---------------------------------------------
36
37manualpage()
38{
39cat << _MANUALPAGE
40Name
41
42xdg-mime - command line tool for querying information about file type handling
43and adding descriptions for new file types
44
45Synopsis
46
47xdg-mime query { filetype | default } ...
48
49xdg-mime default application mimetype(s)
50
51xdg-mime install [--mode mode] [--novendor] mimetypes-file
52
53xdg-mime uninstall [--mode mode] mimetypes-file
54
55xdg-mime { --help | --manual | --version }
56
57Description
58
59The xdg-mime program can be used to query information about file types and to
60add descriptions for new file types.
61
62Commands
63
64query
65
66    Returns information related to file types.
67
68    The query option is for use inside a desktop session only. It is not
69    recommended to use xdg-mime query as root.
70
71    The following queries are supported:
72
73    query filetype FILE: Returns the file type of FILE in the form of a MIME
74    type.
75
76    query default mimetype: Returns the default application that the desktop
77    environment uses for opening files of type mimetype. The default
78    application is identified by its *.desktop file.
79
80default
81
82    Ask the desktop environment to make application the default application for
83    opening files of type mimetype. An application can be made the default for
84    several file types by specifying multiple mimetypes.
85
86    application is the desktop file id of the application and has the form
87    vendor-name.desktop application must already be installed in the desktop
88    menu before it can be made the default handler. The aplication's desktop
89    file must list support for all the MIME types that it wishes to be the
90    default handler for.
91
92    Requests to make an application a default handler may be subject to system
93    policy or approval by the end-user. xdg-mime query can be used to verify
94    whether an application is the actual default handler for a specific file
95    type.
96
97    The default option is for use inside a desktop session only. It is not
98    recommended to use xdg-mime default as root.
99
100install
101    Adds the file type descriptions provided in mimetypes-file to the desktop
102    environment. mimetypes-file must be a XML file that follows the
103    freedesktop.org Shared MIME-info Database specification and that has a
104    mime-info element as its document root. For each new file type one or more
105    icons with name type-subtype must be installed with the xdg-icon-resource
106    command in the mimetypes context. For example the filetype application/
107    vnd.oasis.opendocument.text requires an icon named
108    application-vnd.oasis.opendocument.text to be installed (unless the file
109    type recommends another icon name).
110uninstall
111    Removes the file type descriptions provided in mimetypes-file and
112    previously added with xdg-mime install from the desktop environment.
113    mimetypes-file must be a XML file that follows the freedesktop.org Shared
114    MIME-info Database specification and that has a mime-info element as its
115    document root.
116
117Options
118
119--mode mode
120
121    mode can be user or system. In user mode the file is (un)installed for the
122    current user only. In system mode the file is (un)installed for all users
123    on the system. Usually only root is allowed to install in system mode.
124
125    The default is to use system mode when called by root and to use user mode
126    when called by a non-root user.
127
128--novendor
129
130    Normally, xdg-mime checks to ensure that the mimetypes-file to be installed
131    has a proper vendor prefix. This option can be used to disable that check.
132
133    A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated
134    with a dash ("-"). Companies and organizations are encouraged to use a word
135    or phrase, preferably the organizations name, for which they hold a
136    trademark as their vendor prefix. The purpose of the vendor prefix is to
137    prevent name conflicts.
138
139--help
140    Show command synopsis.
141--manual
142    Show this manualpage.
143--version
144    Show the xdg-utils version information.
145
146Environment Variables
147
148xdg-mime honours the following environment variables:
149
150XDG_UTILS_DEBUG_LEVEL
151    Setting this environment variable to a non-zero numerical value makes
152    xdg-mime do more verbose reporting on stderr. Setting a higher value
153    increases the verbosity.
154XDG_UTILS_INSTALL_MODE
155    This environment variable can be used by the user or administrator to
156    override the installation mode. Valid values are user and system.
157
158Exit Codes
159
160An exit code of 0 indicates success while a non-zero exit code indicates
161failure. The following failure codes can be returned:
162
1631
164    Error in command line syntax.
1652
166    One of the files passed on the command line did not exist.
1673
168    A required tool could not be found.
1694
170    The action failed.
1715
172    No permission to read one of the files passed on the command line.
173
174See Also
175
176xdg-icon-resource(1), xdg-desktop-menu(1)
177
178Examples
179
180xdg-mime query filetype /tmp/foobar.png
181
182Prints the MIME type of the file /tmp/foobar.png, in this case image/png
183
184xdg-mime query default image/png
185
186Prints the .desktop filename of the application which is registered to open PNG
187files.
188
189xdg-mime install shinythings-shiny.xml
190
191Adds a file type description for "shiny"-files. "shinythings-" is used as the
192vendor prefix. The file type description could look as folows.
193
194shinythings-shiny.xml:
195
196<?xml version="1.0"?>
197<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
198  <mime-type type="text/x-shiny">
199    <comment>Shiny new file type</comment>
200    <glob pattern="*.shiny"/>
201    <glob pattern="*.shi"/>
202  </mime-type>
203</mime-info>
204
205An icon for this new file type must also be installed, for example with:
206
207xdg-icon-resource install --context mimetypes --size 64 shiny-file-icon.png text-x-shiny
208
209_MANUALPAGE
210}
211
212usage()
213{
214cat << _USAGE
215xdg-mime - command line tool for querying information about file type handling
216and adding descriptions for new file types
217
218Synopsis
219
220xdg-mime query { filetype | default } ...
221
222xdg-mime default application mimetype(s)
223
224xdg-mime install [--mode mode] [--novendor] mimetypes-file
225
226xdg-mime uninstall [--mode mode] mimetypes-file
227
228xdg-mime { --help | --manual | --version }
229
230_USAGE
231}
232
233#@xdg-utils-common@
234
235#----------------------------------------------------------------------------
236#   Common utility functions included in all XDG wrapper scripts
237#----------------------------------------------------------------------------
238
239DEBUG()
240{
241  [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
242  [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
243  shift
244  echo "$@" >&2
245}
246
247# This handles backslashes but not quote marks.
248first_word()
249{
250    read first rest
251    echo "$first"
252}
253
254#-------------------------------------------------------------
255# map a binary to a .desktop file
256binary_to_desktop_file()
257{
258    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
259    binary="`which "$1"`"
260    binary="`readlink -f "$binary"`"
261    base="`basename "$binary"`"
262    IFS=:
263    for dir in $search; do
264        unset IFS
265        [ "$dir" ] || continue
266        [ -d "$dir/applications" -o -d "$dir/applnk" ] || continue
267        for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do
268            [ -r "$file" ] || continue
269            # Check to make sure it's worth the processing.
270            grep -q "^Exec.*$base" "$file" || continue
271            # Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop").
272            grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
273            command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
274            command="`which "$command"`"
275            if [ x"`readlink -f "$command"`" = x"$binary" ]; then
276                # Fix any double slashes that got added path composition
277                echo "$file" | sed -e 's,//*,/,g'
278                return
279            fi
280        done
281    done
282}
283
284#-------------------------------------------------------------
285# map a .desktop file to a binary
286## FIXME: handle vendor dir case
287desktop_file_to_binary()
288{
289    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
290    desktop="`basename "$1"`"
291    IFS=:
292    for dir in $search; do
293        unset IFS
294        [ "$dir" -a -d "$dir/applications" ] || continue
295        file="$dir/applications/$desktop"
296        [ -r "$file" ] || continue
297        # Remove any arguments (%F, %f, %U, %u, etc.).
298        command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
299        command="`which "$command"`"
300        readlink -f "$command"
301        return
302    done
303}
304
305#-------------------------------------------------------------
306# Exit script on successfully completing the desired operation
307
308exit_success()
309{
310    if [ $# -gt 0 ]; then
311        echo "$@"
312        echo
313    fi
314
315    exit 0
316}
317
318
319#-----------------------------------------
320# Exit script on malformed arguments, not enough arguments
321# or missing required option.
322# prints usage information
323
324exit_failure_syntax()
325{
326    if [ $# -gt 0 ]; then
327        echo "xdg-mime: $@" >&2
328        echo "Try 'xdg-mime --help' for more information." >&2
329    else
330        usage
331        echo "Use 'man xdg-mime' or 'xdg-mime --manual' for additional info."
332    fi
333
334    exit 1
335}
336
337#-------------------------------------------------------------
338# Exit script on missing file specified on command line
339
340exit_failure_file_missing()
341{
342    if [ $# -gt 0 ]; then
343        echo "xdg-mime: $@" >&2
344    fi
345
346    exit 2
347}
348
349#-------------------------------------------------------------
350# Exit script on failure to locate necessary tool applications
351
352exit_failure_operation_impossible()
353{
354    if [ $# -gt 0 ]; then
355        echo "xdg-mime: $@" >&2
356    fi
357
358    exit 3
359}
360
361#-------------------------------------------------------------
362# Exit script on failure returned by a tool application
363
364exit_failure_operation_failed()
365{
366    if [ $# -gt 0 ]; then
367        echo "xdg-mime: $@" >&2
368    fi
369
370    exit 4
371}
372
373#------------------------------------------------------------
374# Exit script on insufficient permission to read a specified file
375
376exit_failure_file_permission_read()
377{
378    if [ $# -gt 0 ]; then
379        echo "xdg-mime: $@" >&2
380    fi
381
382    exit 5
383}
384
385#------------------------------------------------------------
386# Exit script on insufficient permission to write a specified file
387
388exit_failure_file_permission_write()
389{
390    if [ $# -gt 0 ]; then
391        echo "xdg-mime: $@" >&2
392    fi
393
394    exit 6
395}
396
397check_input_file()
398{
399    if [ ! -e "$1" ]; then
400        exit_failure_file_missing "file '$1' does not exist"
401    fi
402    if [ ! -r "$1" ]; then
403        exit_failure_file_permission_read "no permission to read file '$1'"
404    fi
405}
406
407check_vendor_prefix()
408{
409    file_label="$2"
410    [ -n "$file_label" ] || file_label="filename"
411    file=`basename "$1"`
412    case "$file" in
413       [a-zA-Z]*-*)
414         return
415         ;;
416    esac
417
418    echo "xdg-mime: $file_label '$file' does not have a proper vendor prefix" >&2
419    echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
420    echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
421    echo "Use --novendor to override or 'xdg-mime --manual' for additional info." >&2
422    exit 1
423}
424
425check_output_file()
426{
427    # if the file exists, check if it is writeable
428    # if it does not exists, check if we are allowed to write on the directory
429    if [ -e "$1" ]; then
430        if [ ! -w "$1" ]; then
431            exit_failure_file_permission_write "no permission to write to file '$1'"
432        fi
433    else
434        DIR=`dirname "$1"`
435        if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
436            exit_failure_file_permission_write "no permission to create file '$1'"
437        fi
438    fi
439}
440
441#----------------------------------------
442# Checks for shared commands, e.g. --help
443
444check_common_commands()
445{
446    while [ $# -gt 0 ] ; do
447        parm="$1"
448        shift
449
450        case "$parm" in
451            --help)
452            usage
453            echo "Use 'man xdg-mime' or 'xdg-mime --manual' for additional info."
454            exit_success
455            ;;
456
457            --manual)
458            manualpage
459            exit_success
460            ;;
461
462            --version)
463            echo "xdg-mime 1.1.0 rc1"
464            exit_success
465            ;;
466        esac
467    done
468}
469
470check_common_commands "$@"
471
472[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
473if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
474    # Be silent
475    xdg_redirect_output=" > /dev/null 2> /dev/null"
476else
477    # All output to stderr
478    xdg_redirect_output=" >&2"
479fi
480
481#--------------------------------------
482# Checks for known desktop environments
483# set variable DE to the desktop environments name, lowercase
484
485detectDE()
486{
487    # see https://bugs.freedesktop.org/show_bug.cgi?id=34164
488    unset GREP_OPTIONS
489
490    if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
491      case "${XDG_CURRENT_DESKTOP}" in
492         GNOME)
493           DE=gnome;
494           ;;
495         KDE)
496           DE=kde;
497           ;;
498         LXDE)
499           DE=lxde;
500           ;;
501         XFCE)
502           DE=xfce
503      esac
504    fi
505
506    if [ x"$DE" = x"" ]; then
507      # classic fallbacks
508      if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
509      elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
510      elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
511      elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
512      elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
513      fi
514    fi
515
516    if [ x"$DE" = x"" ]; then
517      # fallback to checking $DESKTOP_SESSION
518      case "$DESKTOP_SESSION" in
519         gnome)
520           DE=gnome;
521           ;;
522         LXDE)
523           DE=lxde;
524           ;;
525         xfce|xfce4)
526           DE=xfce;
527           ;;
528      esac
529    fi
530
531    if [ x"$DE" = x"" ]; then
532      # fallback to uname output for other platforms
533      case "$(uname 2>/dev/null)" in
534        Darwin)
535          DE=darwin;
536          ;;
537      esac
538    fi
539
540    if [ x"$DE" = x"gnome" ]; then
541      # gnome-default-applications-properties is only available in GNOME 2.x
542      # but not in GNOME 3.x
543      which gnome-default-applications-properties > /dev/null 2>&1  || DE="gnome3"
544    fi
545}
546
547#----------------------------------------------------------------------------
548# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
549# It also always returns 1 in KDE 3.4 and earlier
550# Simply return 0 in such case
551
552kfmclient_fix_exit_code()
553{
554    version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`
555    major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
556    minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
557    release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
558    test "$major" -gt 3 && return $1
559    test "$minor" -gt 5 && return $1
560    test "$release" -gt 4 && return $1
561    return 0
562}
563
564update_mime_database()
565{
566   if [ x"$mode" = x"user" -a -n "$DISPLAY" ] ; then
567      detectDE
568      if [ x"$DE" = x"kde" ] ; then
569         DEBUG 1 "Running kbuildsycoca"
570         if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
571             eval 'kbuildsycoca4'$xdg_redirect_output
572         else
573             eval 'kbuildsycoca'$xdg_redirect_output
574         fi
575      fi
576   fi
577   for x in `echo "$PATH:/opt/gnome/bin" | sed 's/:/ /g'`; do
578      if [ -x $x/update-mime-database ] ; then
579         DEBUG 1 "Running $x/update-mime-database $1"
580         eval '$x/update-mime-database $1'$xdg_redirect_output
581         return
582      fi
583   done
584}
585
586info_kde()
587{
588    if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
589        DEBUG 1 "Running kmimetypefinder \"$1\""
590        kmimetypefinder "$1" 2>/dev/null | head -n 1
591    else
592        DEBUG 1 "Running kfile \"$1\""
593        kfile "$1" 2> /dev/null | head -n 1 | cut -d "(" -f 2 | cut -d ")" -f 1
594    fi
595
596    if [ $? -eq 0 ]; then
597        exit_success
598    else
599        exit_failure_operation_failed
600    fi
601}
602
603info_gnome()
604{
605    if gvfs-info --help 2>/dev/null 1>&2; then
606        DEBUG 1 "Running gvfs-info \"$1\""
607        gvfs-info "$1" 2> /dev/null | grep standard::content-type | cut -d' ' -f4
608    elif gnomevfs-info --help 2>/dev/null 1>&2; then
609       DEBUG 1 "Running gnomevfs-info \"$1\""
610       gnomevfs-info --slow-mime "$1" 2> /dev/null | grep "^MIME" | cut -d ":" -f 2 | sed s/"^ "//
611    else
612       # according to https://bugs.freedesktop.org/show_bug.cgi?id=33094#c5
613       # neither gvfs-info or gnomevfs-info are present in a default Ubuntu Natty
614       # install, so fallback to info_generic
615       info_generic "$1"
616    fi
617
618    if [ $? -eq 0 ]; then
619        exit_success
620    else
621        exit_failure_operation_failed
622    fi
623}
624
625info_generic()
626{
627    if mimetype --version >/dev/null 2>&1; then
628        DEBUG 1 "Running mimetype -b \"$1\""
629        mimetype -b "$1"
630    else
631        DEBUG 1 "Running file --mime-type \"$1\""
632        /usr/bin/file --mime-type "$1" 2> /dev/null | cut -d ":" -f 2 | sed s/"^ "//
633    fi
634
635    if [ $? -eq 0 ]; then
636        exit_success
637    else
638        exit_failure_operation_failed
639    fi
640}
641
642make_default_kde()
643{
644    # $1 is vendor-name.desktop
645    # $2 is mime/type
646    #
647    # On KDE 3, add to $KDE_CONFIG_PATH/profilerc:
648    # [$2 - 1]
649    # Application=$1
650    #
651    # Remove all [$2 - *] sections, or even better,
652    # renumber [$2 - *] sections and remove duplicate
653    #
654    # On KDE 4, add $2=$1 to $XDG_DATA_APPS/mimeapps.list
655    #
656    # Example file:
657    #
658    # [Added Associations]
659    # text/plain=kde4-kate.desktop;kde4-kwrite.desktop;
660    #
661    # [Removed Associations]
662    # text/plain=gnome-gedit.desktop;gnu-emacs.desktop;
663    vendor="$1"
664    mimetype="$2"
665    if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
666        default_dir=`kde4-config --path xdgdata-apps 2> /dev/null | cut -d ':' -f 1`
667        default_file="$default_dir/mimeapps.list"
668    else
669        default_dir=`kde-config --path config 2> /dev/null | cut -d ':' -f 1`
670        default_file="$default_dir/profilerc"
671    fi
672    if [ -z "$default_dir" ]; then
673        DEBUG 2 "make_default_kde: No kde runtime detected"
674        return
675    fi
676    DEBUG 2 "make_default_kde $vendor $mimetype"
677    DEBUG 1 "Updating $default_file"
678    mkdir -p "$default_dir"
679    [ -f "$default_file" ] || touch "$default_file"
680    if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
681        [ -f "$default_file" ] || touch "$default_file"
682        awk -v application="$vendor" -v mimetype="$mimetype" '
683    BEGIN {
684        prefix=mimetype "="
685        associations=0
686        found=0
687        blanks=0
688    }
689    {
690        suppress=0
691        if (index($0, "[Added Associations]") == 1) {
692            associations=1
693        } else if (index($0, "[") == 1) {
694            if (associations && !found) {
695                print prefix application
696                found=1
697            }
698            associations=0
699        } else if ($0 == "") {
700            blanks++
701            suppress=1
702        } else if (associations && index($0, prefix) == 1) {
703            value=substr($0, length(prefix) + 1, length)
704            split(value, apps, ";")
705            value=application ";"
706            count=0
707            for (i in apps) {
708              count++
709            }
710            for (i=0; i < count; i++) {
711                if (apps[i] != application && apps[i] != "") {
712                    value=value apps[i] ";"
713                }
714            }
715            $0=prefix value
716            found=1
717        }
718        if (!suppress) {
719            while (blanks > 0) {
720                print ""
721                blanks--
722            }
723            print $0
724        }
725    }
726    END {
727        if (!found) {
728            if (!associations) {
729                print "[Added Associations]"
730            }
731            print prefix application
732        }
733        while (blanks > 0) {
734            print ""
735            blanks--
736        }
737    }
738' "$default_file" > "${default_file}.new" && mv "${default_file}.new" "$default_file"
739        eval 'kbuildsycoca4'$xdg_redirect_output
740    else
741        awk -v application="$vendor" -v mimetype="$mimetype" '
742    BEGIN {
743        header_start="[" mimetype " - "
744        suppress=0
745    }
746    {
747        if (index($0, header_start) == 1 )
748            suppress=1
749        else
750            if (/^\[/) { suppress=0 }
751
752        if (!suppress) {
753            print $0
754        }
755    }
756    END {
757        print ""
758        print "[" mimetype " - 1]"
759        print "Application=" application
760        print "AllowAsDefault=true"
761        print "GenericServiceType=Application"
762        print "Preference=1"
763        print "ServiceType=" mimetype
764    }
765' "$default_file" > "${default_file}.new" && mv "${default_file}.new" "$default_file"
766    fi
767}
768
769make_default_generic()
770{
771    # $1 is vendor-name.desktop
772    # $2 is mime/type
773    # Add $2=$1 to XDG_DATA_HOME/applications/mimeapps.list
774    xdg_user_dir="$XDG_DATA_HOME"
775    [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
776    default_file="$xdg_user_dir/applications/mimeapps.list"
777    DEBUG 2 "make_default_generic $1 $2"
778    DEBUG 1 "Updating $default_file"
779    [ -f "$default_file" ] || touch "$default_file"
780    awk -v mimetype="$2" -v application="$1" '
781    BEGIN {
782        prefix=mimetype "="
783        indefault=0
784        added=0
785        blanks=0
786        found=0
787    }
788    {
789        suppress=0
790        if (index($0, "[Default Applications]") == 1) {
791            indefault=1
792            found=1
793        } else if (index($0, "[") == 1) {
794            if (!added && indefault) {
795                print prefix application
796                added=1
797            }
798            indefault=0
799        } else if ($0 == "") {
800            suppress=1
801            blanks++
802        } else if (indefault && !added && index($0, prefix) == 1) {
803                $0=prefix application
804                added=1
805        }
806        if (!suppress) {
807            while (blanks > 0) {
808                print ""
809                blanks--
810            }
811            print $0
812        }
813    }
814    END {
815        if (!added) {
816            if (!found) {
817                print ""
818                print "[Default Applications]"
819            }
820            print prefix application
821        }
822        while (blanks > 0) {
823            print ""
824            blanks--
825        }
826    }
827' "$default_file" > "${default_file}.new" && mv "${default_file}.new" "$default_file"
828}
829
830defapp_generic()
831{
832    MIME="$1"
833    xdg_user_dir="$XDG_DATA_HOME"
834    [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
835    xdg_user_dir="$xdg_user_dir/$xdg_dir_name"
836    xdg_system_dirs="$XDG_DATA_DIRS"
837    [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
838
839    for x in `echo "$xdg_user_dir" | sed 's/:/ /g'`; do
840        mimeapps_list="$x/applications/mimeapps.list"
841        if [ -f "$mimeapps_list" ] ; then
842            DEBUG 2 "Checking $mimeapps_list"
843            trader_result=`awk -v mimetype="$MIME" '
844    BEGIN {
845        prefix=mimetype "="
846        indefault=0
847        found=0
848    }
849    {
850        if (index($0, "[Default Applications]") == 1) {
851            indefault=1
852        } else if (index($0, "[") == 1) {
853            indefault=0
854        } else if (!found && indefault && index($0, prefix) == 1) {
855            print substr($0, length(prefix) +1, length)
856            found=1
857        }
858    }
859' $mimeapps_list`
860            if [ -n "$trader_result" ] ; then
861                echo $trader_result
862                exit_success
863            fi
864        fi
865    done
866
867    for x in `echo "$xdg_system_dirs" | sed 's/:/ /g'`; do
868       DEBUG 2 "Checking $x/applications/defaults.list"
869       trader_result=`grep "$MIME=" $x/applications/defaults.list 2> /dev/null | cut -d '=' -f 2 | cut -d ';' -f 1`
870       if [ -n "$trader_result" ] ; then
871          echo $trader_result
872          exit_success
873       fi
874    done
875    exit_success
876}
877
878defapp_kde()
879{
880    MIME="$1"
881    if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
882        KTRADER=`which ktraderclient 2> /dev/null`
883        MIMETYPE="--mimetype"
884        SERVICETYPE="--servicetype"
885    else
886        KTRADER=`which ktradertest 2> /dev/null`
887    fi
888    if [ -n "$KTRADER" ] ; then
889        DEBUG 1 "Running KDE trader query \"$MIME\" mimetype and \"Application\" servicetype"
890        trader_result=`$KTRADER $MIMETYPE "$MIME" $SERVICETYPE Application 2>/dev/null \
891            | grep DesktopEntryPath | head -n 1 | cut -d ':' -f 2 | cut -d \' -f 2`
892        if [ -n "$trader_result" ] ; then
893            basename "$trader_result"
894            exit_success
895        else
896            exit_failure_operation_failed
897        fi
898    else
899        defapp_generic "$1"
900    fi
901}
902
903[ x"$1" != x"" ] || exit_failure_syntax
904
905mode=
906action=
907filename=
908mimetype=
909
910case $1 in
911  install)
912    action=install
913    ;;
914
915  uninstall)
916    action=uninstall
917    ;;
918
919  query)
920    shift
921
922    if [ -z "$1" ] ; then
923        exit_failure_syntax "query type argument missing"
924    fi
925
926    case $1 in
927      filetype)
928        action=info
929
930        filename="$2"
931        if [ -z "$filename" ] ; then
932            exit_failure_syntax "FILE argument missing"
933        fi
934        case $filename in
935          -*)
936            exit_failure_syntax "unexpected option '$filename'"
937            ;;
938        esac
939        check_input_file "$filename"
940        filename=`readlink -f -- "$filename"`
941        ;;
942
943      default)
944        action=defapp
945        mimetype="$2"
946        if [ -z "$mimetype" ] ; then
947            exit_failure_syntax "mimetype argument missing"
948        fi
949        case $mimetype in
950          -*)
951            exit_failure_syntax "unexpected option '$mimetype'"
952            ;;
953
954          */*)
955            # Ok
956            ;;
957
958          *)
959            exit_failure_syntax "mimetype '$mimetype' is not in the form 'minor/major'"
960            ;;
961        esac
962        ;;
963
964      *)
965      exit_failure_syntax "unknown query type '$1'"
966      ;;
967    esac
968    ;;
969
970  default)
971    action=makedefault
972    shift
973
974    if [ -z "$1" ] ; then
975        exit_failure_syntax "application argument missing"
976    fi
977    case $1 in
978      -*)
979        exit_failure_syntax "unexpected option '$1'"
980        ;;
981
982      *.desktop)
983        filename="$1"
984        ;;
985
986      *)
987        exit_failure_syntax "malformed argument '$1', expected *.desktop"
988        ;;
989    esac
990    ;;
991
992  *)
993  exit_failure_syntax "unknown command '$1'"
994  ;;
995esac
996
997shift
998
999
1000if [ "$action" = "makedefault" ]; then
1001    if [ -z "$1" ] ; then
1002        exit_failure_syntax "mimetype argument missing"
1003    fi
1004
1005    while [ $# -gt 0 ] ; do
1006        case $1 in
1007          -*)
1008            exit_failure_syntax "unexpected option '$1'"
1009            ;;
1010        esac
1011        mimetype="$1"
1012        shift
1013
1014        make_default_kde "$filename" "$mimetype"
1015        make_default_generic "$filename" "$mimetype"
1016    done
1017    exit_success
1018fi
1019
1020if [ "$action" = "info" ]; then
1021    detectDE
1022
1023    if [ x"$DE" = x"" ]; then
1024        if [ -x /usr/bin/file ]; then
1025            DE=generic
1026        fi
1027    fi
1028
1029    case "$DE" in
1030        kde)
1031        info_kde "$filename"
1032        ;;
1033
1034        gnome*)
1035        info_gnome "$filename"
1036        ;;
1037
1038        *)
1039        info_generic "$filename"
1040        ;;
1041    esac
1042    exit_failure_operation_impossible "no method available for quering MIME type of '$filename'"
1043fi
1044
1045if [ "$action" = "defapp" ]; then
1046    detectDE
1047
1048    case "$DE" in
1049        kde)
1050        defapp_kde "$mimetype"
1051        ;;
1052
1053        *)
1054        defapp_generic "$mimetype"
1055        ;;
1056    esac
1057    exit_failure_operation_impossible "no method available for quering default application for '$mimetype'"
1058fi
1059
1060vendor=true
1061while [ $# -gt 0 ] ; do
1062    parm="$1"
1063    shift
1064
1065    case $parm in
1066      --mode)
1067        if [ -z "$1" ] ; then
1068            exit_failure_syntax "mode argument missing for --mode"
1069        fi
1070        case "$1" in
1071          user)
1072            mode="user"
1073            ;;
1074
1075          system)
1076            mode="system"
1077            ;;
1078
1079          *)
1080            exit_failure_syntax "unknown mode '$1'"
1081            ;;
1082        esac
1083        shift
1084        ;;
1085
1086      --novendor)
1087        vendor=false
1088        ;;
1089
1090      -*)
1091        exit_failure_syntax "unexpected option '$parm'"
1092        ;;
1093
1094      *)
1095        if [ -n "$filename" ] ; then
1096            exit_failure_syntax "unexpected argument '$parm'"
1097        fi
1098
1099        filename="$parm"
1100        check_input_file "$filename"
1101        ;;
1102    esac
1103done
1104
1105if [ -z "$action" ] ; then
1106    exit_failure_syntax "command argument missing"
1107fi
1108
1109if [ -n "$XDG_UTILS_INSTALL_MODE" ] ; then
1110    if [ "$XDG_UTILS_INSTALL_MODE" = "system" ] ; then
1111        mode="system"
1112    elif [ "$XDG_UTILS_INSTALL_MODE" = "user" ] ; then
1113        mode="user"
1114    fi
1115fi
1116
1117if [ -z "$mode" ] ; then
1118    if [ `whoami` = "root" ] ; then
1119        mode="system"
1120    else
1121        mode="user"
1122    fi
1123fi
1124
1125if [ -z "$filename" ] ; then
1126    exit_failure_syntax "mimetypes-file argument missing"
1127fi
1128
1129if [ "$vendor" =  "true" -a "$action" = "install" ] ; then
1130    check_vendor_prefix "$filename"
1131fi
1132
1133xdg_base_dir=
1134xdg_dir_name=mime/packages/
1135
1136xdg_user_dir="$XDG_DATA_HOME"
1137[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
1138[ x"$mode" = x"user" ] && xdg_base_dir="$xdg_user_dir/mime"
1139xdg_user_dir="$xdg_user_dir/$xdg_dir_name"
1140
1141xdg_system_dirs="$XDG_DATA_DIRS"
1142[ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
1143for x in `echo $xdg_system_dirs | sed 's/:/ /g'`; do
1144    if [ -w $x/$xdg_dir_name ] ; then
1145        [ x"$mode" = x"system" ] && xdg_base_dir="$x/mime"
1146        xdg_global_dir="$x/$xdg_dir_name"
1147        break
1148    fi
1149done
1150[ -w $xdg_global_dir ] || xdg_global_dir=
1151DEBUG 3 "xdg_user_dir: $xdg_user_dir"
1152DEBUG 3 "xdg_global_dir: $xdg_global_dir"
1153
1154# Find KDE3 mimelnk directory
1155kde_user_dir=
1156kde_global_dir=
1157kde_global_dirs=`kde${KDE_SESSION_VERSION}-config --path mime 2> /dev/null`
1158DEBUG 3 "kde_global_dirs: $kde_global_dirs"
1159first=
1160for x in `echo $kde_global_dirs | sed 's/:/ /g'` ; do
1161    if [ -z "$first" ] ; then
1162        first=false
1163        kde_user_dir="$x"
1164    elif [ -w $x ] ; then
1165        kde_global_dir="$x"
1166    fi
1167done
1168DEBUG 3 "kde_user_dir: $kde_user_dir"
1169DEBUG 3 "kde_global_dir: $kde_global_dir"
1170
1171# TODO: Gnome legacy support
1172# See http://forums.fedoraforum.org/showthread.php?t=26875
1173gnome_user_dir="$HOME/.gnome/apps"
1174gnome_global_dir=/usr/share/gnome/apps
1175[ -w $gnome_global_dir ] || gnome_global_dir=
1176DEBUG 3 "gnome_user_dir: $gnome_user_dir"
1177DEBUG 3 "gnome_global_dir: $gnome_global_dir"
1178
1179if [ x"$mode" = x"user" ] ; then
1180    xdg_dir="$xdg_user_dir"
1181    kde_dir="$kde_user_dir"
1182    gnome_dir="$gnome_user_dir"
1183    my_umask=077
1184else
1185    xdg_dir="$xdg_global_dir"
1186    kde_dir="$kde_global_dir"
1187    gnome_dir="$gnome_global_dir"
1188    my_umask=022
1189    if [ -z "${xdg_dir}${kde_dir}${gnome_dir}" ] ; then
1190        exit_failure_operation_impossible "No writable system mimetype directory found."
1191    fi
1192fi
1193
1194# echo "[xdg|$xdg_user_dir|$xdg_global_dir]"
1195# echo "[kde|$kde_user_dir|$kde_global_dir]"
1196# echo "[gnome|$gnome_user_dir|$gnome_global_dir]"
1197# echo "[using|$xdg_dir|$kde_dir|$gnome_dir]"
1198
1199basefile=`basename "$filename"`
1200#[ -z $vendor ] || basefile="$vendor-$basefile"
1201
1202mimetypes=
1203if [ -n "$kde_dir" ] ; then
1204    DEBUG 2 "KDE3 mimelnk directory found, extracting mimetypes from XML file"
1205
1206    mimetypes=`awk < "$filename" '
1207# Strip XML comments
1208BEGIN {
1209 suppress=0
1210}
1211{
1212 do
1213    if (suppress) {
1214       if (match($0,/-->/)) {
1215           $0=substr($0,RSTART+RLENGTH)
1216           suppress=0
1217       }
1218       else {
1219           break
1220       }
1221    }
1222    else {
1223       if (match($0,/<!--/)) {
1224           if (RSTART>1) print substr($0,0,RSTART)
1225           $0=substr($0,RSTART+RLENGTH)
1226           suppress=1
1227       }
1228       else {
1229           if ($0) print $0
1230           break
1231       }
1232    }
1233 while(1)
1234}
1235' | awk '
1236# List MIME types listed in <mime-type> tags
1237BEGIN {
1238  RS="<"
1239}
1240/^mime-info/, /^\/mime-info/ {
1241  if (match($0,/^mime-type/)) {
1242    if (match($0,/type="[^"]*/) || match($0,/type='"'"'[^'"'"']*/)) {
1243      print substr($0,RSTART+6,RLENGTH-6)
1244    }
1245  }
1246}'`
1247fi
1248
1249DEBUG 1 "$action mimetype in $xdg_dir"
1250
1251case $action in
1252    install)
1253        save_umask=`umask`
1254        umask $my_umask
1255
1256        for x in $xdg_dir ; do
1257            mkdir -p $x
1258            eval 'cp $filename $x/$basefile'$xdg_redirect_output
1259        done
1260
1261        if [ -n "$mimetypes" ] ; then
1262            # No quotes around $mimetypes
1263            for x in $mimetypes ; do
1264                DEBUG 1 "Installing $kde_dir/$x.desktop (KDE 3.x support)"
1265                mkdir -p `dirname $kde_dir/$x.desktop`
1266                awk < "$filename" '
1267# Strip XML comments
1268BEGIN {
1269 suppress=0
1270}
1271{
1272 do
1273    if (suppress) {
1274       if (match($0,/-->/)) {
1275           $0=substr($0,RSTART+RLENGTH)
1276           suppress=0
1277       }
1278       else {
1279           break
1280       }
1281    }
1282    else {
1283       if (match($0,/<!--/)) {
1284           if (RSTART>1) print substr($0,0,RSTART)
1285           $0=substr($0,RSTART+RLENGTH)
1286           suppress=1
1287       }
1288       else {
1289           if ($0) print $0
1290           break
1291       }
1292    }
1293 while(1)
1294}
1295' | awk > $kde_dir/$x.desktop '
1296# Extract mimetype $x from the XML file $filename
1297# Note that bash requires us to escape a single quote as '"'"'
1298BEGIN {
1299  the_type=ARGV[1]
1300  the_source=ARGV[2]
1301  ARGC=1
1302  RS="<"
1303  found=0
1304  glob_patterns=""
1305}
1306/^mime-info/, /^\/mime-info/ {
1307  if (match($0,/^mime-type/)) {
1308    if (match($0,/type="[^"]*/) || match($0,/type='"'"'[^'"'"']*/)) {
1309      if (substr($0,RSTART+6,RLENGTH-6) == the_type) {
1310        found=1
1311        print "[Desktop Entry]"
1312        print "# Installed by xdg-mime from " the_source
1313        print "Type=MimeType"
1314        print "MimeType=" the_type
1315        the_icon=the_type
1316        sub("/", "-", the_icon)
1317        print "Icon=" the_icon
1318      }
1319    }
1320  }
1321  else if (found) {
1322    if (match($0,/^\/mime-type/)) {
1323      if (glob_patterns)
1324         print "Patterns=" glob_patterns
1325      exit 0
1326    }
1327
1328    if (match($0,/^sub-class-of/)) {
1329      if (match($0,/type="[^"]*/) || match($0,/type='"'"'[^'"'"']*/)) {
1330        print "X-KDE-IsAlso=" substr($0,RSTART+6,RLENGTH-6)
1331      }
1332      else {
1333        print "Error: '"'"'type'"'"' argument missing in " RS $0
1334        exit 1
1335      }
1336    }
1337    if (match($0,/^glob/)) {
1338      if (match($0,/pattern="[^"]*/) || match($0,/pattern='"'"'[^'"'"']*/)) {
1339        glob_patterns = glob_patterns substr($0,RSTART+9,RLENGTH-9) ";"
1340      }
1341      else {
1342        print "Error: '"'"'pattern'"'"' argument missing in " RS $0
1343        exit 1
1344      }
1345    }
1346    if (match($0,/^comment/)) {
1347      if (match($0,/xml:lang="[^"]*/) || match($0,/xml:lang='"'"'[^'"'"']*/)) {
1348        lang=substr($0,RSTART+10,RLENGTH-10)
1349      }
1350      else {
1351        lang=""
1352      }
1353      if (match($0,/>/)) {
1354        comment=substr($0,RSTART+1)
1355        sub("&lt;", "<", comment)
1356        sub("&gt;", ">", comment)
1357        sub("&amp;", "\\&", comment)
1358        if (lang)
1359           print "Comment[" lang "]=" comment
1360        else
1361           print "Comment=" comment
1362      }
1363    }
1364  }
1365}
1366END {
1367  if (!found) {
1368    print "Error: Mimetype '"'"'" the_type "'"'"' not found"
1369    exit 1
1370  }
1371}
1372' $x $basefile
1373                if [ "$?" = "1" ] ; then
1374                    grep -A 10 "^Error:" $kde_dir/$x.desktop >&2
1375                    rm $kde_dir/$x.desktop
1376                    exit 1
1377                fi
1378            done
1379        fi
1380
1381        umask $save_umask
1382        ;;
1383
1384    uninstall)
1385        for x in $xdg_dir ; do
1386            rm -f $x/$basefile
1387        done
1388
1389        # No quotes around $mimetypes
1390        for x in $mimetypes ; do
1391            if grep '^# Installed by xdg-mime' $kde_dir/$x.desktop >/dev/null 2>&1; then
1392                DEBUG 1 "Removing $kde_dir/$x.desktop (KDE 3.x support)"
1393                rm -f $kde_dir/$x.desktop
1394            fi
1395        done
1396        ;;
1397esac
1398
1399update_mime_database $xdg_base_dir
1400
1401exit_success
1402
1403