pwsafe/transport — Implementation Plan

See: transport design | ident & dispatch | file: plugin | webdav design

Phase 0 — Verify Before Writing Code

wxFilePickerCtrl verdict: cannot cleanly accept URLs. DbSelectionPanel::DoValidation() calls wxFileName(path).FileExists() which is a direct OS call, bypassing our pws_os::FileExists() intercept. Resolution: replace path entry with a plain wxTextCtrl + recent-locations dropdown + a "Browse…" button (Browse opens wxFilePickerCtrl for local files only). See Phase 3. • Scope: Linux first, Mac next, Windows later. Mac needs file.cpp is a separate file; .so.dll and /proc/self/exeGetModuleFileName() differ. Add Windows support later. • Cache directory: use /home/john/.cache/pwsafe/ (not ~/.pwsafe/cache/). Create on first use. • WebDAV plugin registers for webdav, http, and https. Bare https://server/file.psafe3 routes to WebDAV plugin without needing the explicit webdav: prefix.

Phase 1 — Transport Interface & Dispatch Skeleton

No plugins yet. URLs → "no plugin found" error. Goal: infrastructure compiles and intercepts correctly.

New: src/os/transport.hPWSTransport struct (see design) • PWSTransport_ABI_VERSION constant • pws_register_transport(), pws_find_transport() declarations • pws_transport_load_for_scheme(scheme) — lazy loader

New: src/os/transport.cpp • Transport registry (std::map<std::string, PWSTransport*>) • Lazy loader: construct pwsafe-<scheme>.so filename, find in app dir (+ cwd if DEVELOPMENT), pre-scan identity string, dlopen, call init, dlclose on failure • Cache path resolver: ~/.pwsafe/cache/ + sha256 of URL (create dir if absent) • std::map<FILE*, CacheInfo> bridging FOpenFClose • Plugin unload: pws_transport_unload(scheme) — called on database close

Modify: src/os/unix/file.cppFileExists: if URL → call transport exists()FOpen: if URL → call transport fetch() to cache, open cache file, register in map • FClose: if fd in map and write mode → call transport store() from cache, remove from map • LockFile / UnlockFile: if URL → call transport lock()/unlock() • URL detection helper: static bool is_url(const stringT&) — checks for scheme: prefix

Modify: CMakeLists.txt — add transport.cpp to core build

Phase 2 — file: Plugin

Goal: full round-trip working against a local file via the plugin stack. Validates the entire infrastructure before any network code.

New: src/os/plugins/file/transport-file.cpp • Implements all five PWSTransport functions (fetch/store = file copy, exists = stat, lock/unlock = no-op) • Embeds identity string: PWS_TRANSPORT_INFO:1:file:Local file transport (testing)pws_plugin_init registers for scheme file

Modify: CMakeLists.txt — add MODULE target → pwsafe-file.so

Test: pwsafe file:///tmp/test.psafe3 — open, modify, save, reopen, verify contents

Phase 3 — UI: Accept URLs in Open Dialog

Goal: user can type or paste a local path or URL into the open dialog. wxFilePickerCtrl is replaced for the path entry field.

• Replace wxFilePickerCtrl in DbSelectionPanel with: a wxComboBox (text entry + recent-locations dropdown) alongside a "Browse…" button • Browse button opens a wxFilePickerCtrl dialog for local files only; selected path is written back into the combo box • Recent locations list: persist to prefs (capped, e.g. 10 entries); includes both local paths and URLs • Update DoValidation(): detect URL first; if URL skip wxFileName check entirely; validate via pws_os::FileExists() (which we intercept) • Error dialog: "No plugin found for scheme 'X'. Install pwsafe-X.so alongside the application." • Windows UI: defer

Phase 4 — WebDAV Plugin

Goal: open and save a real .psafe3 file on a WebDAV server.

New: src/os/plugins/webdav/transport-webdav.cpp • fetch → libcurl GET; store → libcurl PUT; exists → HEAD • On first use: OPTIONS to detect LOCK support; cache result per host • lock → LOCK (if supported); unlock → UNLOCK • CURLOPT_NETRC = CURL_NETRC_OPTIONAL (credentials from ~/.netrc) • Strips webdav: prefix before passing URL to libcurl • Identity string: PWS_TRANSPORT_INFO:1:webdav:WebDAV transport via libcurl

Modify: CMakeLists.txt — MODULE target → pwsafe-webdav.so, link libcurl • find_package(CURL REQUIRED) inside the plugin target only — main binary stays libcurl-free

Test: Local WebDAV server (e.g. nginx + mod_dav) → open, modify, save, verify on server. Then test with LOCK-capable server (e.g. Apache mod_dav).

Open Questions — Resolved

All four questions from the original plan are resolved above in Phase 0 and Phase 3. No open questions remain.

version3
created2026-02-24
updated2026-02-24