saltcorn-samba 0.4.1 → 0.4.2

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 CHANGED
@@ -4,6 +4,41 @@ 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.2] – 2026-07-05
8
+
9
+ ### Fixed – **Dropdown-Felder zeigen `[object Object]` statt Optionen**
10
+
11
+ Die neuen Felder `signing_mode` und `encryption_mode` wurden in der
12
+ Saltcorn-UI mit `[object Object]` als einzige Auswahl gerendert.
13
+
14
+ **Ursache:** In der genutzten Saltcorn-Version werden `attributes.options`
15
+ als einfaches String-Array erwartet (`["a", "b", "c"]`), nicht als
16
+ `{value, label}`-Objekt. Die Objekt-Form ist erst in neueren Builds
17
+ vollständig unterstützt; sonst castet das UI die Objekte zu Strings.
18
+
19
+ **Fix:** `options` auf String-Array umgestellt —
20
+ `["if-offered", "required", "disabled"]`. Die Beschriftung bleibt in
21
+ der `sublabel` erhalten (der Config-Wizard erklärt jeden Wert dort).
22
+
23
+ ### Fixed – **`STATUS_OBJECT_NAME_NOT_FOUND` durch fehlgeleiteten Root-Fallback**
24
+
25
+ Der 0.4.1-Fallback `share/.` triggerte auf Samba einen anderen NT-Status:
26
+ `STATUS_OBJECT_NAME_NOT_FOUND` (0xC0000034 / `ENOENT`), weil Samba `.` als
27
+ literalen Dateinamen sucht statt als Current-Directory-Marker (das ist im
28
+ POSIX-Layer, nicht im SMB2-Protokoll). Der zweite Fallback `share/*` ist
29
+ protokoll-illegal (Wildcard im CREATE) und wird ebenfalls abgelehnt.
30
+
31
+ **Neuer Ansatz:** Wir versuchen den Fallback erst gar nicht. Statt zu
32
+ raten geben wir eine klare deutsche Fehlermeldung aus: **einen Basispfad
33
+ setzen**. Das ist die einzige zuverlässige Lösung, solange `smb3-client`
34
+ die `FileInformationClass` nicht konfigurierbar macht.
35
+
36
+ Die Test-Route bleibt weiterhin nachsichtig: bei `OBJECT_NAME_INVALID`
37
+ auf dem Root fällt sie auf `stat("")` zurück und meldet ein Erfolg mit
38
+ Hinweis („Verbindung + Anmeldung erfolgreich, aber Share-Root nicht
39
+ direkt auflistbar — bitte Basispfad setzen“). Der Hinweis wird als gelbe
40
+ Box im Test-Ergebnis angezeigt.
41
+
7
42
  ## [0.4.1] – 2026-07-05
8
43
 
9
44
  ### Fixed – **`QUERY_DIRECTORY failed: 0xC0000033` auf Share-Root (Samba 4.20+/4.23+)**
package/index.js CHANGED
@@ -160,6 +160,9 @@ window.sambaTestConn = async function(btn) {
160
160
  var rows = (data.entries||[]).map(function(e){
161
161
  return '<li>'+ (e.isDirectory?'📁 ':'📄 ') + String(e.name).replace(/[<>&]/g,'?') +'</li>';
162
162
  }).join('');
163
+ var noteHtml = data.note
164
+ ? '<div style="margin-top:.5rem;padding:.4rem .6rem;background:#fff3cd;border:1px solid #ffeeba;border-radius:.25rem"><b>Hinweis:</b> ' + String(data.note).replace(/[<>&]/g,'?') + '</div>'
165
+ : '';
163
166
  out.innerHTML =
164
167
  '<div class="alert alert-success">' +
165
168
  '<b>✓ Verbindung erfolgreich</b> (' + data.duration_ms + ' ms)<br>' +
@@ -167,6 +170,7 @@ window.sambaTestConn = async function(btn) {
167
170
  'Basispfad: <code>' + data.base_path + '</code>, Benutzer: <code>' + data.username + '</code><br>' +
168
171
  'Einträge gefunden: <b>' + data.entry_count + '</b>' + (data.truncated ? ' (erste 20 unten)' : '') +
169
172
  (rows ? '<ul style="margin:.5rem 0 0 1rem">' + rows + '</ul>' : '') +
173
+ noteHtml +
170
174
  '</div>';
171
175
  } else {
172
176
  var a = data && data.attempted || {};
@@ -301,11 +305,7 @@ const configuration_workflow = () =>
301
305
  type: "String",
302
306
  required: true,
303
307
  attributes: {
304
- options: [
305
- { value: "if-offered", label: "if-offered (Standard)" },
306
- { value: "required", label: "required (strikt)" },
307
- { value: "disabled", label: "disabled (aus)" },
308
- ],
308
+ options: ["if-offered", "required", "disabled"],
309
309
  },
310
310
  default: "if-offered",
311
311
  }),
@@ -321,11 +321,7 @@ const configuration_workflow = () =>
321
321
  type: "String",
322
322
  required: true,
323
323
  attributes: {
324
- options: [
325
- { value: "if-offered", label: "if-offered (Standard)" },
326
- { value: "required", label: "required (strikt)" },
327
- { value: "disabled", label: "disabled (aus)" },
328
- ],
324
+ options: ["if-offered", "required", "disabled"],
329
325
  },
330
326
  default: "if-offered",
331
327
  }),
@@ -804,18 +800,32 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
804
800
  try {
805
801
  return await client.readdir(rel);
806
802
  } catch (err) {
807
- const msg = String((err && err.message) || err || "");
803
+ // Look at both the wrapper error and its underlying cause.
804
+ const causeMsg = String((err && err.cause && err.cause.message) || "");
805
+ const msg = String((err && err.message) || err || "") + " " + causeMsg;
808
806
  const isRootProbe = !rel;
809
- const isNameInvalid = /0xC0000033|OBJECT_NAME_INVALID/i.test(msg);
810
- if (isRootProbe && isNameInvalid) {
811
- // Fall back: proof-of-life via stat on share root.
812
- await client.stat("");
813
- return [];
807
+ const isRootEnumBug = /0xC0000033|OBJECT_NAME_INVALID|Share-Root/i.test(msg);
808
+ if (isRootProbe && isRootEnumBug) {
809
+ // Fall back: proof-of-life via stat on share root. This
810
+ // confirms TCP + Negotiate + Session + Auth + TREE_CONNECT
811
+ // without hitting the broken QUERY_DIRECTORY path.
812
+ try {
813
+ await client.stat("");
814
+ // Signal to the caller that the connection works, but
815
+ // the share root cannot be enumerated on this server.
816
+ const marker = [];
817
+ marker._rootNotEnumerable = true;
818
+ return marker;
819
+ } catch (statErr) {
820
+ // stat also failed — surface the original error.
821
+ throw err;
822
+ }
814
823
  }
815
824
  throw err;
816
825
  }
817
826
  });
818
827
  const took = Date.now() - started;
828
+ const rootNotEnum = Array.isArray(listing) && listing._rootNotEnumerable === true;
819
829
  return res.json({
820
830
  ok: true,
821
831
  server: testCfg.server,
@@ -831,6 +841,13 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
831
841
  isDirectory: !!(e && (e.isDirectory === true || (e.stats && e.stats.isDirectory && e.stats.isDirectory()))),
832
842
  })),
833
843
  truncated: Array.isArray(listing) && listing.length > 20,
844
+ note: rootNotEnum
845
+ ? "Verbindung + Anmeldung erfolgreich. Der Server erlaubt jedoch " +
846
+ "kein direktes Auflisten des Share-Roots (bekanntes Samba-" +
847
+ "Verhalten mit smb3-client). Bitte setzen Sie einen Basispfad " +
848
+ "in der Plugin-Config (z.\u202fB. einen Unterordner der " +
849
+ "Freigabe) — dann funktioniert der File-Manager vollständig."
850
+ : undefined,
834
851
  });
835
852
  } catch (err) {
836
853
  // Turn opaque SMB / socket errors into actionable hints.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saltcorn-samba",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
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": {
package/smb-client.js CHANGED
@@ -279,36 +279,32 @@ async function buildClient(config) {
279
279
  } catch (err) {
280
280
  // Some Samba builds (observed on 4.20+/4.23+) reject
281
281
  // SMB2 QUERY_DIRECTORY on the *share root* with
282
- // STATUS_OBJECT_NAME_INVALID (0xC0000033) because the CREATE
283
- // used an empty filename. The fallback below re-issues the
284
- // request with an explicit path (".") which most Samba VFS
285
- // modules accept for the current directory.
286
- const status = (err && (err.status || err.code)) || "";
287
- const msg = String((err && err.message) || err || "");
282
+ // STATUS_OBJECT_NAME_INVALID (0xC0000033) because smb3-client
283
+ // asks for FileIdBothDirectoryInformation (class 37) on a
284
+ // handle opened with an empty filename. We cannot swap the
285
+ // information class from userland, and Samba refuses both
286
+ // "." ( STATUS_OBJECT_NAME_NOT_FOUND) and "*" (→ CREATE with
287
+ // wildcard is protocol-illegal). The clean way out is:
288
+ // require the caller to configure a real base_path so every
289
+ // readdir happens inside a directory the server can enumerate.
290
+ const msg = String((err && err.message) || err || "");
288
291
  const isRoot = !rel && !basePath;
289
- const isNameInvalid =
290
- /0xC0000033|STATUS_OBJECT_NAME_INVALID|OBJECT_NAME_INVALID/i.test(msg) ||
291
- status === 0xc0000033;
292
- if (isRoot && isNameInvalid) {
293
- try {
294
- dirents = await client.readdir(shareName + "/.", { withFileTypes: true });
295
- } catch (_) {
296
- // Second fallback: an explicit wildcard segment.
297
- try {
298
- dirents = await client.readdir(shareName + "/*", { withFileTypes: true });
299
- } catch (_) {
300
- const e = new Error(
301
- "QUERY_DIRECTORY auf dem Share-Root schlug fehl (" +
302
- (msg || "OBJECT_NAME_INVALID") + "). " +
303
- "Setzen Sie einen Basispfad im Plugin-Konfig oder prüfen Sie die Share-Definition auf dem Server."
304
- );
305
- e.cause = err;
306
- throw e;
307
- }
308
- }
309
- } else {
310
- throw err;
292
+ const isRootEnumBug =
293
+ /0xC0000033|OBJECT_NAME_INVALID|QUERY_DIRECTORY/i.test(msg);
294
+ if (isRoot && isRootEnumBug) {
295
+ const e = new Error(
296
+ "Das Share-Root lässt sich auf diesem Samba-Server nicht " +
297
+ "direkt auflisten (" + (msg || "QUERY_DIRECTORY failed") + "). " +
298
+ "Bitte in der Plugin-Config einen Basispfad setzen (z.\u202fB. " +
299
+ "einen Unterordner der Freigabe wie „daten“ oder „projekte“) — " +
300
+ "dann funktionieren alle Directory-Listings innerhalb dieses " +
301
+ "Unterordners. Details siehe README, Abschnitt „Troubleshooting " +
302
+ "QUERY_DIRECTORY auf Share-Root“."
303
+ );
304
+ e.cause = err;
305
+ throw e;
311
306
  }
307
+ throw err;
312
308
  }
313
309
  // Parallel enrichment. Bounded to a reasonable concurrency to avoid
314
310
  // saturating the SMB session on huge directories.