saltcorn-samba 0.4.5 → 0.4.6

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/index.js +46 -19
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,40 @@ 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.6] – 2026-07-05
8
+
9
+ ### Fixed – **Schreibvarianten-Test verdoppelte den Basispfad**
10
+
11
+ Die Case-Probes in v0.4.4/0.4.5 riefen `client.readdir(<candidate>)` auf.
12
+ Der Wrapper prependiert aber automatisch den bereits konfigurierten
13
+ `basePath` — dadurch wurde intern nach `buero/static/static` statt
14
+ `buero/static` gesucht. Alle Probes scheiterten deshalb systematisch
15
+ mit derselben Meldung, unabhängig von der wahren Ursache.
16
+
17
+ **Fix:** Die Probes gehen jetzt direkt gegen den rohen `smb3-client`
18
+ (`client._raw.readdir(share/<absoluter Pfad>)`) und bauen die
19
+ From-Share-Root-Pfade selbst zusammen. Der Test-Endpunkt zeigt jetzt
20
+ zusätzlich pro Probe den tatsächlich getesteten Pfad an.
21
+
22
+ ### Fixed – **Falscher „Login rejected“-Hinweis bei BASE_PATH_NOT_FOUND**
23
+
24
+ Der Fallback-Hint-Table prüfte per Substring-Match auf `access_denied`
25
+ u.\u00e4. — das triggerte auf den erklärenden Text in unserer eigenen
26
+ deutschen Fehlermeldung (wir *erwähnen* `ACCESS_DENIED` didaktisch).
27
+ Ein ungefundener Basispfad wurde deshalb zusätzlich mit dem irreführenden
28
+ Hinweis „Login rejected“ versehen.
29
+
30
+ **Fix:** Fehler mit eigenem `code` (`BASE_PATH_NOT_FOUND`,
31
+ `BASE_PATH_NOT_A_DIR`) überspringen den Substring-Match — die
32
+ Wrapper-Meldung ist bereits vollständig und braucht keinen „Hint“.
33
+
34
+ ### Changed – **Bessere Basispfad-Meldung**
35
+
36
+ Wenn kein Case-Match gefunden wurde, zeigt die Meldung jetzt den
37
+ tatsächlich getesteten UNC-Pfad (`\\\\server\\share\\pfad`) und weist
38
+ explizit darauf hin, dass der Basispfad **relativ zur Freigabe** ist —
39
+ also `unterordner` und **nicht** `sharename/unterordner`.
40
+
7
41
  ## [0.4.5] – 2026-07-05
8
42
 
9
43
  ### Fixed – **Test-Verbindung-Button reagiert nicht mehr (v0.4.4-Regression)**
package/index.js CHANGED
@@ -901,15 +901,28 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
901
901
  const missing = parts[parts.length - 1];
902
902
  const parent = parts.slice(0, -1).join("/");
903
903
  const parentAbs = parent || "(Share-Root)";
904
+ // We must probe via the *raw* smb3-client because the
905
+ // wrapper's readdir() automatically prepends basePath —
906
+ // that would double up the segment we're trying to test
907
+ // (e.g. readdir("static") → "buero/static/static").
908
+ const raw = client._raw;
909
+ const rawShare = client.shareName;
910
+ async function rawReaddir(relFromShareRoot) {
911
+ const full = [rawShare, relFromShareRoot].filter(Boolean).join("/");
912
+ return await raw.readdir(full, { withFileTypes: true });
913
+ }
904
914
  let siblings = null;
905
915
  let parent_error = null;
906
916
  if (parent) {
907
917
  try {
908
- const listing = await client.readdir(parent);
918
+ // parent here is relative to basePath — but we want
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);
909
922
  siblings = Array.isArray(listing)
910
923
  ? listing.map((d) => ({
911
- name: d && (d.name || d),
912
- isDirectory: !!(d && (d.isDirectory === true || (typeof d.isDirectory === "function" && d.isDirectory()))),
924
+ name: d && d.name,
925
+ isDirectory: !!(d && typeof d.isDirectory === "function" && d.isDirectory()),
913
926
  }))
914
927
  : null;
915
928
  } catch (parentErr) {
@@ -932,42 +945,44 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
932
945
  addProbe(missing.charAt(0).toUpperCase() + missing.slice(1).toLowerCase());
933
946
  const probe_results = [];
934
947
  for (const p of probes) {
935
- const candidate = parent ? parent + "/" + p : p;
948
+ // Build the *from-share-root* relative path so the probe
949
+ // does not accidentally get basePath prepended twice.
950
+ const relFromShare = [client.basePath, parent, p].filter(Boolean).join("/");
936
951
  let ok = false;
937
952
  let perr = null;
938
953
  try {
939
- await client.readdir(candidate);
954
+ await rawReaddir(relFromShare);
940
955
  ok = true;
941
956
  } catch (pErr) {
942
957
  perr = String((pErr && pErr.message) || pErr || "");
943
958
  }
944
- probe_results.push({ candidate: p, ok, error: ok ? null : perr });
959
+ probe_results.push({ candidate: p, tested_path: relFromShare, ok, error: ok ? null : perr });
945
960
  }
946
961
  const workingAlt = probe_results.find(
947
962
  (r) => r.ok && r.candidate !== missing
948
963
  );
964
+ const testedPath = "\\\\" + testCfg.server + "\\" + testCfg.share + "\\" + rel.replace(/\//g, "\\");
949
965
  let hintText;
950
966
  if (workingAlt) {
951
967
  hintText =
952
- "Der Ordner heisst auf dem Server \u201e" +
968
+ "Der Ordner hei\u00dft auf dem Server \u201e" +
953
969
  workingAlt.candidate +
954
- "\u201c (andere Gross-/Kleinschreibung). Bitte den " +
970
+ "\u201c (andere Gro\u00df-/Kleinschreibung). Bitte den " +
955
971
  "Basispfad exakt so eintragen \u2014 der Samba-Server ist " +
956
972
  "case-sensitive (\u201ecase sensitive = yes\u201c in smb.conf).";
957
973
  } else {
958
974
  hintText =
959
- "Der Basispfad \u201e" + rel + "\u201c ist auf dem " +
960
- "Server nicht auffindbar. M\u00f6gliche Ursachen: " +
961
- "(a) der Ordner existiert wirklich nicht (bitte mit " +
962
- "einem SMB-Client wie \u201esmbclient\u201c oder dem " +
963
- "Windows-Explorer gegenpr\u00fcfen); " +
975
+ "Der Basispfad \u201e" + rel + "\u201c wurde auf der " +
976
+ "Freigabe \u201e" + testCfg.share + "\u201c nicht " +
977
+ "gefunden. Getestet wurde der Pfad: " + testedPath + ". " +
978
+ "M\u00f6gliche Ursachen: (a) Ordner existiert unter genau " +
979
+ "diesem Namen nicht in dieser Freigabe (Basispfad ist " +
980
+ "relativ zur Freigabe \u2014 also z.\u202fB. \u201eunterordner\u201c, " +
981
+ "nicht \u201e" + testCfg.share + "/unterordner\u201c); " +
964
982
  "(b) der angemeldete Benutzer \u201e" +
965
983
  (testCfg.username || "(anonymous)") +
966
- "\u201c hat kein Leserecht auf den Ordner (Samba antwortet " +
967
- "dann bei \u201ehide unreadable = yes\u201c mit " +
968
- "NAME_NOT_FOUND statt ACCESS_DENIED); " +
969
- "(c) der Ordner ist per \u201eveto files\u201c / " +
970
- "\u201ehide files\u201c auf dem Server ausgeblendet.";
984
+ "\u201c darf den Ordner nicht sehen; " +
985
+ "(c) der Ordner ist per Samba-Konfiguration ausgeblendet.";
971
986
  }
972
987
  const e = new Error(hintText);
973
988
  e.cause = err;
@@ -1017,7 +1032,19 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
1017
1032
  const code = (err && (err.code || err.errno)) || null;
1018
1033
  let hint = null;
1019
1034
  const m = msg.toLowerCase();
1020
- if (code === "ECONNREFUSED" || m.includes("econnrefused"))
1035
+ // If the wrapper already produced a well-formed, self-explanatory
1036
+ // error (own `code` set), skip the substring-match hint table —
1037
+ // it would otherwise trigger on words that appear inside our own
1038
+ // human-readable German explanation text (e.g. our message text
1039
+ // mentions "access_denied" or "path" for teaching purposes).
1040
+ const codesWithOwnMessage = new Set([
1041
+ "BASE_PATH_NOT_FOUND",
1042
+ "BASE_PATH_NOT_A_DIR",
1043
+ ]);
1044
+ if (code && codesWithOwnMessage.has(code)) {
1045
+ hint = null;
1046
+ }
1047
+ else if (code === "ECONNREFUSED" || m.includes("econnrefused"))
1021
1048
  hint = "The server refused the connection on port " + testCfg.port +
1022
1049
  ". Check that Samba is running and that a firewall (or Docker) does not block TCP/445.";
1023
1050
  else if (code === "ETIMEDOUT" || m.includes("etimedout") || m.includes("timed out"))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saltcorn-samba",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
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": {