v5.0.9: ZIP модуль вынесен в site/static/zip.js, тесты test_zip.py (28 тестов), исправлен баг смещения start+18
Deploy drhider / validate (push) Waiting to run
Deploy drhider / validate (push) Waiting to run
This commit is contained in:
+1
-1
@@ -20,7 +20,7 @@ if _sys_path_root not in sys.path:
|
||||
sys.path.insert(0, _sys_path_root)
|
||||
|
||||
# Версия приложения (меняется при изменениях)
|
||||
VERSION = "5.0.7"
|
||||
VERSION = "5.0.9"
|
||||
|
||||
|
||||
def create_app():
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Нативный ZIP — упаковка и распаковка без внешних библиотек.
|
||||
*
|
||||
* Использование:
|
||||
* const files = await unzip(zipBlob); // {name: Blob, ...}
|
||||
* const zip = await buildZip([{name, data}]); // Blob
|
||||
*/
|
||||
|
||||
/**
|
||||
* Распаковать ZIP blob → {filename: Blob}.
|
||||
* Поддерживает только STORE (без сжатия) — как отдаёт drhider.
|
||||
*/
|
||||
async function unzip(zb) {
|
||||
const buf = await zb.arrayBuffer();
|
||||
const dv = new DataView(buf);
|
||||
const files = {};
|
||||
let pos = 0;
|
||||
|
||||
while (pos < buf.byteLength - 4) {
|
||||
const sig = dv.getUint32(pos, true);
|
||||
if (sig === 0x04034b50) {
|
||||
// Local file header
|
||||
const start = pos;
|
||||
pos += 26;
|
||||
const nameLen = dv.getUint16(pos, true);
|
||||
pos += 2;
|
||||
const extraLen = dv.getUint16(pos, true);
|
||||
pos += 2;
|
||||
const name = new TextDecoder().decode(
|
||||
new Uint8Array(buf, pos, nameLen)
|
||||
);
|
||||
pos += nameLen + extraLen;
|
||||
const compSize = dv.getUint32(start + 18, true);
|
||||
files[name] = new Blob([new Uint8Array(buf, pos, compSize)]);
|
||||
pos += compSize;
|
||||
} else {
|
||||
break; // Central directory или EOCD — конец файлов
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать ZIP blob из entries [{name, data}].
|
||||
* Метод: STORE (без сжатия), UTF-8 имена.
|
||||
*/
|
||||
async function buildZip(entries) {
|
||||
const parts = [];
|
||||
const centralDir = [];
|
||||
let offset = 0;
|
||||
|
||||
for (const e of entries) {
|
||||
const data = new Uint8Array(await e.data.arrayBuffer());
|
||||
const nameBytes = new TextEncoder().encode(e.name);
|
||||
|
||||
// Local file header (30 bytes + filename)
|
||||
const lh = new Uint8Array(30 + nameBytes.length);
|
||||
const lv = new DataView(lh.buffer);
|
||||
lv.setUint32(0, 0x04034b50, true); // signature
|
||||
lv.setUint16(6, 0x800, true); // flags: UTF-8
|
||||
lv.setUint32(18, data.length, true); // compressed size
|
||||
lv.setUint32(22, data.length, true); // uncompressed size
|
||||
lv.setUint16(26, nameBytes.length, true);
|
||||
lh.set(nameBytes, 30);
|
||||
parts.push(lh, data);
|
||||
|
||||
// Central directory entry (46 bytes + filename)
|
||||
const ce = new Uint8Array(46 + nameBytes.length);
|
||||
const cv = new DataView(ce.buffer);
|
||||
cv.setUint32(0, 0x02014b50, true);
|
||||
cv.setUint16(8, 0x800, true);
|
||||
cv.setUint32(20, data.length, true);
|
||||
cv.setUint32(24, data.length, true);
|
||||
cv.setUint16(28, nameBytes.length, true);
|
||||
cv.setUint32(42, offset, true);
|
||||
ce.set(nameBytes, 46);
|
||||
centralDir.push(ce);
|
||||
|
||||
offset += 30 + nameBytes.length + data.length;
|
||||
}
|
||||
|
||||
// End of central directory (22 bytes)
|
||||
const cdSize = centralDir.reduce((s, c) => s + c.length, 0);
|
||||
const eocd = new Uint8Array(22);
|
||||
const ev = new DataView(eocd.buffer);
|
||||
ev.setUint32(0, 0x06054b50, true);
|
||||
ev.setUint16(8, entries.length, true);
|
||||
ev.setUint16(10, entries.length, true);
|
||||
ev.setUint32(12, cdSize, true);
|
||||
ev.setUint32(16, offset, true);
|
||||
|
||||
return new Blob([...parts, ...centralDir, eocd], { type: 'application/zip' });
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>DrHider — обфускация документов</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||
<script src="/static/zip.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--brand-primary: #2563eb;
|
||||
@@ -151,48 +152,6 @@ function ts() {
|
||||
|
||||
function ss(idx, h) { const e = document.getElementById('st-' + idx); if (e) e.innerHTML = h; }
|
||||
|
||||
// ═══════════ Нативный ZIP (без внешних библиотек) ═══════════
|
||||
|
||||
async function unzip(zb) {
|
||||
const buf = await zb.arrayBuffer(); const dv = new DataView(buf);
|
||||
const files = {}; let pos = 0;
|
||||
while (pos < buf.byteLength - 4) {
|
||||
const sig = dv.getUint32(pos, true);
|
||||
if (sig === 0x04034b50) {
|
||||
const start = pos; // Начало local file header
|
||||
pos += 26;
|
||||
const nl = dv.getUint16(pos, true); pos += 2;
|
||||
const el = dv.getUint16(pos, true); pos += 2;
|
||||
const nm = new TextDecoder().decode(new Uint8Array(buf, pos, nl)); pos += nl + el;
|
||||
const cs = dv.getUint32(start + 18, true); // compressed size at offset 18
|
||||
files[nm] = new Blob([new Uint8Array(buf, pos, cs)]); pos += cs;
|
||||
} else break;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
async function buildZip(entries) {
|
||||
const parts = []; const cd = []; let off = 0;
|
||||
for (const e of entries) {
|
||||
const d = new Uint8Array(await e.data.arrayBuffer());
|
||||
const nm = new TextEncoder().encode(e.name);
|
||||
const lh = new Uint8Array(30 + nm.length); const lv = new DataView(lh.buffer);
|
||||
lv.setUint32(0, 0x04034b50, true); lv.setUint16(6, 0x800, true);
|
||||
lv.setUint32(18, d.length, true); lv.setUint32(22, d.length, true);
|
||||
lv.setUint16(26, nm.length, true); lh.set(nm, 30); parts.push(lh, d);
|
||||
const ce = new Uint8Array(46 + nm.length); const cv = new DataView(ce.buffer);
|
||||
cv.setUint32(0, 0x02014b50, true); cv.setUint16(8, 0x800, true);
|
||||
cv.setUint32(20, d.length, true); cv.setUint32(24, d.length, true);
|
||||
cv.setUint16(28, nm.length, true); cv.setUint32(42, off, true); ce.set(nm, 46); cd.push(ce);
|
||||
off += 30 + nm.length + d.length;
|
||||
}
|
||||
const cs = cd.reduce((s, c) => s + c.length, 0);
|
||||
const eo = new Uint8Array(22); const ev = new DataView(eo.buffer);
|
||||
ev.setUint32(0, 0x06054b50, true); ev.setUint16(8, entries.length, true);
|
||||
ev.setUint16(10, entries.length, true); ev.setUint32(12, cs, true); ev.setUint32(16, off, true);
|
||||
return new Blob([...parts, ...cd, eo], { type: 'application/zip' });
|
||||
}
|
||||
|
||||
async function uploadFiles() {
|
||||
if (sf.length === 0) return;
|
||||
ub.disabled = true;
|
||||
|
||||
Reference in New Issue
Block a user