* add the base crate and repoint the moved modules at it
`libs/base` (crate `base`) takes the parts of hbb_common that only this app
uses: `fs`, `platform`, `keyboard`, `message.proto`, and 145 of the 177
`config::keys` constants. hbb_common keeps what the server names, and the 32
keys it reads itself are re-exported from `base::config::keys` so call sites
still see the full set through one path.
Sources move verbatim. The only edits inside them are `crate::` prefixes that
now have to say `hbb_common::`; `keyboard.rs` and `platform/windows.rs` are
byte-identical. The crate stays on edition 2018, the edition the moved code was
written under. `log`, `lazy_static` and `anyhow` become direct dependencies so
the bare paths in that code resolve exactly as before, and its winapi features
are spelled out rather than left to feature unification.
Two call sites outside Rust and Cargo had to follow the move: the Android
protobuf source dir, which still pointed at hbb_common/protos for message.proto,
and the three AGENTS.md entries that named hbb_common for options, protos and
file transfer.
`scrap`'s `drm` feature now forwards to `base/wayland_probe`. Left pointing at
hbb_common it would still have compiled, silently dropping the Wayland
socket-probe fallback, so that forward is verified by a build with and without
the feature.
`config::keys` carries a test asserting its names stay disjoint from the ones
hbb_common kept: the glob re-export and the local constants share a namespace,
and Rust prefers the local item silently, so a name added to both sides would
otherwise let client and server disagree with no diagnostic.
Verified: macOS and Linux, debug and release, `--all-targets`; the 177 key
constants diffed name-for-name and value-for-value; the generated protobuf types
compared before and after; every `#[cfg]` gate on a moved import checked against
its original; and every file that was `rustfmt`-clean before this change still
is, compared against master file by file. Windows is checked by inspection only
-- it cannot be compiled here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
* one `use` per crate, and write the rule down
`fs.rs` came out of the move with two ungated `use hbb_common::` statements,
because the original single `use crate::{...}` had to give up `message_proto`
to the new crate and the rest was left in a second block. Fold it back into one.
A scan of the whole tree for the same shape finds nothing else: every other file
with more than one top-level `use base::` or `use hbb_common::` is split by a
`#[cfg]` that does not cover the whole block, or by `pub use` next to `use`.
Those are the cases that cannot merge, so AGENTS.md now states both the rule and
the exemption.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix: android: replace all-files access with scoped storage + system picker
Remove MANAGE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, and
WRITE_EXTERNAL_STORAGE from the Android manifest. Remove
requestLegacyExternalStorage. Replace broad external storage with
app-scoped external storage for the file-transfer workspace.
File import uses the system file_picker. File export uses Android's
SAF ACTION_CREATE_DOCUMENT with path validation that restricts
export sources to app-owned directories.
Remove the external_path dependency.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android: refine file import feedback
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android: use SAF for file imports
Replace file_picker imports with Android's Storage Access Framework to avoid legacy storage permissions, stale cached files, and duplicate staging of large imports. Stream selected documents into app-scoped storage with failure-safe replacement, keep exports restricted to validated app storage roots, use filesDir for the internal fallback workspace, and remove legacy permissions contributed during manifest merging.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android: keep file imports in the selected directory
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android: reset projection and constrain file workspace
Release capture resources when media projection is revoked externally. Keep Android local file navigation within the app-scoped workspace.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android: handle scoped storage start-up regressions. Allow zero digits in POSIX filenames by rejecting NUL explicitly, and initialise the app-specific home directory before the Android service starts the native server.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: update content resolver mode to use 'wt' instead of 'w' to prevent trailing bytes from old document whilst reporting sucess
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android, enforce file workspace boundary on the server, and unblock the ui thread.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: android: validate rename destinations against the app workspace bound file-operation paths. report rename failures, general import failures, and unregister / reregister projection when its onStop callback fires.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: reconnect was refreshing the directory with net entry instances, while selected items retained the old instances, it was reporting a selected item, but checkbox statue used object identity, and appeared unchecked. Fixed by reconciling by path and entry type before replacing the directory snapshot, rebinding valid selections, and dropping missing ones.
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix: (android) add SAF folder import and multi item export - import directories using ACTION_OPEN_DOCUMENT_TREE. Export multiple files, logs, and screen recordings via export buttons, add localisation keys for new actions
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
* fix(android): harden scoped storage file handling
- create new SAF documents instead of overwriting export sources
- reject empty peer paths except for home directory reads
- report directory backup restore and cleanup failures
- resolve log export paths from the configured app name
Signed-off-by: fufesou <linlong1266@gmail.com>
* fix(android): harden scoped-storage file operations
- snapshot directory exports before writing to the destination
- query document provider metadata off the main thread
- reject invalid remote directories without read timeouts
Signed-off-by: fufesou <linlong1266@gmail.com>
* fix(android): handle SAF directory name collisions
- reject dot-segment folder names during import
- fail imports with duplicate document display names
- only reuse matching directories during export
Signed-off-by: fufesou <linlong1266@gmail.com>
* fix(android): handle SAF folder import collisions
Reject filesystem-equivalent destination names and
avoid showing a failure when folder overwrite is skipped.
Signed-off-by: fufesou <linlong1266@gmail.com>
---------
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
* fix(linux): a session logout should hand the peer to the login screen
Logging out closes every window in the session, the connection manager's
included, and its close handler kicks every peer with the reason a person
gets when they disconnect one by hand. That reason is the one thing the
client never retries on, so the remote session dies on a frozen frame
instead of reconnecting to the greeter that is already there.
The close carries nothing to tell the two apart: measured on KDE, the CM
receives no signal and logind still reports the session active at that
instant, and the server is killed within a few hundred ms either way, so
neither a state check nor a grace period can decide it. What is
distinguishable is the ACTION: disconnecting a peer is not the same event
as this window going away. So the window-close path now says so, and the
server ends the session without poisoning the retry; the Disconnect
button and the app's own close control keep kicking exactly as before.
Linux only, since that is where a logout closes the window.
Verified on plasma/sddm with a client attached: a logout now reconnects
to the greeter with no dialog, while closing the manager window still
shows Closed manually by the peer.
* fix(linux): close the tunnel too, and keep the web build compiling
Three seams the first pass missed. The web bridge is hand written, not
generated, so the new call needs its stub there or flutter build web
stops compiling - and that job is disabled in CI, so it would have gone
green. try_port_forward_loop is a second consumer of the same channel
and only knew Close, so a forwarded tunnel outlived the window it was
supposed to die with. And the variant had landed inside the DRM section,
whose comment says everything below it is drm-gated.
quit_gui() ends the process on Windows (std::process::exit) and macOS
(NSApp terminate), but on Linux it calls gtk_main_quit(), which has no
effect in the Flutter connection manager: flutter/linux/main.cc runs
g_application_run() (GtkApplication), so gtk_main() is never called and
the assertion inside gtk_main_quit() just fails.
quit_cm() is the only caller that relies on quit_gui() to end the
process. The main window path in ipc.rs calls std::process::exit(-1)
right after it, and the two remaining call sites are in the Sciter UI,
which is not compiled for flutter builds. So a connection manager
reaching quit_cm() on Linux kept running while no longer serving the
`_cm` ipc endpoint, which also stops the server from reusing it, so the
next connection spawns one more.
NOTE: this is a fallback, not an explanation for the stale processes of
#15698: a client merely disconnecting does not reach quit_cm(), the
Flutter side closes the window instead.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* avatar
* refactor avatar display: unify rendering and resolve at use time
- Extract buildAvatarWidget() in common.dart to share avatar rendering
logic across desktop settings, desktop CM and mobile CM
- Add resolve_avatar_url() in Rust, exposed via FFI (SyncReturn),
to resolve relative avatar paths (e.g. "/avatar/xxx") to absolute URLs
- Store avatar as-is in local config, only resolve when displaying
(settings page) or sending (LoginRequest)
- Resolve avatar in LoginRequest before sending to remote peer
- Add error handling for network image load failures
- Guard against empty client.name[0] crash
- Show avatar in mobile settings page account tile
Signed-off-by: 21pages <sunboeasy@gmail.com>
* web: implement mainResolveAvatarUrl via js getByName
Signed-off-by: 21pages <sunboeasy@gmail.com>
* increase ipc Data enum size limit to 120 bytes
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
- Route Windows server-to-client file reads through CM instead of the connection layer
- Add FS IPC commands (ReadFile, CancelRead, SendConfirmForRead, ReadAllFiles) and CM data messages
(ReadJobInitResult, FileBlockFromCM, FileReadDone, FileReadError, FileDigestFromCM, AllFilesResult)
- Track pending read validations and read jobs to coordinate CM-driven file transfers and clean them up
on completion, cancellation, and errors
- Enforce a configurable file-transfer-max-files limit for ReadAllFiles and add stronger file name/path
validation on the CM side
- Improve Flutter file transfer UX and robustness:
- Use explicit percent/percentText progress fields
- Derive speed and cancel actions from the active job
- Handle job errors via FileModel.handleJobError and complete pending recursive tasks on failure
- Wrap recursive directory operations in try/catch and await sendRemoveEmptyDir when removing empty directories
Signed-off-by: fufesou <linlong1266@gmail.com>
1. Send raw contents if `content_len` > 1024*3.
2. Send raw contents if it is not empty.
3. Try read clipboard again if no data from cm.
Signed-off-by: fufesou <linlong1266@gmail.com>