Skip to content

feat(ec): manage the core's shared folders from amuleGUI - #530

Merged
got3nks merged 1 commit into
amule-org:masterfrom
got3nks:feat/shared-dirs-over-ec
Jul 20, 2026
Merged

feat(ec): manage the core's shared folders from amuleGUI#530
got3nks merged 1 commit into
amule-org:masterfrom
got3nks:feat/shared-dirs-over-ec

Conversation

@got3nks

@got3nks got3nks commented Jul 20, 2026

Copy link
Copy Markdown

Closes #484.

The Directories preferences panel was inert in amuleGUI. Its tree browses the machine amuleGUI runs on — the wrong filesystem whenever the core is remote — and nothing it produced ever reached the core: the shared-folder lists aren't part of the EC preferences packet, and SaveSharedFolders() is compiled out under CLIENT_GUI. Ticking folders wrote to a list discarded at exit, while the EC_OP_SHAREDFILES_RELOAD sent alongside made the core rescan its own unchanged config — so the UI looked like it had applied the change. That was true on localhost too, not just remote.

Protocol

New EC_OP_GET_SHARED_DIRS / EC_OP_SET_SHARED_DIRS, carrying one EC_TAG_SHAREDDIR per configured root (path as the value, EC_TAG_SHAREDDIR_RECURSIVE marking a recursive one). Only the two intent lists travel; shareddir.dat stays a core-derived union. Negotiated via EC_TAG_CAN_SHAREDDIRS_CONFIG, so a new GUI against an older core leaves the editor disabled rather than pretending an edit will land.

Core

SET validates every path before applying — a remote user can't browse the core's filesystem to check one, so a typo would otherwise become a silently dead share. Valid roots are applied, persisted and rescanned; refused ones come back as EC_TAG_SHAREDDIR_REJECTED with a numeric reason the client renders in the user's own locale, so the core's locale never leaks into the UI. A rejected path doesn't discard the rest of the edit.

The union file is refreshed alongside the intent lists. ReloadSharedFolders() reconciles against shareddir.dat and drops any explicit root missing from it — that's how it honours external edits — so writing only the intent files would let the rescan trim the roots just added and persist the trimmed result.

amuleGUI

The directory tree is replaced with a Shared folder / Recursive list plus an add-row and Remove. The roots live on CPreferencesRem, which outlives the Preferences dialog, so a reply arriving after it closes repaints nothing rather than a freed dialog. The rejection report is deferred through CallAfter: a modal opened from a packet handler re-enters the EC socket and corrupts its receive state (see CAddLinkHandler).

Keeping the monolithic tree honest

Once the roots became externally editable, the monolithic tree could show stale state. TransferToWindow() already re-seeds the tree's backing maps on every open, so the model was fine — but shared marks below the root's immediate children are applied when a node is created, so anything already expanded kept the marks it was built with, and the Preferences dialog is never destroyed (OnClose vetoes).

The tree now records the roots it was painted from and rebuilds only when they've actually moved (NeedsRepaintFor), so an unchanged reopen keeps the user's expansion state and skips the drive re-scan. HasChanged is also now cleared at session end (OK/Cancel/Close) — it was only ever reset in the constructor and Init(), so a single click in the tree latched it for the dialog's lifetime.

Build layout

muuli_wdr.cpp moves from the muleappgui static library into GUI_SOURCES, so each executable compiles it with its own defines. It was previously built once without CLIENT_GUI and linked into both apps, which made every #ifdef CLIENT_GUI in it dead code. Two such branches already existed and had never taken effect — they meant to give amuleGUI plain text fields for the interface selectors, which until now silently rendered as empty combo boxes. Those now behave as intended.

Testing

Manually verified against a remote core: add/remove roots (recursive and not) persist to shareddir-explicit.dat / shareddir-recursive.dat and survive a rescan; invalid paths are reported and the valid ones still apply; the monolithic tree reflects changes made from amuleGUI. Built clean on macOS (arm64) and Ubuntu ARM64; clang-format clean across src/, Tier-1 and Tier-2 clang-tidy clean; po catalogs regenerated.

The Directories preferences panel was inert in amuleGUI. Its tree browses the
machine amuleGUI runs on, which is the wrong filesystem whenever the core is
remote, and nothing it produced ever reached the core: the shared-folder lists
are not part of the EC preferences packet, and SaveSharedFolders() is compiled
out under CLIENT_GUI. Ticking folders wrote to a list that was discarded on
exit, while the EC_OP_SHAREDFILES_RELOAD sent alongside made the core rescan
its own unchanged config — so the UI looked like it had applied the change.

Give the configuration a real transport and the remote GUI a UI that fits it:

- New EC ops EC_OP_GET/SET_SHARED_DIRS carrying one EC_TAG_SHAREDDIR per root
  (path as the value, EC_TAG_SHAREDDIR_RECURSIVE marking a recursive one).
  Only the two intent lists travel; shareddir.dat stays a daemon-derived union.
- The core validates each path on SET — a remote user cannot browse the core's
  filesystem to check a path, so a typo would otherwise become a silently dead
  share. Valid paths are applied, persisted and rescanned; refused ones come
  back as EC_TAG_SHAREDDIR_REJECTED with a numeric reason the client renders in
  the user's own locale.
- Negotiated via EC_TAG_CAN_SHAREDDIRS_CONFIG, so a new GUI against an older
  core leaves the editor disabled instead of pretending an edit will land.
- amuleGUI replaces the directory tree with a Path/Recursive list plus an
  add-row and Remove. The roots live on CPreferencesRem, which outlives the
  Preferences dialog, so a reply arriving after it closes repaints nothing
  rather than a freed dialog. The rejection report is deferred via CallAfter:
  a modal opened from a packet handler re-enters the EC socket (CAddLinkHandler).

muuli_wdr.cpp moves from the muleappgui static library into GUI_SOURCES so each
executable compiles it with its own defines. It was built once without
CLIENT_GUI and linked into both apps, which made every #ifdef CLIENT_GUI in it
dead code — including the two that meant to give amuleGUI plain text fields for
the interface selectors, which until now silently rendered as empty combo boxes.
@got3nks
got3nks merged commit ea20f86 into amule-org:master Jul 20, 2026
14 checks passed
@got3nks
got3nks deleted the feat/shared-dirs-over-ec branch July 20, 2026 12:48
got3nks added a commit that referenced this pull request Jul 20, 2026
The Flatpak job regenerates ECCodes.h from ECCodes.abstract rather than using the committed header, and #530's comments in that file made the generated header unparseable ("invalid preprocessing directive #=" plus a truncated ECOpCodes enum), failing every Flatpak build on master.

The generator only treats a line as a comment when it matches "^#" with no leading whitespace, so the comments indented to line up with the tag entries fell through to the data path and had every word turned into a field. It also flattens the file into a CMake list by replacing newlines with ";", so a semicolon inside a comment splits the line and the remainder loses its leading "#".

Documentation for these codes lives in the hand-maintained header, so the abstract keeps to entries. Generating before and after: master produces 8 mangled lines and 11 compile errors, this produces none, with all seven new codes intact.
got3nks added a commit that referenced this pull request Jul 20, 2026
Follow-up to #530, which made the core's share roots reachable over EC. amuleapi could list the files a share produced and ask the core to re-walk its roots, but had no way to see or change which roots those were.

Adds /shared/directories: GET returns the configured roots as {path, recursive} (GUEST, matching GET /shared and GET /preferences); PUT replaces the whole set, mirroring the core operation rather than hiding a read-modify-write; POST adds a single root and DELETE removes one, since the scripted case is a single folder. POST is idempotent so "ensure this is shared" repeats safely; DELETE 404s on an unconfigured path so a typo is visible.

The core validates each path — a REST client cannot stat the core filesystem — applying the ones that pass and returning the rest in `rejected` with a reason, so one bad path never discards the edit. Reasons arrive as codes and are rendered by the API, keeping the core locale out of responses.

POST and DELETE hold a mutex across their read and write, since SendRecvSerialized locks per roundtrip and two concurrent adds would otherwise lose one. Covered by a new curl smoke that snapshots and restores the operator configuration, 30/30 passing live.
got3nks added a commit that referenced this pull request Jul 21, 2026
…ale daemons (#542)

A headless amuled started without LANG / LC_* (systemd, Docker) runs under
the POSIX/C locale, whose ASCII codeset cannot represent accented or other
non-ASCII UTF-8 filesystem paths. wxConvFileName then fails to open them, so
an accented shared directory silently becomes invisible to the file scan —
reproduced on glibc: `LC_ALL=C` finds 0 files in an accented share where a
UTF-8 locale finds them. This newly bites the remote shared-folder config
(#530), whose whole point is configuring a headless core, but it affects all
of amuled's non-ASCII file handling.

aMuleInitLocale() now promotes LC_CTYPE to UTF-8 (C.UTF-8, falling back to
en_US.UTF-8) when the resolved codeset is bare ASCII, and exports it so the
promotion survives wx re-resolving the locale from the environment during
app init — a plain setlocale() alone is undone there. The guard is the
codeset itself, so a deliberate UTF-8 or latin1 locale is never overridden,
and musl (already UTF-8 in its C locale) never triggers it. Windows is exempt
(wide-char filesystem APIs).

Verified on an ARM64 glibc VM: with the fix an accented shared folder is
found under a bare environment, an explicit `LC_ALL=C`, and healthy UTF-8
locales alike, with no change for a real locale (it_IT.UTF-8).
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.

Remote Control: "Shared directories" context menu does not work

1 participant