1#!/bin/sh
2#---------------------------------------------
3#   xdg-settings
4#
5#   Utility script to get various settings from the desktop environment.
6#
7#   Refer to the usage() function below for usage.
8#
9#   Copyright 2009, Google Inc.
10#
11#   LICENSE:
12#
13#   Permission is hereby granted, free of charge, to any person obtaining a
14#   copy of this software and associated documentation files (the "Software"),
15#   to deal in the Software without restriction, including without limitation
16#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
17#   and/or sell copies of the Software, and to permit persons to whom the
18#   Software is furnished to do so, subject to the following conditions:
19#
20#   The above copyright notice and this permission notice shall be included
21#   in all copies or substantial portions of the Software.
22#
23#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
27#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
28#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29#   OTHER DEALINGS IN THE SOFTWARE.
30#
31#---------------------------------------------
32
33manualpage()
34{
35cat << _MANUALPAGE
36Name
37
38xdg-settings - get various settings from the desktop environment
39
40Synopsis
41
42xdg-settings { get | check | set } {property} [subproperty] [value]
43
44xdg-settings { --help | --list | --manual | --version }
45
46Description
47
48xdg-settings gets various settings from the desktop environment. For instance,
49desktop environments often provide proxy configuration and default web browser
50settings. Using xdg-settings these parameters can be extracted for use by
51applications that do not use the desktop environment's libraries (which would
52use the settings natively).
53
54xdg-settings is for use inside a desktop session only. It is not recommended to
55use xdg-settings as root.
56
57Options
58
59--help
60    Show command synopsis.
61--list
62    List all properties xdg-settings knows about.
63--manual
64    Show this manualpage.
65--version
66    Show the xdg-utils version information.
67
68Properties
69
70When using xdg-settings to get, check or set a destkop setting, properties and
71possibly sub-properties are used to specify the setting to be changed.
72
73Some properties (such as default-web-browser) fully describe the setting to be
74changed. Other properties (such as default-url-scheme-handler) require more
75information (in this case the actual scheme to set the default handler for)
76which must be provided in a sub-property.
77
78Exit Codes
79
80An exit code of 0 indicates success while a non-zero exit code indicates
81failure. The following failure codes can be returned:
82
831
84    Error in command line syntax.
852
86    One of the files passed on the command line did not exist.
873
88    A required tool could not be found.
894
90    The action failed.
91
92Examples
93
94Get the desktop file name of the current default web browser
95
96        xdg-settings get default-web-browser
97
98
99Check whether the default web browser is firefox.desktop, which can be false
100even if "get default-web-browser" says that is the current value (if only some
101of the underlying settings actually reflect that value)
102
103        xdg-settings check default-web-browser firefox.desktop
104
105
106Set the default web browser to google-chrome.desktop
107
108        xdg-settings set default-web-browser google-chrome.desktop
109
110
111Set the default mailto URL scheme handler to be evolution.desktop
112
113        xdg-settings set default-url-scheme-handler mailto evolution.desktop
114
115
116_MANUALPAGE
117}
118
119usage()
120{
121cat << _USAGE
122xdg-settings - get various settings from the desktop environment
123
124Synopsis
125
126xdg-settings { get | check | set } {property} [subproperty] [value]
127
128xdg-settings { --help | --list | --manual | --version }
129
130_USAGE
131}
132
133#@xdg-utils-common@
134
135#----------------------------------------------------------------------------
136#   Common utility functions included in all XDG wrapper scripts
137#----------------------------------------------------------------------------
138
139DEBUG()
140{
141  [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
142  [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
143  shift
144  echo "$@" >&2
145}
146
147# This handles backslashes but not quote marks.
148first_word()
149{
150    read first rest
151    echo "$first"
152}
153
154#-------------------------------------------------------------
155# map a binary to a .desktop file
156binary_to_desktop_file()
157{
158    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
159    binary="`which "$1"`"
160    binary="`readlink -f "$binary"`"
161    base="`basename "$binary"`"
162    IFS=:
163    for dir in $search; do
164        unset IFS
165        [ "$dir" ] || continue
166        [ -d "$dir/applications" -o -d "$dir/applnk" ] || continue
167        for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do
168            [ -r "$file" ] || continue
169            # Check to make sure it's worth the processing.
170            grep -q "^Exec.*$base" "$file" || continue
171            # Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop").
172            grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
173            command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
174            command="`which "$command"`"
175            if [ x"`readlink -f "$command"`" = x"$binary" ]; then
176                # Fix any double slashes that got added path composition
177                echo "$file" | sed -e 's,//*,/,g'
178                return
179            fi
180        done
181    done
182}
183
184#-------------------------------------------------------------
185# map a .desktop file to a binary
186## FIXME: handle vendor dir case
187desktop_file_to_binary()
188{
189    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
190    desktop="`basename "$1"`"
191    IFS=:
192    for dir in $search; do
193        unset IFS
194        [ "$dir" -a -d "$dir/applications" ] || continue
195        file="$dir/applications/$desktop"
196        [ -r "$file" ] || continue
197        # Remove any arguments (%F, %f, %U, %u, etc.).
198        command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
199        command="`which "$command"`"
200        readlink -f "$command"
201        return
202    done
203}
204
205#-------------------------------------------------------------
206# Exit script on successfully completing the desired operation
207
208exit_success()
209{
210    if [ $# -gt 0 ]; then
211        echo "$@"
212        echo
213    fi
214
215    exit 0
216}
217
218
219#-----------------------------------------
220# Exit script on malformed arguments, not enough arguments
221# or missing required option.
222# prints usage information
223
224exit_failure_syntax()
225{
226    if [ $# -gt 0 ]; then
227        echo "xdg-settings: $@" >&2
228        echo "Try 'xdg-settings --help' for more information." >&2
229    else
230        usage
231        echo "Use 'man xdg-settings' or 'xdg-settings --manual' for additional info."
232    fi
233
234    exit 1
235}
236
237#-------------------------------------------------------------
238# Exit script on missing file specified on command line
239
240exit_failure_file_missing()
241{
242    if [ $# -gt 0 ]; then
243        echo "xdg-settings: $@" >&2
244    fi
245
246    exit 2
247}
248
249#-------------------------------------------------------------
250# Exit script on failure to locate necessary tool applications
251
252exit_failure_operation_impossible()
253{
254    if [ $# -gt 0 ]; then
255        echo "xdg-settings: $@" >&2
256    fi
257
258    exit 3
259}
260
261#-------------------------------------------------------------
262# Exit script on failure returned by a tool application
263
264exit_failure_operation_failed()
265{
266    if [ $# -gt 0 ]; then
267        echo "xdg-settings: $@" >&2
268    fi
269
270    exit 4
271}
272
273#------------------------------------------------------------
274# Exit script on insufficient permission to read a specified file
275
276exit_failure_file_permission_read()
277{
278    if [ $# -gt 0 ]; then
279        echo "xdg-settings: $@" >&2
280    fi
281
282    exit 5
283}
284
285#------------------------------------------------------------
286# Exit script on insufficient permission to write a specified file
287
288exit_failure_file_permission_write()
289{
290    if [ $# -gt 0 ]; then
291        echo "xdg-settings: $@" >&2
292    fi
293
294    exit 6
295}
296
297check_input_file()
298{
299    if [ ! -e "$1" ]; then
300        exit_failure_file_missing "file '$1' does not exist"
301    fi
302    if [ ! -r "$1" ]; then
303        exit_failure_file_permission_read "no permission to read file '$1'"
304    fi
305}
306
307check_vendor_prefix()
308{
309    file_label="$2"
310    [ -n "$file_label" ] || file_label="filename"
311    file=`basename "$1"`
312    case "$file" in
313       [a-zA-Z]*-*)
314         return
315         ;;
316    esac
317
318    echo "xdg-settings: $file_label '$file' does not have a proper vendor prefix" >&2
319    echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
320    echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
321    echo "Use --novendor to override or 'xdg-settings --manual' for additional info." >&2
322    exit 1
323}
324
325check_output_file()
326{
327    # if the file exists, check if it is writeable
328    # if it does not exists, check if we are allowed to write on the directory
329    if [ -e "$1" ]; then
330        if [ ! -w "$1" ]; then
331            exit_failure_file_permission_write "no permission to write to file '$1'"
332        fi
333    else
334        DIR=`dirname "$1"`
335        if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
336            exit_failure_file_permission_write "no permission to create file '$1'"
337        fi
338    fi
339}
340
341#----------------------------------------
342# Checks for shared commands, e.g. --help
343
344check_common_commands()
345{
346    while [ $# -gt 0 ] ; do
347        parm="$1"
348        shift
349
350        case "$parm" in
351            --help)
352            usage
353            echo "Use 'man xdg-settings' or 'xdg-settings --manual' for additional info."
354            exit_success
355            ;;
356
357            --manual)
358            manualpage
359            exit_success
360            ;;
361
362            --version)
363            echo "xdg-settings 1.1.0 rc1"
364            exit_success
365            ;;
366        esac
367    done
368}
369
370check_common_commands "$@"
371
372[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
373if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
374    # Be silent
375    xdg_redirect_output=" > /dev/null 2> /dev/null"
376else
377    # All output to stderr
378    xdg_redirect_output=" >&2"
379fi
380
381#--------------------------------------
382# Checks for known desktop environments
383# set variable DE to the desktop environments name, lowercase
384
385detectDE()
386{
387    # see https://bugs.freedesktop.org/show_bug.cgi?id=34164
388    unset GREP_OPTIONS
389
390    if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
391      case "${XDG_CURRENT_DESKTOP}" in
392         GNOME)
393           DE=gnome;
394           ;;
395         KDE)
396           DE=kde;
397           ;;
398         LXDE)
399           DE=lxde;
400           ;;
401         XFCE)
402           DE=xfce
403      esac
404    fi
405
406    if [ x"$DE" = x"" ]; then
407      # classic fallbacks
408      if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
409      elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
410      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;
411      elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
412      elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
413      fi
414    fi
415
416    if [ x"$DE" = x"" ]; then
417      # fallback to checking $DESKTOP_SESSION
418      case "$DESKTOP_SESSION" in
419         gnome)
420           DE=gnome;
421           ;;
422         LXDE)
423           DE=lxde;
424           ;;
425         xfce|xfce4)
426           DE=xfce;
427           ;;
428      esac
429    fi
430
431    if [ x"$DE" = x"" ]; then
432      # fallback to uname output for other platforms
433      case "$(uname 2>/dev/null)" in
434        Darwin)
435          DE=darwin;
436          ;;
437      esac
438    fi
439
440    if [ x"$DE" = x"gnome" ]; then
441      # gnome-default-applications-properties is only available in GNOME 2.x
442      # but not in GNOME 3.x
443      which gnome-default-applications-properties > /dev/null 2>&1  || DE="gnome3"
444    fi
445}
446
447#----------------------------------------------------------------------------
448# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
449# It also always returns 1 in KDE 3.4 and earlier
450# Simply return 0 in such case
451
452kfmclient_fix_exit_code()
453{
454    version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`
455    major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
456    minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
457    release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
458    test "$major" -gt 3 && return $1
459    test "$minor" -gt 5 && return $1
460    test "$release" -gt 4 && return $1
461    return 0
462}
463
464check_desktop_filename()
465{
466    case "$1" in
467      */*)
468        exit_failure_syntax "invalid application name"
469        ;;
470      *.desktop)
471        return
472        ;;
473      *)
474        exit_failure_syntax "invalid application name"
475        ;;
476    esac
477}
478
479# {{{ default browser
480# {{{ utility functions
481
482# In order to remove an application from the automatically-generated list of
483# applications for handling a given MIME type, the desktop environment may copy
484# the global .desktop file into the user's .local directory, and remove that
485# MIME type from its list. In that case, we must restore the MIME type to the
486# application's list of MIME types before we can set it as the default for that
487# MIME type. (We can't just delete the local version, since the user may have
488# made other changes to it as well. So, tweak the existing file.)
489# This function is hard-coded for text/html but it could be adapted if needed.
490fix_local_desktop_file()
491{
492    if test -z "$2" ; then
493        MIME="text/html"
494    else
495        MIME="$2"
496    fi
497    apps="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
498    # No local desktop file?
499    [ ! -f "$apps/$1" ] && return
500    MIMETYPES="`grep "^MimeType=" "$apps/$1" | cut -d= -f 2-`"
501    case "$MIMETYPES" in
502      $MIME\;*|*\;$MIME\;*|*\;$MIME\;|*\;$MIME)
503        # Already has the mime-type? Great!
504        return 0
505        ;;
506    esac
507
508    # Add the mime-type to the list
509    temp="`mktemp "$apps/$1.XXXXXX"`" || return
510    grep -v "^MimeType=" "$apps/$1" >> "$temp"
511    echo "MimeType=$MIME;$MIMETYPES" >> "$temp"
512
513    oldlines="`wc -l < "$apps/$1"`"
514    newlines="`wc -l < "$temp"`"
515    # The new file should have at least as many lines as the old.
516    if [ $oldlines -le $newlines ]; then
517        mv "$temp" "$apps/$1"
518        # This can take a little bit to get noticed.
519        sleep 4
520    else
521        rm -f "$temp"
522        return 1
523    fi
524}
525
526# }}} utility functions
527# {{{ MIME utilities
528
529xdg_mime_fixup()
530{
531    # xdg-mime may use ktradertest, which will fork off a copy of kdeinit if
532    # one does not already exist. It will exit after about 15 seconds if no
533    # further processes need it around. But since it does not close its stdout,
534    # the shell (via grep) will wait around for kdeinit to exit. If we start a
535    # copy here, that copy will be used in xdg-mime and we will avoid waiting.
536    if [ "$DE" = kde -a -z "$XDG_MIME_FIXED" ]; then
537        ktradertest text/html Application > /dev/null 2>&1
538        # Only do this once, as we only need it once.
539        XDG_MIME_FIXED=yes
540    fi
541}
542
543get_browser_mime()
544{
545    if test -z "$1" ; then
546        MIME="text/html"
547    else
548        MIME="$1"
549    fi
550    xdg_mime_fixup
551    xdg-mime query default "$MIME"
552}
553
554set_browser_mime()
555{
556    xdg_mime_fixup
557    if test -z "$2" ; then
558        MIME="text/html"
559    else
560        MIME="$2"
561    fi
562    orig="`get_browser_mime $MIME`"
563    # Fixing the local desktop file can actually change the default browser all
564    # by itself, so we fix it only after querying to find the current default.
565    fix_local_desktop_file "$1" "$MIME" || return
566    mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/applications"
567    xdg-mime default "$1" "$MIME" || return
568    if [ x"`get_browser_mime`" != x"$1" ]; then
569        # Put back the original value
570        xdg-mime default "$orig" "$MIME"
571        exit_failure_operation_failed
572    fi
573}
574
575# }}} MIME utilities
576# {{{ KDE utilities
577
578# Reads the KDE configuration setting, compensating for a bug in some versions of kreadconfig.
579read_kde_config()
580{
581    configfile="$1"
582    configsection="$2"
583    configkey="$3"
584    application="`kreadconfig --file $configfile --group $configsection --key $configkey`"
585    if [ x"$application" != x ]; then
586        echo "$application"
587    else
588        # kreadconfig in KDE 4 may not notice Key[$*]=... localized settings, so
589        # check by hand if it didn't find anything (oddly kwriteconfig works
590        # fine though).
591        configfile_dir=`kde${KDE_SESSION_VERSION}-config --path config  | cut -d ':' -f 1`
592        configfile_path="$configfile_dir/$configfile"
593        [ ! -f "$configfile_path" ] && return
594        # This will only take the first value if there is more than one.
595        grep "^$configkey"'\[$[^]=]*\]=' "$configfile_path" | head -n 1 | cut -d= -f 2-
596    fi
597}
598
599# }}} KDE utilities
600# {{{ KDE
601
602# Resolves the KDE browser setting to a binary: if prefixed with !, simply removes it;
603# otherwise, uses desktop_file_to_binary to get the binary out of the desktop file.
604resolve_kde_browser()
605{
606    [ -z "$browser" ] && return
607    case "$browser" in
608      !*)
609        echo "${browser#!}"
610        ;;
611      *)
612        desktop_file_to_binary "$browser"
613        ;;
614    esac
615}
616
617# Does the opposite of resolve_kde_browser: if prefixed with !, tries to find a desktop
618# file corresponding to the binary, otherwise just returns the desktop file name.
619resolve_kde_browser_desktop()
620{
621    [ -z "$browser" ] && return
622    case "$browser" in
623      !*)
624        desktop="`binary_to_desktop_file "${browser#!}"`"
625        basename "$desktop"
626        ;;
627      *)
628        echo "$browser"
629        ;;
630    esac
631}
632
633read_kde_browser()
634{
635    read_kde_config kdeglobals General BrowserApplication
636}
637
638get_browser_kde()
639{
640    browser="`read_kde_browser`"
641    if [ x"$browser" = x ]; then
642        # No explicit default browser; KDE will use the MIME type text/html.
643        get_browser_mime
644    else
645        resolve_kde_browser_desktop
646    fi
647}
648
649check_browser_kde()
650{
651    check="`desktop_file_to_binary "$1"`"
652    if [ -z "$check" ]; then
653        echo no
654        exit_success
655    fi
656    browser="`read_kde_browser`"
657    binary="`resolve_kde_browser`"
658    # Because KDE will use the handler for MIME type text/html if this value
659    # is empty, we allow either the empty string or a match to $check here.
660    if [ x"$binary" != x -a x"$binary" != x"$check" ]; then
661        echo no
662        exit_success
663    fi
664    browser="`get_browser_mime`"
665    binary="`desktop_file_to_binary "$browser"`"
666    if [ x"$binary" != x"$check" ]; then
667        echo no
668        exit_success
669    fi
670    echo yes
671    exit_success
672}
673
674set_browser_kde()
675{
676    set_browser_mime "$1" || return
677    kwriteconfig --file kdeglobals --group General --key BrowserApplication "$1"
678}
679
680# }}} KDE
681# {{{ GNOME
682
683get_browser_gnome()
684{
685    binary="`gconftool-2 --get /desktop/gnome/applications/browser/exec | first_word`"
686    if [ x"$binary" = x ]; then
687        # No default browser; GNOME might use the MIME type text/html.
688        get_browser_mime
689    else
690        # gconftool gives the binary (maybe with %s etc. afterward),
691        # but we want the desktop file name, not the binary. So, we
692        # have to find the desktop file to which it corresponds.
693        desktop="`binary_to_desktop_file "$binary"`"
694        basename "$desktop"
695    fi
696}
697
698check_browser_gnome()
699{
700    check="`desktop_file_to_binary "$1"`"
701    if [ -z "$check" ]; then
702        echo no
703        exit_success
704    fi
705    binary="`gconftool-2 --get /desktop/gnome/applications/browser/exec | first_word`"
706    if [ x"$binary" != x"$check" ]; then
707        echo no
708        exit_success
709    fi
710    # Check HTTP and HTTPS, but not about: and unknown:.
711    for protocol in http https; do
712        binary="`gconftool-2 --get /desktop/gnome/url-handlers/$protocol/command | first_word`"
713        if [ x"$binary" != x"$check" ]; then
714            echo no
715            exit_success
716        fi
717    done
718    browser="`get_browser_mime`"
719    binary="`desktop_file_to_binary "$browser"`"
720    if [ x"$binary" != x"$check" ]; then
721        echo no
722        exit_success
723    fi
724    echo yes
725    exit_success
726}
727
728set_browser_gnome()
729{
730    binary="`desktop_file_to_binary "$1"`"
731    [ "$binary" ] || exit_failure_file_missing
732    set_browser_mime "$1" || return
733
734    # Set the default browser.
735    gconftool-2 --type string --set /desktop/gnome/applications/browser/exec "$binary"
736    gconftool-2 --type bool --set /desktop/gnome/applications/browser/needs_term false
737    gconftool-2 --type bool --set /desktop/gnome/applications/browser/nremote true
738    # Set the handler for HTTP and HTTPS.
739    for protocol in http https; do
740        gconftool-2 --type string --set /desktop/gnome/url-handlers/$protocol/command "$binary %s"
741        gconftool-2 --type bool --set /desktop/gnome/url-handlers/$protocol/needs_terminal false
742        gconftool-2 --type bool --set /desktop/gnome/url-handlers/$protocol/enabled true
743    done
744    # Set the handler for about: and unknown URL types.
745    for protocol in about unknown; do
746        gconftool-2 --type string --set /desktop/gnome/url-handlers/$protocol/command "$binary %s"
747    done
748}
749
750# }}} GNOME
751# {{{ GNOME 3.x
752
753get_browser_gnome3()
754{
755    get_browser_mime "x-scheme-handler/http"
756}
757
758check_browser_gnome3()
759{
760    desktop="$1"
761    check="`desktop_file_to_binary "$1"`"
762    if [ -z "$check" ]; then
763        echo no
764        exit_success
765    fi
766    # Check HTTP and HTTPS, but not about: and unknown:.
767    for protocol in http https; do
768        browser="`get_browser_mime "x-scheme-handler/$protocol"`"
769        if [ x"$browser" != x"$desktop" ]; then
770            echo no
771            exit_success
772        fi
773    done
774    echo yes
775    exit_success
776}
777
778set_browser_gnome3()
779{
780    binary="`desktop_file_to_binary "$1"`"
781    [ "$binary" ] || exit_failure_file_missing
782    set_browser_mime "$1" || return
783
784    # Set the default browser.
785    for protocol in http https about unknown; do
786        set_browser_mime "$1" "x-scheme-handler/$protocol" || return
787    done
788}
789# }}} GNOME 3.x
790# {{{ xfce
791
792get_browser_xfce()
793{
794    search="${XDG_CONFIG_HOME:-$HOME/.config}:${XDG_CONFIG_DIRS:-/etc/xdg}"
795    IFS=:
796    for dir in $search; do
797        unset IFS
798        [ "$dir" -a -d "$dir/xfce4" ] || continue
799        file="$dir/xfce4/helpers.rc"
800        [ -r "$file" ] || continue
801        grep -q "^WebBrowser=" "$file" || continue
802        desktop="`grep "^WebBrowser=" "$file" | cut -d= -f 2-`"
803        echo "$desktop.desktop"
804        return
805    done
806    exit_failure_operation_failed
807}
808
809check_browser_xfce()
810{
811    browser="`get_browser_xfce`"
812    if [ x"$browser" != x"$1" ]; then
813        echo no
814        exit_success
815    fi
816    echo yes
817    exit_success
818}
819
820check_xfce_desktop_file()
821{
822    # Annoyingly, xfce wants its .desktop files in a separate directory instead
823    # of the standard locations, and requires a few custom tweaks to them:
824    # "Type" must be "X-XFCE-Helper"
825    # "X-XFCE-Category" must be "WebBrowser" (for web browsers, anyway)
826    # "X-XFCE-Commands" and "X-XFCE-CommandsWithParameter" must be set
827    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
828    IFS=:
829    for dir in $search; do
830        unset IFS
831        [ "$dir" -a -d "$dir/xfce4/helpers" ] || continue
832        file="$dir/xfce4/helpers/$1"
833        # We have the file, no need to create it.
834        [ -r "$file" ] && return
835    done
836    IFS=:
837    for dir in $search; do
838        unset IFS
839        [ "$dir" -a -d "$dir/applications" ] || continue
840        file="$dir/applications/$1"
841        if [ -r "$file" ]; then
842            # Found a file to convert.
843            target="${XDG_DATA_HOME:-$HOME/.local/share}/xfce4/helpers"
844            mkdir -p "$target"
845            grep -v "^Type=" "$file" > "$target/$1"
846            echo "Type=X-XFCE-Helper" >> "$target/$1"
847            echo "X-XFCE-Category=WebBrowser" >> "$target/$1"
848            # Change %F, %f, %U, and %u to "%s".
849            command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | sed -e 's/%[FfUu]/"%s"/g'`"
850            echo "X-XFCE-Commands=`echo "$command" | first_word`" >> "$target/$1"
851            echo "X-XFCE-CommandsWithParameter=$command" >> "$target/$1"
852            return
853        fi
854    done
855    return 1
856}
857
858set_browser_xfce()
859{
860    check_xfce_desktop_file "$1" || exit_failure_operation_failed
861
862    helper_dir="${XDG_CONFIG_HOME:-$HOME/.config}/xfce4"
863    if [ ! -d "$helper_dir" ]; then
864        mkdir -p "$helper_dir" || exit_failure_operation_failed
865    fi
866
867    helpers_rc="$helper_dir/helpers.rc"
868    # Create the file if it does not exist to avoid special cases below.
869    if [ ! -r "$helpers_rc" ]; then
870        touch "$helpers_rc" || exit_failure_operation_failed
871    fi
872
873    temp="`mktemp "$helpers_rc.XXXXXX"`" || return
874    grep -v "^WebBrowser=" "$helpers_rc" >> "$temp"
875    echo "WebBrowser=${1%.desktop}" >> "$temp"
876
877    oldlines="`wc -l < "$helpers_rc"`"
878    newlines="`wc -l < "$temp"`"
879    # The new file should have at least as many lines as the old.
880    if [ $oldlines -le $newlines ]; then
881        mv "$temp" "$helpers_rc"
882    else
883        rm -f "$temp"
884        return 1
885    fi
886}
887
888# }}} xfce
889# }}} default browser
890
891# {{{ default url scheme handler
892
893exit_unimplemented_default_handler()
894{
895    exit_failure_operation_impossible "default-url-scheme-handler not implemented for $DE"
896}
897
898# {{{ KDE
899
900# Recent versions of KDE support default scheme handler applications using the
901# mime type of x-scheme-handler/scheme. Older versions will not support this
902# but do have support for setting a default mail handler. There is also a
903# system in KDE where .protocol files can be used, however this is not
904# supported by this script. When reading a scheme handler we will use the
905# default mail handler for the mailto scheme, otherwise we will use the mime
906# type x-scheme-handler/scheme.
907
908get_url_scheme_handler_kde()
909{
910    if [ "$1" = "mailto" ]; then
911        handler="`read_kde_config emaildefaults PROFILE_Default EmailClient | first_word`"
912        echo "handler is $handler"
913        if [ x"$handler" != x ]; then
914            binary_to_desktop_file "$handler"
915        else
916            get_browser_mime "x-scheme-handler/$1"
917        fi
918    else
919        get_browser_mime "x-scheme-handler/$1"
920    fi
921}
922
923check_url_scheme_handler_kde()
924{
925    check="`desktop_file_to_binary "$2"`"
926    if [ -z "$check" ]; then
927        echo no
928        exit_success
929    fi
930    if [ x"$1" = "mailto" ]; then
931        binary="`read_kde_config emaildefaults PROFILE_Default EmailClient`"
932        if [ x"$binary" != x"$check" ]; then
933            echo no
934            exit_success
935        fi
936    fi
937    handler="`get_browser_mime x-scheme-handler/$1`"
938    binary="`desktop_file_to_binary "$handler"`"
939    if [ x"$binary" != x"$check" ]; then
940        echo no
941        exit_success
942    fi
943    echo yes
944    exit_success
945}
946
947set_url_scheme_handler_kde()
948{
949    set_browser_mime "$2" "x-scheme-handler/$1" || return
950    if [ "$1" = "mailto" ]; then
951        binary="`desktop_file_to_binary "$2"`"
952        kwriteconfig --file emaildefaults --group PROFILE_Default --key EmailClient "$binary"
953    fi
954}
955
956# }}} KDE
957# {{{ GNOME
958
959get_url_scheme_handler_gnome()
960{
961    binary="`gconftool-2 --get /desktop/gnome/url-handlers/$1/command | first_word`"
962    if [ x"$binary" != x"" ]; then
963        # gconftool gives the binary (maybe with %s etc. afterward),
964        # but we want the desktop file name, not the binary. So, we
965        # have to find the desktop file to which it corresponds.
966        desktop="`binary_to_desktop_file "$binary"`"
967        basename "$desktop"
968    fi
969}
970
971check_url_scheme_handler_gnome()
972{
973    check="`desktop_file_to_binary "$2"`"
974    if [ -z "$check" ]; then
975        echo no
976        exit_success
977    fi
978    binary="`gconftool-2 --get /desktop/gnome/url-handlers/$1/command | first_word`"
979    if [ x"$binary" != x"$check" ]; then
980        echo no
981        exit_success
982    fi
983    echo yes
984    exit_success
985}
986
987set_url_scheme_handler_gnome()
988{
989    binary="`desktop_file_to_binary "$2"`"
990    [ "$binary" ] || exit_failure_file_missing
991
992    gconftool-2 --type string --set /desktop/gnome/url-handlers/$1/command "$binary %s"
993    gconftool-2 --type bool --set /desktop/gnome/url-handlers/$1/needs_terminal false
994    gconftool-2 --type bool --set /desktop/gnome/url-handlers/$1/enabled true
995}
996
997# }}} GNOME
998# {{{ GNOME 3.x
999
1000get_url_scheme_handler_gnome3()
1001{
1002    get_browser_mime "x-scheme-handler/$1"
1003}
1004
1005check_url_scheme_handler_gnome3()
1006{
1007    desktop="$2"
1008    check="`desktop_file_to_binary "$2"`"
1009    if [ -z "$check" ]; then
1010        echo no
1011        exit_success
1012    fi
1013    browser="`get_browser_mime "x-scheme-handler/$1"`"
1014    if [ x"$browser" != x"$desktop" ]; then
1015        echo no
1016        exit_success
1017    fi
1018    echo yes
1019    exit_success
1020}
1021
1022set_url_scheme_handler_gnome3()
1023{
1024    binary="`desktop_file_to_binary "$2"`"
1025    [ "$binary" ] || exit_failure_file_missing
1026    set_browser_mime "$2" || return
1027
1028    # Set the default browser.
1029    set_browser_mime "$2" "x-scheme-handler/$1" || return
1030}
1031
1032# }}} GNOME 3.x
1033# {{{ xfce
1034
1035get_url_scheme_handler_xfce()
1036{
1037    exit_unimplemented_default_handler "$1"
1038}
1039
1040check_url_scheme_handler_xfce()
1041{
1042    exit_unimplemented_default_handler "$1"
1043}
1044
1045set_url_scheme_handler_xfce()
1046{
1047    exit_unimplemented_default_handler "$1"
1048}
1049
1050# }}} xfce
1051# }}} default protocol handler
1052
1053dispatch_specific()
1054{
1055    # The PROP comments in this function are used to generate the output of
1056    # the --list option. The formatting is important. Make sure to line up the
1057    # property descriptions with spaces so that it will look nice.
1058    if [ x"$op" = x"get" ]; then
1059        case "$parm" in
1060          default-web-browser) # PROP:           Default web browser
1061            get_browser_$DE
1062            ;;
1063
1064          default-url-scheme-handler) # PROP:    Default handler for URL scheme
1065            get_url_scheme_handler_$DE "$1"
1066            ;;
1067
1068          *)
1069            exit_failure_syntax
1070            ;;
1071        esac
1072    elif [ x"$op" = x"check" ]; then
1073        case "$parm" in
1074          default-web-browser)
1075            check_desktop_filename "$1"
1076            check_browser_$DE "$1"
1077            ;;
1078
1079          default-url-scheme-handler)
1080            check_desktop_filename "$2"
1081            check_url_scheme_handler_$DE "$1" "$2"
1082            ;;
1083
1084          *)
1085            exit_failure_syntax
1086            ;;
1087        esac
1088    else # set
1089        case "$parm" in
1090          default-web-browser)
1091	    [ $# -eq 1 ] || exit_failure_syntax "unexpected/missing argument"
1092            check_desktop_filename "$1"
1093            set_browser_$DE "$1"
1094            ;;
1095
1096          default-url-scheme-handler)
1097	    [ $# -eq 2 ] || exit_failure_syntax "unexpected/missing argument"
1098            check_desktop_filename "$2"
1099            set_url_scheme_handler_$DE "$1" "$2"
1100            ;;
1101
1102          *)
1103            exit_failure_syntax
1104            ;;
1105        esac
1106    fi
1107
1108    if [ $? -eq 0 ]; then
1109        exit_success
1110    else
1111        exit_failure_operation_failed
1112    fi
1113}
1114
1115dispatch_generic()
1116{
1117    # We only know how to get or check the default web browser.
1118    [ x"$op" != x"get" -a x"$op" != x"check" ] && exit_failure_operation_impossible
1119    [ x"$parm" != x"default-web-browser" ] && exit_failure_operation_impossible
1120
1121    # First look in $BROWSER
1122    if [ x"$BROWSER" != x ]; then
1123        binary="`which "${BROWSER%%:*}"`"
1124    else
1125        # Debian and Ubuntu (and others?) have x-www-browser.
1126        binary="`which x-www-browser`"
1127    fi
1128
1129    [ "$binary" ] || exit_failure_operation_failed
1130
1131    binary="`readlink -f "$binary"`"
1132
1133    [ "$binary" ] || exit_failure_operation_failed
1134
1135    if [ x"$op" = x"get" ]; then
1136        desktop="`binary_to_desktop_file "$binary"`"
1137        basename "$desktop"
1138    else
1139        # $op = "check"
1140        check="`desktop_file_to_binary "$1"`"
1141        if [ -z "$check" ]; then
1142            echo no
1143            exit_success
1144        fi
1145        if [ x"$binary" != x"$check" ]; then
1146            echo no
1147            exit_success
1148        fi
1149        echo yes
1150    fi
1151    exit_success
1152}
1153
1154if [ x"$1" = x"--list" ]; then
1155    echo "Known properties:"
1156    # Extract the property names from dispatch_specific() above.
1157    grep "^[ 	]*[^)]*) # PROP:" "$0" | sed -e 's/^[ 	]*\([^)]*\)) # PROP: \(.*\)$/  \1 \2/' | sort
1158    exit_success
1159fi
1160
1161[ x"$1" != x ] || exit_failure_syntax "no operation given"
1162[ x"$2" != x ] || exit_failure_syntax "no parameter name given"
1163[ x"$1" = x"get" -o x"$3" != x ] || exit_failure_syntax "no parameter value given"
1164
1165op="$1"
1166parm="$2"
1167shift 2
1168
1169if [ x"$op" != x"get" -a x"$op" != x"check" -a x"$op" != x"set" ]; then
1170  exit_failure_syntax "invalid operation"
1171fi
1172
1173detectDE
1174
1175case "$DE" in
1176    kde|gnome*|xfce)
1177    dispatch_specific "$@"
1178    ;;
1179
1180    generic)
1181    dispatch_generic "$@"
1182    ;;
1183
1184    *)
1185    exit_failure_operation_impossible "unknown desktop environment"
1186    ;;
1187esac
1188