Skip to content

desktop: fix saving files with '[', '*', or '?' in name on Linux#6905

Open
Narasimha-sc wants to merge 2 commits into
simplex-chat:masterfrom
Narasimha-sc:nd/fix-save-file-brackets-linux
Open

desktop: fix saving files with '[', '*', or '?' in name on Linux#6905
Narasimha-sc wants to merge 2 commits into
simplex-chat:masterfrom
Narasimha-sc:nd/fix-save-file-brackets-linux

Conversation

@Narasimha-sc
Copy link
Copy Markdown
Collaborator

@Narasimha-sc Narasimha-sc commented Apr 28, 2026

Summary

On Linux, you cannot save a received file whose name contains [, *, or ? — the Save dialog stays open and nothing is written to disk. Reproduced with [test].txt; common in practice with browser-style downloads like report[1].pdf.

Root cause

javax.swing.plaf.basic.BasicFileChooserUI.ApproveSelectionAction.actionPerformed() does this when the user clicks Save:

  1. reads the typed filename via getFileName()
  2. resolves it via FileSystemView.createFileObject(name)
  3. if the file does not exist AND isGlobPattern(name) is true, it interprets the name as a glob: it calls changeDirectory(parent), sets a GlobFilter on the chooser, and returns — without ever calling approveSelection().

BasicFileChooserUI.isGlobPattern() (verified by reflection on JDK 17.0.18):

separator glob chars
/ (Linux/macOS) *, ?, [
\ (Windows) *, ?

So file[1].pdfisGlobPattern true → glob branch taken → save never happens. On Windows [ is not a glob char per the JDK; on macOS this code base uses java.awt.FileDialog, not JFileChooser, so neither is affected by the [ case. (* and ? are also intercepted as glob in the JFileChooser save dialog on Windows, but they are illegal NTFS filename chars anyway.)

When this regressed

Introduced by 02d0094 / #2789 (2023-07-28, "desktop: fix creating tmpDir and providing name for file to save"). That commit started pre-populating the filename via:

fileChooser.selectedFile = File(filename)

Before then, the save dialog ran in DIRECTORIES_ONLY mode — the user picked a folder and the file was written with its original name; JFileChooser never saw the filename, so the glob branch never ran.

Fix approach

BasicFileChooserUI.getApproveSelectionAction() is a public method that returns the exact ActionListener instance wired onto the Save button. Using that we:

  • locate the Save button by identity (it === listener) — no class-name string match;
  • replace it with a small handler that resolves the typed text as a literal filename, traverses on directory paths, and calls approveSelection() directly — bypassing the glob branch entirely.

The handler is installed only when !isLoad && desktopPlatform.isLinux():

  • Windows save dialog: untouched.
  • macOS save dialog: untouched (uses java.awt.FileDialog, different code path).
  • Open dialog on every platform: untouched (the fix is gated on !isLoad).

Why bypass glob-on-save entirely (instead of only for [)

JFileChooser's glob-on-save behaviour is a Swing-only eccentricity that no native OS save dialog implements:

  • macOS NSSavePanel — no glob-on-save (and this app already uses it on macOS via AWT FileDialog).
  • Native Windows save dialog — no glob-on-save.
  • Native Linux GTK / KDE save dialogs — no glob-on-save.

It also blocks legitimate POSIX filenames containing * or ? from being saved (those are legal on POSIX filesystems even though Windows forbids them).

So the user-facing change on Linux is: the Save button always treats the typed text as a literal filename — matching what users get on macOS in this same app and what they get from every native OS save dialog.

Alternatives considered and rejected

Option Why rejected
Sanitize/escape the filename Destructive and user-visible — rewrites the proposed name.
Use java.awt.FileDialog on Linux for save Existing comment in DefaultDialog.desktop.kt warns of "graphic glitches on many Linux distributions" — that is why JFileChooser was chosen for Linux in the first place.
Reflection on BasicFileChooserUI.getApproveButton(JFileChooser) Method is protected; setAccessible(true) fails under JPMS without --add-opens java.desktop/javax.swing.plaf.basic=ALL-UNNAMED. Verified on JDK 17.
Override JFileChooser.approveSelection() in a subclass The glob branch returns before approveSelection() is called, so an override never sees the click.
Replace the actionMap["approveSelection"] entry The Save button has the action wired as a direct ActionListener, not via the action map — verified by walking the component tree. The action map binding only matters for keyboard shortcuts, which are not on the broken path.
Bypass only when filename contains [ (and delegate for */?) Preserves Swing's glob-on-save for */?, but no native OS save dialog has this feature, and it still blocks saving legitimate POSIX filenames containing * or ?.
Custom FileSystemView that returns a File whose exists() lies Would silently affect every other consumer of the FSV; high blast radius for a narrow workaround.

Test plan

  • Build Linux AppImage; receive [test].txt, click Save → saves correctly.
  • Save filenames containing * and ? on Linux → save correctly.
  • Save normal filename hello.txt → unchanged behaviour.
  • Save into a directory by typing its path in the filename field → traverses into it.
  • Confirm Windows save dialog unaffected (no code path change for Windows).
  • Confirm macOS save dialog unaffected (no code path change for macOS).
  • Confirm open dialog unchanged on every platform.

JFileChooser's BasicFileChooserUI treats filenames containing
'[', '*', or '?' as glob patterns on Unix when the file does not
exist on disk. The Save button's ApproveSelectionAction then
applies a GlobFilter and returns without saving — the dialog
stays open and nothing is written.

Regression introduced in simplex-chat#2789 (commit 02d0094, 2023-07-28),
which started pre-populating the filename via selectedFile =
File(filename). Before that the save dialog ran in
DIRECTORIES_ONLY mode and the JDK glob check never saw the
filename.

Fix: on Linux save dialogs, find the Save button by identity
(BasicFileChooserUI.getApproveSelectionAction() is public and
returns the exact ActionListener wired on the button) and
replace it with a wrapper that delegates to the original
action for non-glob names and calls approveSelection()
directly for glob names.

Windows (per the JDK only '*' and '?' are glob chars there)
and macOS (uses AWT FileDialog) are unaffected. Open dialogs
keep glob filtering for power-user search.
@Narasimha-sc Narasimha-sc marked this pull request as draft April 28, 2026 10:17
Earlier in this branch we added a heuristic to delegate to the
original ApproveSelectionAction for non-'[' names so that typing
'*.pdf' in the save dialog would still glob-filter the listing.
That preserved a Swing-only quirk that no native OS save dialog
implements:
  - macOS uses NSSavePanel (already used in this app via AWT
    FileDialog) — no glob-on-save.
  - Native Windows / Linux GTK / KDE save dialogs — no glob-on-save.
  - JFileChooser on Windows — has it, but '*' and '?' aren't even
    legal NTFS filename chars, so users won't be typing them.

It also blocks legitimate filenames containing '*' or '?' on Linux
(legal on POSIX filesystems) from being saved, since they too get
intercepted as glob.

Replace the Save button's action with a handler that always treats
the typed text as a literal filename. The result: any filename can
be saved on Linux, behaviour aligns with macOS and with every
native OS save dialog. The open dialog is untouched, so '*.pdf'
glob-filtering there still works.
@Narasimha-sc Narasimha-sc marked this pull request as ready for review April 28, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant