Files
felhom.eu/documentation/audits/fixes-2026-10-21/tools/nc_compare.py
T

52 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""nc_compare.py — every file under Ödön's „Csalad" folder through Nextcloud's own WebDAV door (the box's LAN address with
the Host header; TLS not verified, the rehearsal's tester4.test has no DNS), sha256 against the rehearsal's A5 manifest.
Env: REHEARSAL_2026_10_20_ODON_PW (under `sec run`). Prints counts and up to 15 differing names; never a secret."""
import base64, hashlib, http.client, os, ssl, sys, unicodedata, urllib.parse, xml.etree.ElementTree as ET
IP, HOST = "192.168.0.139", "cloud.tester4.test"
MAN = "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/rehearsal-2026-10-20/A5-data/MANIFEST.tsv"
auth = "Basic " + base64.b64encode(("odon:" + os.environ["REHEARSAL_2026_10_20_ODON_PW"]).encode()).decode()
ctx = ssl._create_unverified_context()
def req(method, path, body=None, extra=None):
c = http.client.HTTPSConnection(IP, 443, context=ctx, timeout=300)
h = {"Host": HOST, "Authorization": auth}; h.update(extra or {})
c.request(method, path, body=body, headers=h); r = c.getresponse(); data = r.read(); c.close()
return r.status, data
base = "/remote.php/dav/files/odon"
files = {}
def walk(path):
st, data = req("PROPFIND", urllib.parse.quote(base + path), b'<?xml version="1.0"?><d:propfind xmlns:d="DAV:"><d:prop><d:resourcetype/></d:prop></d:propfind>', {"Depth": "1"})
if st != 207:
print("PROPFIND", path, st); return
for resp in ET.fromstring(data).findall("{DAV:}response"):
href = urllib.parse.unquote(resp.find("{DAV:}href").text)
rel = href[len(base):]
if rel.rstrip("/") == path.rstrip("/"):
continue
if resp.find(".//{DAV:}collection") is not None:
walk(rel)
else:
files[rel] = href
walk("/Csalad/")
want = {}
for line in open(MAN, encoding="utf-8"):
p = line.rstrip("\n").split("\t")
if p[0].startswith("nextcloud/Csalad/"):
want[unicodedata.normalize("NFC", "/" + p[0][len("nextcloud/"):])] = p[2]
same, differ, extra = 0, [], []
got = {}
for rel, href in files.items():
st, data = req("GET", urllib.parse.quote(href))
got[unicodedata.normalize("NFC", rel)] = hashlib.sha256(data).hexdigest() if st == 200 else f"HTTP{st}"
for k, h in want.items():
if got.get(k) == h:
same += 1
else:
differ.append(("MISSING" if k not in got else "DIFFERS") + " " + k)
extra = [k for k in got if k not in want]
print(f"nextcloud Csalad: manifest {len(want)} files; on the box {len(got)}; byte-identical {same}; missing/different {len(differ)}; extra {len(extra)}")
for d in differ[:15]:
print(" ", d)
for e in extra[:8]:
print(" EXTRA", e)