feat(ec): manage the core's shared folders from amuleGUI - #530
Merged
Conversation
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.
This was referenced Jul 20, 2026
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.
This was referenced Jul 20, 2026
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 underCLIENT_GUI. Ticking folders wrote to a list discarded at exit, while theEC_OP_SHAREDFILES_RELOADsent 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 oneEC_TAG_SHAREDDIRper configured root (path as the value,EC_TAG_SHAREDDIR_RECURSIVEmarking a recursive one). Only the two intent lists travel;shareddir.datstays a core-derived union. Negotiated viaEC_TAG_CAN_SHAREDDIRS_CONFIG, so a new GUI against an older core leaves the editor disabled rather than pretending an edit will land.Core
SETvalidates 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 asEC_TAG_SHAREDDIR_REJECTEDwith 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 againstshareddir.datand 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 throughCallAfter: a modal opened from a packet handler re-enters the EC socket and corrupts its receive state (seeCAddLinkHandler).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 (OnClosevetoes).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.HasChangedis also now cleared at session end (OK/Cancel/Close) — it was only ever reset in the constructor andInit(), so a single click in the tree latched it for the dialog's lifetime.Build layout
muuli_wdr.cppmoves from themuleappguistatic library intoGUI_SOURCES, so each executable compiles it with its own defines. It was previously built once withoutCLIENT_GUIand linked into both apps, which made every#ifdef CLIENT_GUIin 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.datand 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 acrosssrc/, Tier-1 and Tier-2 clang-tidy clean; po catalogs regenerated.