saltcorn-samba 0.3.8 → 0.3.10
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 +104 -0
- package/index.js +8 -8
- package/package.json +1 -1
- package/smb-client.js +29 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,110 @@ 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.3.10] – 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Fixed – **DNS-Fehler: `getaddrinfo ENOTFOUND "host:445"`**
|
|
10
|
+
|
|
11
|
+
Nachdem 0.3.9 den Callback-Signatur-Bug behoben hatte und der Test-Button
|
|
12
|
+
endlich echt gegen den SMB-Server lief, kam bei der Verbindung ein neuer
|
|
13
|
+
Fehler zum Vorschein:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
getaddrinfo ENOTFOUND 192.168.110.10:445 (ENOTFOUND)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Ursache:** In `smb-client.js` wurde der UNC-Share-String so gebaut:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const shareStr = `\\\\${server}${port ? ":" + port : ""}\\${share}`;
|
|
23
|
+
// => "\\\\192.168.110.10:445\\buero"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`@marsaud/smb2` interpretiert alles zwischen den führenden `\\` und dem
|
|
27
|
+
nächsten `\` als **Hostnamen** und reicht das direkt an
|
|
28
|
+
`net.connect()` / `dns.lookup()` weiter. Node versucht dann
|
|
29
|
+
`"192.168.110.10:445"` als Hostnamen aufzulösen — was natürlich
|
|
30
|
+
fehlschlägt.
|
|
31
|
+
|
|
32
|
+
**Fix:** Host und Port sauber trennen.
|
|
33
|
+
|
|
34
|
+
- Der `share`-UNC-Pfad enthält jetzt **nur den Host**:
|
|
35
|
+
`\\192.168.110.10\buero`
|
|
36
|
+
- Der Port wird über die separate `port`-Option an `SMB2` übergeben
|
|
37
|
+
(Default 445).
|
|
38
|
+
- Zusätzlich tolerant: falls jemand versehentlich `host:445` ins
|
|
39
|
+
Server-Feld einträgt, wird der Port dort herausgezogen.
|
|
40
|
+
- IPv6-Adressen in eckigen Klammern (`[::1]:445`) werden unterstützt.
|
|
41
|
+
|
|
42
|
+
### Notes
|
|
43
|
+
- Keine Konfig-Migration nötig. Wer bisher Host **ohne** Port eingetragen
|
|
44
|
+
hat, bekommt jetzt genau die gleiche Verbindung wie vorher — nur
|
|
45
|
+
funktionierend.
|
|
46
|
+
- Wer aus Verzweiflung `IP:445` ins Server-Feld getippt hatte: das wird
|
|
47
|
+
jetzt automatisch aufgesplittet. Sauberer ist trotzdem: Host im
|
|
48
|
+
Server-Feld, Port im Port-Feld.
|
|
49
|
+
|
|
50
|
+
## [0.3.9] – 2026-07-05
|
|
51
|
+
|
|
52
|
+
### Fixed – **Root-Cause-Fix: Falsche Callback-Signatur in allen Routen**
|
|
53
|
+
|
|
54
|
+
Die Diagnose-Ausgabe aus 0.3.8 hat gezeigt, dass in den Route-Handlern
|
|
55
|
+
**alle** Felder von `req` leer waren (`has_req_user: false`,
|
|
56
|
+
`has_session: false`, `referer: null` usw.). Ursache war eine falsche
|
|
57
|
+
Callback-Signatur, die seit 0.1 in **allen 8 Routen** verwendet wurde.
|
|
58
|
+
|
|
59
|
+
Saltcorn registriert Plugin-Routen so
|
|
60
|
+
(`packages/server/plugin_routes_handler.js`, Zeilen 40–52):
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
tenantRouter.post(url, error_catcher(route.callback));
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
und `error_catcher` ist definiert als
|
|
67
|
+
(`packages/server/routes/utils.ts`, Zeile 427):
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const error_catcher = (fn) => (request, response, next) => {
|
|
71
|
+
...; fn(request, response, next);
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Die Callbacks werden also mit der klassischen Express-Signatur
|
|
76
|
+
`(req, res, next)` aufgerufen — **nicht** mit einem Objekt `{ req, res }`.
|
|
77
|
+
|
|
78
|
+
Bisher stand in `index.js` überall:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
callback: async ({ req, res }) => { ... }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Dadurch wurde aus dem echten Express-`request`-Objekt versucht, `req.req`
|
|
85
|
+
und `req.res` per Destructuring zu holen — beides `undefined`. Effekt:
|
|
86
|
+
|
|
87
|
+
- `req.user`, `req.session`, `req.headers`, `req.body`, `req.query`,
|
|
88
|
+
`req.csrfToken()` waren alle unerreichbar.
|
|
89
|
+
- Der Admin-Check konnte niemals erfolgreich sein.
|
|
90
|
+
- Der Samba-Verbindungstest hat *nie* funktioniert.
|
|
91
|
+
- Datei-Listing, Upload, Rename, Delete, Mkdir waren ebenfalls betroffen.
|
|
92
|
+
|
|
93
|
+
**Fix:** Alle 8 Routen (`/sambadir`, `/sambafile`, `/sambalink`,
|
|
94
|
+
`/sambatest`, `/sambaupload`, `/sambadelete`, `/sambarename`,
|
|
95
|
+
`/sambamkdir`) auf die korrekte Signatur umgestellt:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
callback: async (req, res) => { ... }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Damit erhält jeder Handler das echte Express-`req`-Objekt mit
|
|
102
|
+
`req.user`, `req.session`, `req.headers`, `req.body`, `req.csrfToken()`,
|
|
103
|
+
usw. — und die gesamte Admin- und Berechtigungslogik aus 0.3.7/0.3.8
|
|
104
|
+
greift jetzt so, wie sie gedacht war.
|
|
105
|
+
|
|
106
|
+
### Notes
|
|
107
|
+
- Kein Funktionsverlust, keine Konfig-Migration nötig.
|
|
108
|
+
- Wer 0.3.8 installiert hat und den Test-Button nicht zum Laufen bekommen
|
|
109
|
+
hat: **Update auf 0.3.9 einspielen, Server neu starten, erneut testen.**
|
|
110
|
+
|
|
7
111
|
## [0.3.8] – 2026-07-05
|
|
8
112
|
|
|
9
113
|
### Changed
|
package/index.js
CHANGED
|
@@ -480,7 +480,7 @@ const routes = [
|
|
|
480
480
|
{
|
|
481
481
|
url: "/sambadir",
|
|
482
482
|
method: "get",
|
|
483
|
-
callback: async (
|
|
483
|
+
callback: async (req, res) => {
|
|
484
484
|
const cfg = getConfig();
|
|
485
485
|
if (!cfg.server) return jsonError(res, 500, "Samba plugin not configured");
|
|
486
486
|
if (!canRead(req, cfg)) return jsonError(res, 403, "Forbidden");
|
|
@@ -528,7 +528,7 @@ const routes = [
|
|
|
528
528
|
{
|
|
529
529
|
url: "/sambafile",
|
|
530
530
|
method: "get",
|
|
531
|
-
callback: async (
|
|
531
|
+
callback: async (req, res) => {
|
|
532
532
|
const cfg = getConfig();
|
|
533
533
|
if (!cfg.server) return res.status(500).send("Samba plugin not configured");
|
|
534
534
|
if (!canRead(req, cfg)) return res.status(403).send("Forbidden");
|
|
@@ -561,7 +561,7 @@ const routes = [
|
|
|
561
561
|
{
|
|
562
562
|
url: "/sambalink",
|
|
563
563
|
method: "get",
|
|
564
|
-
callback: async (
|
|
564
|
+
callback: async (req, res) => {
|
|
565
565
|
const cfg = getConfig();
|
|
566
566
|
if (!cfg.server) return res.status(500).send("Samba plugin not configured");
|
|
567
567
|
if (!canRead(req, cfg)) return res.status(403).send("Forbidden");
|
|
@@ -617,7 +617,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
617
617
|
// when the config-page CSRF token has already been consumed by the
|
|
618
618
|
// save-workflow or when a stricter CSRF policy is applied.
|
|
619
619
|
noCsrf: true,
|
|
620
|
-
callback: async (
|
|
620
|
+
callback: async (req, res) => {
|
|
621
621
|
// -----------------------------------------------------------------
|
|
622
622
|
// Admin gate — komplett neu in 0.3.8:
|
|
623
623
|
//
|
|
@@ -822,7 +822,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
822
822
|
{
|
|
823
823
|
url: "/sambaupload",
|
|
824
824
|
method: "post",
|
|
825
|
-
callback: async (
|
|
825
|
+
callback: async (req, res) => {
|
|
826
826
|
const cfg = getConfig();
|
|
827
827
|
if (!cfg.server) return jsonError(res, 500, "Samba plugin not configured");
|
|
828
828
|
if (!canWrite(req, cfg)) return jsonError(res, 403, "Forbidden");
|
|
@@ -893,7 +893,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
893
893
|
{
|
|
894
894
|
url: "/sambadelete",
|
|
895
895
|
method: "post",
|
|
896
|
-
callback: async (
|
|
896
|
+
callback: async (req, res) => {
|
|
897
897
|
const cfg = getConfig();
|
|
898
898
|
if (!cfg.server) return jsonError(res, 500, "Samba plugin not configured");
|
|
899
899
|
if (!canWrite(req, cfg)) return jsonError(res, 403, "Forbidden");
|
|
@@ -924,7 +924,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
924
924
|
{
|
|
925
925
|
url: "/sambarename",
|
|
926
926
|
method: "post",
|
|
927
|
-
callback: async (
|
|
927
|
+
callback: async (req, res) => {
|
|
928
928
|
const cfg = getConfig();
|
|
929
929
|
if (!cfg.server) return jsonError(res, 500, "Samba plugin not configured");
|
|
930
930
|
if (!canWrite(req, cfg)) return jsonError(res, 403, "Forbidden");
|
|
@@ -987,7 +987,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
987
987
|
{
|
|
988
988
|
url: "/sambamkdir",
|
|
989
989
|
method: "post",
|
|
990
|
-
callback: async (
|
|
990
|
+
callback: async (req, res) => {
|
|
991
991
|
const cfg = getConfig();
|
|
992
992
|
if (!cfg.server) return jsonError(res, 500, "Samba plugin not configured");
|
|
993
993
|
if (!canWrite(req, cfg)) return jsonError(res, 403, "Forbidden");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saltcorn-samba",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Saltcorn plugin: browse, upload, rename and delete files on a Samba/CIFS share. 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
|
@@ -116,13 +116,41 @@ function buildClient(config) {
|
|
|
116
116
|
if (!share) throw new Error("Samba: share missing");
|
|
117
117
|
if (/[\\/]/.test(share)) throw new Error("Samba: share must not contain slashes");
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
// Host und Port sauber trennen. Der Server-String darf NICHT im share-UNC-
|
|
120
|
+
// Pfad landen, sonst versucht Node's DNS "host:port" als Hostnamen aufzulösen
|
|
121
|
+
// (getaddrinfo ENOTFOUND "1.2.3.4:445"). @marsaud/smb2 nimmt den Port über
|
|
122
|
+
// die separate `port`-Option entgegen; der share-String enthält nur den Host.
|
|
123
|
+
//
|
|
124
|
+
// Zusätzlich tolerant sein, falls jemand versehentlich "host:445" ins
|
|
125
|
+
// Server-Feld getippt hat — dann Port dort rausziehen.
|
|
126
|
+
let hostOnly = String(server).trim();
|
|
127
|
+
let portFromServer;
|
|
128
|
+
// IPv6-Adressen in eckigen Klammern zulassen: [::1]:445
|
|
129
|
+
const v6 = hostOnly.match(/^\[([^\]]+)\](?::(\d+))?$/);
|
|
130
|
+
if (v6) {
|
|
131
|
+
hostOnly = v6[1];
|
|
132
|
+
if (v6[2]) portFromServer = Number(v6[2]);
|
|
133
|
+
} else {
|
|
134
|
+
// Nur splitten, wenn genau EIN Doppelpunkt — sonst IPv6 ohne Klammern
|
|
135
|
+
const colonCount = (hostOnly.match(/:/g) || []).length;
|
|
136
|
+
if (colonCount === 1) {
|
|
137
|
+
const [h, p] = hostOnly.split(":");
|
|
138
|
+
if (h && /^\d+$/.test(p)) {
|
|
139
|
+
hostOnly = h;
|
|
140
|
+
portFromServer = Number(p);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const effectivePort = Number(port) || portFromServer || 445;
|
|
146
|
+
const shareStr = `\\\\${hostOnly}\\${share}`;
|
|
120
147
|
const SMB2 = getSMB2();
|
|
121
148
|
const smb = new SMB2({
|
|
122
149
|
share: shareStr,
|
|
123
150
|
domain: domain || "WORKGROUP",
|
|
124
151
|
username: username || "guest",
|
|
125
152
|
password: password || "",
|
|
153
|
+
port: effectivePort,
|
|
126
154
|
autoCloseTimeout: 10000,
|
|
127
155
|
});
|
|
128
156
|
|