node-red-contrib-hik-media-buffer 1.1.85 → 1.1.87

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 (2) hide show
  1. package/hik-media-buffer.js +34 -18
  2. package/package.json +1 -1
@@ -36,63 +36,79 @@ module.exports = function(RED) {
36
36
  const nvrAuthAxios = new AxiosDigestAuth({ username: node.user, password: node.pass });
37
37
 
38
38
  // --- FUNZIONE AUSILIARIA PER FARE LA POST DIGEST CON HTTP NATIVO (ELIMINA IL 400) ---
39
+ // --- FUNZIONE DEFINITIVA PER LA POST DIGEST CON CALCOLO INTEGRITÀ DEL BODY ---
39
40
  function nativeDigestPost(urlPath, xmlBody) {
40
41
  return new Promise((resolve, reject) => {
41
- // Primo invio a vuoto per scatenare il 401 dell'handshake Digest
42
42
  const options = {
43
43
  hostname: node.host,
44
44
  port: node.port,
45
45
  path: urlPath,
46
46
  method: 'POST',
47
- headers: { 'Content-Type': 'application/xml', 'Content-Length': Buffer.byteLength(xmlBody) }
47
+ headers: {
48
+ 'Content-Type': 'application/xml; charset=UTF-8',
49
+ 'Content-Length': Buffer.byteLength(xmlBody, 'utf8')
50
+ }
48
51
  };
49
52
 
53
+ // Primo invio a vuoto per scatenare il 401 dell'NVR
50
54
  const req = http.request(options, (res) => {
51
55
  let chunks = [];
52
56
  res.on('data', (chunk) => chunks.push(chunk));
53
57
  res.on('end', () => {
54
58
  const body = Buffer.concat(chunks).toString();
55
59
 
56
- // Se l'NVR risponde 401 (normale al primo colpo), estraiamo i parametri Digest
57
60
  if (res.statusCode === 401) {
58
61
  const authHeader = res.headers['www-authenticate'];
59
62
  if (!authHeader) return reject(new Error("Manca header WWW-Authenticate"));
60
63
 
61
- // Estrazione parametri con regex
64
+ // Estrazione parametri di sicurezza
62
65
  const realm = authHeader.match(/realm="([^"]+)"/)[1];
63
66
  const nonce = authHeader.match(/nonce="([^"]+)"/)[1];
64
67
  const qopMatch = authHeader.match(/qop="([^"]+)"/);
65
68
  const qop = qopMatch ? qopMatch[1] : null;
66
69
 
67
- // Calcolo degli Hash MD5 standard dell'handshake (Come fa Chrome!)
68
70
  const crypto = require('crypto');
69
- const md5 = (str) => crypto.createHash('md5').update(str).digest('hex');
71
+ const md5 = (str) => crypto.createHash('md5').update(str, 'utf8').digest('hex');
70
72
 
73
+ // 1. Calcolo HA1 base
71
74
  const ha1 = md5(`${node.user}:${realm}:${node.pass}`);
72
- const ha2 = md5(`POST:${urlPath}`);
73
75
 
74
- let authString = `Digest username="${node.user}", realm="${realm}", nonce="${nonce}", uri="${urlPath}", response=""`;
76
+ // 2. Calcolo HA2: SE L'NVR CHIEDE AUTH-INT, INSERIAMO L'HASH DEL BODY XML!
77
+ let ha2;
78
+ if (qop && qop.includes('auth-int')) {
79
+ const bodyHash = md5(xmlBody);
80
+ ha2 = md5(`POST:${urlPath}:${bodyHash}`);
81
+ } else {
82
+ ha2 = md5(`POST:${urlPath}`);
83
+ }
75
84
 
85
+ // 3. Generazione parametri finali dell'handshake
86
+ const cnonce = crypto.randomBytes(8).toString('hex');
87
+ const nc = "00000001";
88
+ let response;
89
+ let authString;
90
+
76
91
  if (qop) {
77
- const cnonce = crypto.randomBytes(8).toString('hex');
78
- const nc = "00000001";
79
- const response = md5(`${ha1}:${nonce}:${nc}:${cnonce}:${qop}:${ha2}`);
80
- authString = `Digest username="${node.user}", realm="${realm}", nonce="${nonce}", uri="${urlPath}", qop=${qop}, nc=${nc}, cnonce="${cnonce}", response="${response}"`;
92
+ // Se c'è il qop (auth o auth-int), usiamo la formula estesa
93
+ const actualQop = qop.includes('auth-int') ? 'auth-int' : 'auth';
94
+ response = md5(`${ha1}:${nonce}:${nc}:${cnonce}:${actualQop}:${ha2}`);
95
+ authString = `Digest username="${node.user}", realm="${realm}", nonce="${nonce}", uri="${urlPath}", qop="${actualQop}", nc=${nc}, cnonce="${cnonce}", response="${response}"`;
81
96
  } else {
82
- const response = md5(`${ha1}:${nonce}:${ha2}`);
97
+ // Altrimenti andiamo di Digest classico
98
+ response = md5(`${ha1}:${nonce}:${ha2}`);
83
99
  authString = `Digest username="${node.user}", realm="${realm}", nonce="${nonce}", uri="${urlPath}", response="${response}"`;
84
100
  }
85
101
 
86
- // Secondo invio definitivo con l'autenticazione calcolata al millimetro
102
+ // Secondo invio definitivo con l'header Authorization corretto al millesimo
87
103
  const finalOptions = {
88
104
  hostname: node.host,
89
105
  port: node.port,
90
106
  path: urlPath,
91
107
  method: 'POST',
92
108
  headers: {
93
- 'Content-Type': 'application/xml',
109
+ 'Content-Type': 'application/xml; charset=UTF-8',
94
110
  'Authorization': authString,
95
- 'Content-Length': Buffer.byteLength(xmlBody)
111
+ 'Content-Length': Buffer.byteLength(xmlBody, 'utf8')
96
112
  }
97
113
  };
98
114
 
@@ -107,7 +123,7 @@ module.exports = function(RED) {
107
123
  });
108
124
  });
109
125
  finalReq.on('error', (err) => reject(err));
110
- finalReq.write(xmlBody);
126
+ finalReq.write(xmlBody, 'utf8');
111
127
  finalReq.end();
112
128
  } else {
113
129
  resolve({ statusCode: res.statusCode, data: body });
@@ -115,7 +131,7 @@ module.exports = function(RED) {
115
131
  });
116
132
  });
117
133
  req.on('error', (err) => reject(err));
118
- req.write(xmlBody);
134
+ req.write(xmlBody, 'utf8');
119
135
  req.end();
120
136
  });
121
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-hik-media-buffer",
3
- "version": "1.1.85",
3
+ "version": "1.1.87",
4
4
  "description": "Ottiene buffer video e immagine da camere Hikvision via ISAPI",
5
5
  "keywords": [
6
6
  "node-red",