Bottles icon indicating copy to clipboard operation
Bottles copied to clipboard

[Bug]: Desktop shortcuts for bottles with single quotes cause failures

Open rptb1 opened this issue 2 years ago • 4 comments

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

  1. Create a shortcut in a bottle with a quote in its name, such as "Baldur's Gate 3".
  2. "Add a Desktop Entry" for the game.
  3. Attempt to launch using the desktop icon.
  4. 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
  5. From the desktop icon (in the menu or widget or wherever) right click and choose "Configure in Bottles".
  6. 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'

rptb1 avatar Dec 20 '23 15:12 rptb1

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

rptb1 avatar Dec 20 '23 15:12 rptb1

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") 

rptb1 avatar Dec 21 '23 10:12 rptb1

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

rptb1 avatar Dec 21 '23 10:12 rptb1

You can just use subprocess.list2cmdline instead of quoting each single argument.

JakobDev avatar Dec 23 '23 11:12 JakobDev