saltcorn-samba 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/index.js +34 -47
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,58 @@ All notable changes to `saltcorn-samba` are documented here.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.4.8] – 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Fixed – **Basispfad wurde beim Verbindungstest doppelt vorangestellt (die eigentliche Ursache)**
|
|
10
|
+
|
|
11
|
+
In allen Vorgängerversionen (0.4.4 – 0.4.7) hat der Verbindungstest den
|
|
12
|
+
konfigurierten Basispfad **zweimal** auf den Server geschickt und deshalb
|
|
13
|
+
systematisch die Meldung *„Der Basispfad ist auf dem Server nicht auffindbar"*
|
|
14
|
+
produziert — auch dann, wenn der Pfad in Wirklichkeit existierte und mit
|
|
15
|
+
`smbclient` oder dem Windows-Explorer problemlos erreichbar war.
|
|
16
|
+
|
|
17
|
+
**Ursache:** `buildClient(config)` speichert `config.base_path` intern als
|
|
18
|
+
`client.basePath`. Der Wrapper-`resolvePath(rel)` baut daraus
|
|
19
|
+
`<share>/<basePath>/<rel>`. Die Test-Route rief aber
|
|
20
|
+
`client.readdir(testCfg.base_path)` auf und übergab den Basispfad **noch
|
|
21
|
+
einmal** als relatives Argument. Ergebnis: aus `base_path = "static"` wurde
|
|
22
|
+
auf der Leitung `buero/static/static` — den es natürlich nicht gibt. Die
|
|
23
|
+
Diagnose-Ausgabe (`tested_path: static/static`) zeigte den Bug bereits
|
|
24
|
+
klar an, wurde aber bisher als Symptom statt Ursache gelesen.
|
|
25
|
+
|
|
26
|
+
**Fix:** Die Test-Route ruft jetzt `client.readdir("")` auf. Der Wrapper
|
|
27
|
+
resolvet das intern korrekt zu `<share>/<basePath>` — also genau dem Pfad,
|
|
28
|
+
den der Benutzer im Formular eingetragen hat. Für Anzeige-Zwecke (Meldungen,
|
|
29
|
+
Diagnose-Kacheln, Fallback-Probes) wird der Basispfad separat in
|
|
30
|
+
`baseForDisplay` gehalten.
|
|
31
|
+
|
|
32
|
+
### Added – **Standalone-Diagnose-Skript `tools/diag-basepath.js`**
|
|
33
|
+
|
|
34
|
+
Wer den Verbindungsproblemen auf den Grund gehen will, kann jetzt außerhalb
|
|
35
|
+
von Saltcorn probieren, was smb3-client tatsächlich sieht:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node tools/diag-basepath.js \
|
|
39
|
+
--host 192.168.110.10 --share buero --path static \
|
|
40
|
+
--user 01_vassen --domain buero.ib-vassen.de --password ...
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Das Skript führt sechs Probes durch (Share-Root, Share-Root mit Slash,
|
|
44
|
+
`stat` auf Ziel, `readdir` auf Ziel, Groß-/Kleinschreibvariante) und
|
|
45
|
+
druckt für jede den NT-Status-Code — hilfreich beim Aufspüren von
|
|
46
|
+
Schreibvarianten oder ACL-Problemen.
|
|
47
|
+
|
|
48
|
+
## [0.4.7] – 2026-07-05
|
|
49
|
+
|
|
50
|
+
### Fixed – **Irreführende Windows-UNC-Anzeige in der Fehlermeldung**
|
|
51
|
+
|
|
52
|
+
Die v0.4.6-Fehlermeldung zeigte den getesteten Pfad als Windows-UNC
|
|
53
|
+
(`\\\\192.168.110.10\\buero\\static`). Das war irreführend, weil das
|
|
54
|
+
Plugin an smb3-client tatsächlich Forward-Slash-Pfade schickt
|
|
55
|
+
(`buero/static`) — die Backslash-Anzeige suggerierte einen Bug, der
|
|
56
|
+
keiner ist. Jetzt wird der Pfad in genau der Form gezeigt, die auch
|
|
57
|
+
tatsächlich über die Leitung geht: `<share>/<basispfad>`.
|
|
58
|
+
|
|
7
59
|
## [0.4.6] – 2026-07-05
|
|
8
60
|
|
|
9
61
|
### Fixed – **Schreibvarianten-Test verdoppelte den Basispfad**
|
package/index.js
CHANGED
|
@@ -853,72 +853,62 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
853
853
|
// to stat("") — that already proves the whole handshake
|
|
854
854
|
// (TCP + Negotiate + Session + TREE_CONNECT + Auth) works, which
|
|
855
855
|
// is all the connection test actually promises.
|
|
856
|
-
|
|
857
|
-
//
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
//
|
|
861
|
-
//
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
// for directories with strict ACLs. readdir() sends
|
|
865
|
-
// createOptions=1 (DIRECTORY_FILE) and is the right primitive
|
|
866
|
-
// for a base_path check anyway — we want to know the folder
|
|
867
|
-
// can actually be listed, not just opened.
|
|
856
|
+
//
|
|
857
|
+
// IMPORTANT: buildClient already stores `base_path` as the
|
|
858
|
+
// wrapper's `basePath`, and wrapper.readdir(x) resolves to
|
|
859
|
+
// `share/basePath/x`. So we call readdir("") to list the
|
|
860
|
+
// configured basePath itself — not readdir(base_path), which
|
|
861
|
+
// would append it twice (bug fixed in 0.4.8).
|
|
862
|
+
const rel = "";
|
|
863
|
+
const baseForDisplay = testCfg.base_path ? sanitizeRelativePath(testCfg.base_path) : "";
|
|
868
864
|
try {
|
|
869
865
|
return await client.readdir(rel);
|
|
870
866
|
} catch (err) {
|
|
871
867
|
// Look at both the wrapper error and its underlying cause.
|
|
872
868
|
const causeMsg = String((err && err.cause && err.cause.message) || "");
|
|
873
869
|
const msg = String((err && err.message) || err || "") + " " + causeMsg;
|
|
874
|
-
|
|
870
|
+
// The path we effectively asked the server to enumerate is
|
|
871
|
+
// `share/basePath` (rel is always "" here — basePath is the
|
|
872
|
+
// subdirectory to test).
|
|
873
|
+
const effectivePath = testCfg.share + (baseForDisplay ? "/" + baseForDisplay : "");
|
|
874
|
+
const isRootProbe = !baseForDisplay;
|
|
875
875
|
const isRootEnumBug = /0xC0000033|OBJECT_NAME_INVALID|Share-Root/i.test(msg);
|
|
876
876
|
if (isRootProbe && isRootEnumBug) {
|
|
877
|
-
//
|
|
878
|
-
//
|
|
879
|
-
//
|
|
877
|
+
// Share-root enumeration is a known smb3-client-vs-Samba
|
|
878
|
+
// limitation. Fall back to stat("") which proves the whole
|
|
879
|
+
// handshake works even if we can't list the root.
|
|
880
880
|
try {
|
|
881
881
|
await client.stat("");
|
|
882
|
-
// Signal to the caller that the connection works, but
|
|
883
|
-
// the share root cannot be enumerated on this server.
|
|
884
882
|
const marker = [];
|
|
885
883
|
marker._rootNotEnumerable = true;
|
|
886
884
|
return marker;
|
|
887
885
|
} catch (statErr) {
|
|
888
|
-
// stat also failed — surface the original error.
|
|
889
886
|
throw err;
|
|
890
887
|
}
|
|
891
888
|
}
|
|
892
|
-
//
|
|
893
|
-
//
|
|
894
|
-
//
|
|
895
|
-
// (if not the share root) and probe common alternative
|
|
896
|
-
// spellings of the missing segment so we can distinguish
|
|
897
|
-
// typo / case-mismatch / permission problems.
|
|
889
|
+
// NAME_NOT_FOUND / PATH_NOT_FOUND on a configured base_path.
|
|
890
|
+
// Gather diagnostics: raw probes against alternate spellings
|
|
891
|
+
// and (if possible) a listing of the parent directory.
|
|
898
892
|
const isMissing = /OBJECT_NAME_NOT_FOUND|OBJECT_PATH_NOT_FOUND|ENOENT|STATUS_NO_SUCH_FILE|existiert.*nicht/i.test(msg);
|
|
899
|
-
if (
|
|
900
|
-
const parts =
|
|
893
|
+
if (baseForDisplay && isMissing) {
|
|
894
|
+
const parts = baseForDisplay.split("/").filter(Boolean);
|
|
901
895
|
const missing = parts[parts.length - 1];
|
|
902
896
|
const parent = parts.slice(0, -1).join("/");
|
|
903
897
|
const parentAbs = parent || "(Share-Root)";
|
|
904
|
-
//
|
|
905
|
-
//
|
|
906
|
-
//
|
|
907
|
-
// (e.g. readdir("static") → "buero/static/static").
|
|
898
|
+
// Probe via the raw smb3-client so we bypass the wrapper's
|
|
899
|
+
// basePath prefix and can test siblings/alternate spellings
|
|
900
|
+
// directly from the share root.
|
|
908
901
|
const raw = client._raw;
|
|
909
902
|
const rawShare = client.shareName;
|
|
910
903
|
async function rawReaddir(relFromShareRoot) {
|
|
911
|
-
const full =
|
|
904
|
+
const full = relFromShareRoot ? rawShare + "/" + relFromShareRoot : rawShare;
|
|
912
905
|
return await raw.readdir(full, { withFileTypes: true });
|
|
913
906
|
}
|
|
914
907
|
let siblings = null;
|
|
915
908
|
let parent_error = null;
|
|
916
909
|
if (parent) {
|
|
917
910
|
try {
|
|
918
|
-
|
|
919
|
-
// absolute-from-share-root paths in the raw call.
|
|
920
|
-
const parentFromShare = [client.basePath, parent].filter(Boolean).join("/");
|
|
921
|
-
const listing = await rawReaddir(parentFromShare);
|
|
911
|
+
const listing = await rawReaddir(parent);
|
|
922
912
|
siblings = Array.isArray(listing)
|
|
923
913
|
? listing.map((d) => ({
|
|
924
914
|
name: d && d.name,
|
|
@@ -929,9 +919,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
929
919
|
parent_error = String((parentErr && parentErr.message) || parentErr || "");
|
|
930
920
|
}
|
|
931
921
|
}
|
|
932
|
-
// Probe alternate spellings
|
|
933
|
-
// This lets us report "Ordner existiert unter anderem Namen" if
|
|
934
|
-
// Samba is running with case sensitive = yes / case-preserved.
|
|
922
|
+
// Probe alternate spellings of the missing segment.
|
|
935
923
|
const probes = [];
|
|
936
924
|
const seen = new Set();
|
|
937
925
|
const addProbe = (name) => {
|
|
@@ -945,9 +933,9 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
945
933
|
addProbe(missing.charAt(0).toUpperCase() + missing.slice(1).toLowerCase());
|
|
946
934
|
const probe_results = [];
|
|
947
935
|
for (const p of probes) {
|
|
948
|
-
//
|
|
949
|
-
//
|
|
950
|
-
const relFromShare = [
|
|
936
|
+
// From-share-root path: `parent/candidate`. NEVER include
|
|
937
|
+
// client.basePath here — the raw client is share-relative.
|
|
938
|
+
const relFromShare = [parent, p].filter(Boolean).join("/");
|
|
951
939
|
let ok = false;
|
|
952
940
|
let perr = null;
|
|
953
941
|
try {
|
|
@@ -961,7 +949,6 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
961
949
|
const workingAlt = probe_results.find(
|
|
962
950
|
(r) => r.ok && r.candidate !== missing
|
|
963
951
|
);
|
|
964
|
-
const testedPath = "\\\\" + testCfg.server + "\\" + testCfg.share + "\\" + rel.replace(/\//g, "\\");
|
|
965
952
|
let hintText;
|
|
966
953
|
if (workingAlt) {
|
|
967
954
|
hintText =
|
|
@@ -972,11 +959,11 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
972
959
|
"case-sensitive (\u201ecase sensitive = yes\u201c in smb.conf).";
|
|
973
960
|
} else {
|
|
974
961
|
hintText =
|
|
975
|
-
"Der Basispfad \u201e" +
|
|
962
|
+
"Der Basispfad \u201e" + baseForDisplay + "\u201c wurde auf der " +
|
|
976
963
|
"Freigabe \u201e" + testCfg.share + "\u201c nicht " +
|
|
977
|
-
"gefunden.
|
|
978
|
-
"M\u00f6gliche Ursachen: (a) Ordner existiert unter
|
|
979
|
-
"diesem Namen nicht in dieser Freigabe (Basispfad ist " +
|
|
964
|
+
"gefunden. Intern getestet wurde: \u201e" + effectivePath +
|
|
965
|
+
"\u201c. M\u00f6gliche Ursachen: (a) Ordner existiert unter " +
|
|
966
|
+
"genau diesem Namen nicht in dieser Freigabe (Basispfad ist " +
|
|
980
967
|
"relativ zur Freigabe \u2014 also z.\u202fB. \u201eunterordner\u201c, " +
|
|
981
968
|
"nicht \u201e" + testCfg.share + "/unterordner\u201c); " +
|
|
982
969
|
"(b) der angemeldete Benutzer \u201e" +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saltcorn-samba",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "Saltcorn plugin: browse, upload, rename and delete files on a Samba/CIFS share via SMB 3.1.1 (AES-CMAC signing, optional encryption). File-manager view, directory tree, inline PDF viewer, external-app open (smb://).",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|