fix(nextcloud-files): WR-08/IN-06 Auswahl-ZIP per POST, Downloads mit Vorabpruefung

- WR-08: POST download/zip mit den Namen im Koerper (JSON oder unsichtbares Formular mit
  names als JSON-Text) statt im Query-String; hoechstens 1000 Namen; die Weboberflaeche
  laedt per Formular in einen unsichtbaren Rahmen (Strom direkt auf die Platte, Cookie und
  Content-Disposition bleiben); alles ausgewaehlt -> ganzer Ordner als ZIP
- Nextcloud nimmt die Auswahl nur in ihrer Adresse an (gemessen: Apache lehnt ueber 8190
  Zeichen mit 414 ab); Tessera prueft die Laenge und antwortet vorher mit 413
  selectionTooLarge, die Weboberflaeche meldet es schon vor dem Absenden
- IN-06: Vorabpruefung (check=1 bzw. check: true, ein PROPFIND Depth 0) vor jedem Download;
  Fehler stehen in der Statuszeile, connectionExpired fuehrt zum Anmeldebildschirm; auch der
  Klick auf einen Dateinamen geht ueber die Pruefung
- e2e-transfer auf POST umgestellt (JSON, Formular ueber /api-proxy, 300 Namen, Pruefung),
  Anwenderhandbuch

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 22:50:24 +02:00
parent 486819f80b
commit 8679668cac
16 changed files with 499 additions and 56 deletions
@@ -176,19 +176,45 @@ e2e_expect 200 "$code" "Ordner als ZIP"
grep -qi "^content-disposition: attachment; filename=\"$FNAME.zip\"" "$E2E_TMP/zip.hdr" || e2e_fail "ZIP-Name: $(grep -i content-disposition "$E2E_TMP/zip.hdr")"
unzip -l "$E2E_TMP/folder.zip" | grep -q 'gross.bin' || e2e_fail "Ordner-ZIP enthaelt gross.bin nicht"
code=$(curl -s -D "$E2E_TMP/zip2.hdr" -o "$E2E_TMP/sel.zip" -w '%{http_code}' -b "$ADMIN" -G --data-urlencode "dir=$F" --data-urlencode "name=$UMLAUT_NAME" "$FILES/download/zip")
e2e_expect 200 "$code" "Auswahl als ZIP"
# Auswahl-ZIP per POST (WR-08): die Namen im Koerper, als JSON ...
jlist() { python3 -I -c 'import json,sys;print(json.dumps({"dir":sys.argv[1],"names":sys.argv[2:]}))' "$@"; }
code=$(curl -s -D "$E2E_TMP/zip2.hdr" -o "$E2E_TMP/sel.zip" -w '%{http_code}' -b "$ADMIN" -X POST -H 'Content-Type: application/json' \
--data-binary "$(jlist "$F" "$UMLAUT_NAME")" "$FILES/download/zip")
e2e_expect 200 "$code" "Auswahl als ZIP (JSON)"
[ "$(head -c 2 "$E2E_TMP/sel.zip")" = "PK" ] || e2e_fail "Auswahl-ZIP beginnt nicht mit PK"
grep -qi "^content-disposition: attachment; filename=\"$FNAME.zip\"" "$E2E_TMP/zip2.hdr" || e2e_fail "Auswahl-ZIP-Name: $(grep -i content-disposition "$E2E_TMP/zip2.hdr")"
unzip -Z1 "$E2E_TMP/sel.zip" > "$E2E_TMP/sel.list"
[ "$(wc -l < "$E2E_TMP/sel.list")" = "1" ] || e2e_fail "Auswahl-ZIP enthaelt nicht genau einen Eintrag: $(cat "$E2E_TMP/sel.list")"
[ "$(cat "$E2E_TMP/sel.list")" = "$UMLAUT_NAME" ] || e2e_fail "Auswahl-ZIP: falscher Eintrag: $(cat "$E2E_TMP/sel.list")"
# zwei Namen (wiederholter Schluessel)
code=$(curl -s -o "$E2E_TMP/sel2.zip" -w '%{http_code}' -b "$ADMIN" -G --data-urlencode "dir=$F" --data-urlencode "name=$UMLAUT_NAME" --data-urlencode "name=gross.bin" "$FILES/download/zip")
e2e_expect 200 "$code" "Auswahl mit zwei Namen"
# ... und wie das unsichtbare Formular der Weboberflaeche (urlencoded, names als JSON-Text), ueber /api-proxy
NAMES_JSON=$(python3 -I -c 'import json,sys;print(json.dumps(sys.argv[1:]))' "$UMLAUT_NAME" gross.bin)
code=$(curl -s -o "$E2E_TMP/sel2.zip" -w '%{http_code}' -b "$ADMIN" -X POST --data-urlencode "dir=$F" --data-urlencode "names=$NAMES_JSON" "$VIA_WEB/download/zip")
e2e_expect 200 "$code" "Auswahl mit zwei Namen (Formular, via Web)"
[ "$(unzip -Z1 "$E2E_TMP/sel2.zip" | wc -l)" = "2" ] || e2e_fail "Auswahl-ZIP mit zwei Namen hat nicht zwei Eintraege"
code=$(curl -s -o "$E2E_TMP/body.out" -w '%{http_code}' -b "$ADMIN" -G --data-urlencode "dir=$F" --data-urlencode "name=.." "$FILES/download/zip")
code=$(curl -s -o "$E2E_TMP/body.out" -w '%{http_code}' -b "$ADMIN" -X POST -H 'Content-Type: application/json' \
--data-binary "$(jlist "$F" "..")" "$FILES/download/zip")
e2e_expect 400 "$code" "ZIP mit Name .."
e2e_contains "$E2E_TMP/body.out" '"code":"invalidPath"' "ZIP-Name ..: invalidPath"
# 300 lange Namen kommen im Koerper durch Proxy und API (frueher 431/414 an Tessera). Nextcloud nimmt
# die Auswahl aber nur in ihrer Adresse an (Apache: hoechstens 8190 Zeichen) -> klare 413 selectionTooLarge
# von Tessera, bevor Nextcloud gefragt wird.
MANY=$(python3 -I -c 'import json,sys;print(json.dumps({"dir":sys.argv[1],"names":[sys.argv[2]]+["fehlt-%03d-%s"%(i,"x"*60) for i in range(299)]}))' "$F" "$UMLAUT_NAME")
code=$(curl -s -o "$E2E_TMP/body.out" -w '%{http_code}' -b "$ADMIN" -X POST -H 'Content-Type: application/json' --data-binary "$MANY" "$VIA_WEB/download/zip")
e2e_expect 413 "$code" "300 lange Namen"
e2e_contains "$E2E_TMP/body.out" '"code":"selectionTooLarge"' "300 lange Namen: selectionTooLarge"
# Die alte Adressform gibt es nicht mehr
code=$(curl -s -o /dev/null -w '%{http_code}' -b "$ADMIN" -G --data-urlencode "dir=$F" --data-urlencode "name=gross.bin" "$FILES/download/zip")
e2e_expect 404 "$code" "GET download/zip gibt es nicht mehr"
# Vorabpruefung (IN-06): { ok: true } oder der Fehler, ohne Datenstrom
code=$(e2e_status "$ADMIN" GET "$FILES/download?path=$(enc "$F/gross.bin")&check=1")
e2e_expect 200 "$code" "Vorabpruefung Datei"
e2e_contains "$E2E_TMP/body.out" '"ok":true' "Vorabpruefung Datei ok"
code=$(e2e_status "$ADMIN" GET "$FILES/download?path=$(enc "$F/gibt-es-nicht.bin")&check=1")
e2e_expect 404 "$code" "Vorabpruefung fehlende Datei"
code=$(e2e_status "$ADMIN" POST "$FILES/download/zip" "$(jlist "$F" gross.bin | python3 -I -c 'import json,sys;d=json.load(sys.stdin);d["check"]=True;print(json.dumps(d))')")
e2e_expect 200 "$code" "Vorabpruefung Auswahl-ZIP"
e2e_contains "$E2E_TMP/body.out" '"ok":true' "Vorabpruefung Auswahl-ZIP ok"
# --- 7. Abbrechen raeumt auf -----------------------------------------------------------------------------------------
code=$(e2e_status "$ADMIN" POST "$FILES/uploads" "{\"path\":\"$F/abbruch.bin\",\"size\":$BIG_SIZE}")