[Bug]: Desktop shortcuts for bottles with single quotes cause failures
Describe the bug
"Configure in Bottles" right-click on desktop shortcut fails when game has a single quote (apostrophe) in its title.
EDIT: I originally thought this was caused by the game having a single quote, but found out that it is the bottle name that causes the issue.
To Reproduce
- Create a shortcut in a bottle with a quote in its name, such as "Baldur's Gate 3".
- "Add a Desktop Entry" for the game.
- Attempt to launch using the desktop icon.
- Error: Syntax error in command flatpak run --command=bottles-cli com.usebottles.bottles run -p 'Baldur'"'"'s Gate 3' -b 'Baldur's Gate 3' -- %u coming from /home/deck/.local/share/applications/Baldur's Gate 3--Baldur's Gate 3--1703080590.362178.desktop
- From the desktop icon (in the menu or widget or wherever) right click and choose "Configure in Bottles".
- Error: Syntax error in command flatpak run com.usebottles.bottles -b 'Baldur's Gate 3'
Package
Flatpak from Flathub
Distribution
SteamOS Holo
Debugging Information
Official Package: true
Version: '51.9'
DE/WM: plasma-steamos-oneshot
Display:
X.org: true
X.org (port): :0
Wayland: false
Graphics:
vendors:
amd:
vendor: amd
envs:
DRI_PRIME: '1'
icd: /usr/lib/x86_64-linux-gnu/GL/vulkan/icd.d/radeon_icd.x86_64.json:/usr/lib/i386-linux-gnu/GL/vulkan/icd.d/radeon_icd.i686.json
prime:
integrated: null
discrete: null
Kernel:
Type: Linux
Version: 6.1.52-valve9-1-neptune-61
Disk:
Total: 7767031808
Free: 7766880256
RAM:
MemTotal: 14.5GiB
MemAvailable: 10.7GiB
Bottles_envs: null
Troubleshooting Logs
The error is in the command in the desktop shortcut, not in Bottles itself.
Additional context
I've found some other cases where single quotes cause issues in directory names as well. I noticed for example that https://github.com/bottlesdevs/Bottles/blob/f9c19ae7cd2c830ee085aad30c8076207331a7e0/bottles/backend/managers/installer.py#L251 is not safe against single quotes in values. It's probably worth checking everywhere that the shell is invoked.
Single quotes can be quoted to the shell like this:
flatpak run com.usebottles.bottles -b 'Baldur'"'"'s Gate 3'
I suggest applying shlex.quote in many cases:
>>> print(shlex.quote("Baldur's Gate 3"))
'Baldur'"'"'s Gate 3'
Workaround: Edit the .desktop file in .local/share/applications to add the proper quoting, e.g.
Exec=flatpak run --command=bottles-cli com.usebottles.bottles run -p 'Baldur'"'"'s Gate 3' -b 'Baldur'"'"'s Gate 3' -- %u
and further down (to fix the configure action)
Exec=flatpak run --command=bottles-cli com.usebottles.bottles run -p 'Baldur'"'"'s Gate 3' -b 'Baldur'"'"'s Gate 3' -- %u
It looks like this line https://github.com/bottlesdevs/Bottles/blob/f9c19ae7cd2c830ee085aad30c8076207331a7e0/bottles/backend/utils/manager.py#L245 is missing a shlex.quote on the bottle name. It should probably read
f.write(f"Exec={cmd_cli} run -p {shlex.quote(program.get('name'))} -b '{shlex.quote(config.get('Name'))}' -- %u\n")
It seems likely that many of these need quoting calls too:
grep -e "'{.*}'" -R .
./bottles/backend/wine/winepath.py:40: args = f"--unix '{path}'"
./bottles/backend/wine/winepath.py:63: args = f"--windows '{path}'"
./bottles/backend/wine/winepath.py:69: args = f"--long '{path}'"
./bottles/backend/wine/winepath.py:75: args = f"--short '{path}'"
./bottles/backend/wine/winecommand.py:489: logging.info(f"Running Gamescope command: '{command}'")
./bottles/backend/wine/winecommand.py:545: command = f"{command} ; sh '{post_script}'"
./bottles/backend/wine/net.py:17: args = f"start '{name}'"
./bottles/backend/wine/net.py:25: args = f"stop '{name}'"
./bottles/backend/wine/net.py:34: args = f"use '{name}'"
./bottles/backend/wine/uninstaller.py:17: args = f"--list | grep -i '{name}' | cut -f1 -d\\|"
./bottles/backend/wine/reg.py:126: logging.info(f"Import bundle result: '{res.data}'")
./bottles/backend/managers/steam.py:495: args = "bottles:run/'{0}'/'{1}'"
./bottles/backend/managers/installer.py:251: f"bash -c '{script}'",
./bottles/backend/utils/terminal.py:98: command = ' '.join(self.terminal) % "'sh -c %s'" % f'{command}'
./bottles/backend/utils/terminal.py:100: command = ' '.join(self.terminal) % "sh -c %s" % f'{command}'
./bottles/backend/utils/terminal.py:102: command = ' '.join(self.terminal) % "bash -c %s" % f'{command}'
./bottles/backend/utils/gpu.py:50: f"lspci | grep '{self.__vendors[_vendor]}'",
./bottles/backend/utils/gpu.py:140: f"lspci | grep -iP '{_query}'",
./bottles/backend/utils/gpu.py:164: f"lspci | grep -iP '{vendor.value}'",
./bottles/backend/utils/manager.py:245: f.write(f"Exec={cmd_cli} run -p {shlex.quote(program.get('name'))} -b '{config.get('Name')}' -- %u\n")
./bottles/backend/utils/manager.py:256: f.write(f"Exec={cmd_legacy} -b '{config.get('Name')}'\n")
./bottles/backend/utils/manager.py:287: f"{cmd_cli} run -p {shlex.quote(program.get('name'))} -b '{config.get('Path')}'",
./bottles/backend/utils/manager.py:289: f"{cmd_legacy} -b '{config.get('Name')}'"
./bottles/backend/utils/imagemagick.py:35: cmd = f"identify '{self.path}'"
./bottles/backend/utils/imagemagick.py:59: cmd = f"convert '{self.path}'"
./bottles/backend/utils/imagemagick.py:79: cmd += f" '{dest}'"
./bottles/frontend/cli/cli.py:382: sys.stdout.write(f"'{_name}' added to '{bottle.Name}'!")
./bottles/frontend/cli/cli.py:643: f.write(f"export {k}='{v}'\n")
./utils/flatpak-pip-generator.py:376: with tempfile.TemporaryDirectory(prefix='{}-{}'.format(tempdir_prefix, packag
You can just use subprocess.list2cmdline instead of quoting each single argument.