Bug: 773085 Author: Vincent Bernat Description: fix safety of DE-agnostic xdg-open Let the shell do the splitting of the command contained in the .desktop file (set -- $(sed ...)). . Use "$@" behaving like an array. We cannot modify this array but we can append to it (with set -- "$@" "$newarg"). Basically, we take $command_exec and then shift. Then, we iterate on each argument using a counter and if the argument needs to be modified (because this is the place holder), we append the modified version, otherwise, we append it unmodified. At the end, "$@" is the array of arguments to be passed to "$command_exec". If no replacement has happened, we also append the target file. . No magic quoting is done, no evaluation. I think this is a safe alternative to the current script. I can also push it upstream. --- a/scripts/xdg-open.in 2015-01-03 22:22:18.513474060 +0100 +++ b/scripts/xdg-open.in 2015-01-08 08:42:47.513093876 +0100 @@ -526,6 +526,7 @@ open_generic_xdg_mime() { + target="$1" filetype="$2" default=`xdg-mime query default "$filetype"` if [ -n "$default" ] ; then @@ -546,17 +547,34 @@ fi if [ -r "$file" ] ; then - command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`" - command_exec=`which $command 2>/dev/null` - arguments="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | last_word`" - local sed_escaped_url="$(echo "$1" | sed -e 's/[&\\]/\\&/g')" - arguments_exec="$(echo "$arguments" | sed -e 's*%[fFuU]*$sed_escaped_url*g')" + set -- $(sed -n 's/^Exec\(\[[^]]*\]\)\{0,1\}=//p' "$file") + command_exec="$(which "$1" 2> /dev/null)" if [ -x "$command_exec" ] ; then - if echo $arguments | grep -iq '%[fFuU]' ; then - eval '$command_exec' '$arguments_exec' - else - eval '$command_exec' '$arguments_exec' '"$1"' - fi + shift + # We need to replace any occurrence of "%f", "%F" and + # the like by the target file. We examine each + # argument and append the modified argument to the + # end then shift. + args=$# + replaced=0 + while [ $args -gt 0 ]; do + case $1 in + %[fFuU]) + replaced=1 + arg="$target" + shift + set -- "$@" "$arg" + ;; + *) + arg="$1" + shift + set -- "$@" "$arg" + ;; + esac + args=$(( $args - 1 )) + done + [ $replaced -eq 1 ] || set -- "$@" "$target" + "$command_exec" "$@" if [ $? -eq 0 ]; then exit_success