@echo off
rem ===========================================================================
rem  Track of the Day 8 - Beta einrichten
rem
rem  Doppelklick genuegt. Verlangt Windows Administratorrechte, dann nur fuer
rem  den Eintrag des Zertifikats - beim naechsten Mal entfaellt das.
rem  Am Ende steht auf dem Desktop ein Protokoll.
rem
rem  Nur pruefen, nichts aendern:  TD8-Beta-installieren.cmd pruefen
rem ---------------------------------------------------------------------------
rem  Diese Datei ist nur die Startrampe. Der eigentliche Ablauf ist das
rem  PowerShell-Skript weiter unten (TD8-Beta-einrichten.ps1); es wird von hier
rem  herausgeschnitten und gestartet. Die Batch-Zeilen enden bei "exit /b" -
rem  alles danach sieht der Befehlsprozessor nie.
rem ===========================================================================

rem ★★★ SCHLUSS-BACKSLASH WEG, BEVOR der Pfad weitergereicht wird.
rem  %~dp0 endet immer mit einem Backslash. Enthaelt der Ordner Leerzeichen -
rem  etwa "C:\Workspaces\Track of the Day\..." -, setzt PowerShell beim Aufruf
rem  des naechsten Prozesses Anfuehrungszeichen darum, und der Backslash maskiert
rem  dann das schliessende Anfuehrungszeichen: Ordner und Modus verschmelzen zu
rem  EINEM Wert, der Pruefmodus ging verloren und Test-Path warf "Illegales
rem  Zeichen im Pfad". Gemessen am 08.09.2026.
set "TDORDNER=%~dp0"
if "%TDORDNER:~-1%"=="\" set "TDORDNER=%TDORDNER:~0,-1%"

powershell -NoProfile -ExecutionPolicy Bypass -Command "$c = Get-Content -Raw -LiteralPath '%~f0' -Encoding UTF8; $m = '#--- POWERSHELL' + ' AB HIER ---'; $i = $c.LastIndexOf($m); if ($i -lt 0) { Write-Host 'Die Datei ist unvollstaendig - bitte neu herunterladen.' -ForegroundColor Red; pause; exit 1 }; $f = Join-Path $env:TEMP 'TD8-Beta-einrichten.ps1'; Set-Content -LiteralPath $f -Value $c.Substring($i) -Encoding UTF8; & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $f '%TDORDNER%' '%1'"

exit /b
#--- POWERSHELL AB HIER ---
<#
============================================================================
 Track of the Day 8 - Beta einrichten
 ---------------------------------------------------------------------------
 Traegt das Entwicklerzertifikat an der RICHTIGEN Stelle ein und installiert
 anschliessend das MSIX-Paket.

 Aufruf:
   Rechtsklick -> "Mit PowerShell ausfuehren"      (holt sich Adminrechte selbst)
   oder:  .\TD8-Beta-einrichten.ps1
   nur pruefen, nichts aendern:  .\TD8-Beta-einrichten.ps1 -Modus pruefen

 ★★★ WARUM ES DIESES SKRIPT GIBT: Der Zertifikats-Assistent von Windows steht
 standardmaessig auf "Aktueller Benutzer". Genau dort nuetzt das Zertifikat fuer
 ein MSIX-Paket aber NICHTS - Windows sucht es unter "Lokaler Computer" im
 Speicher "Vertrauenswuerdige Personen". Wer den Assistenten mit den Vorgaben
 durchklickt, bekommt deshalb zwangslaeufig "Das Zertifikat wird nicht erkannt".

 ★ Sicherheitsnetz: Eingetragen wird NUR ein Zertifikat mit genau dem unten
   festgeschriebenen Fingerabdruck, und installiert wird das Paket nur, wenn
   seine Signatur gueltig ist UND von genau diesem Zertifikat stammt. Ein
   Skript, das blind Zertifikate in den Vertrauensspeicher legt, waere genau
   das, wovor man Nutzer warnt.
============================================================================
#>
param(
    # Ordner, in dem .cer/.msix bereits liegen koennten (die .cmd reicht ihn durch,
    # weil das Skript von dort nach %TEMP% ausgepackt wird).
    [string]$Ordner,
    # "pruefen" = nur berichten, nichts veraendern.
    [string]$Modus
)

# ★ BEIDES ist noetig, sonst steht dort "Vertrauenswürdige" als "VertrauenswÃ¼rdige":
#   die Zeichentabelle des Fensters auf UTF-8 stellen UND .NET UTF-8 schreiben lassen.
& chcp.com 65001 | Out-Null
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$ErrorActionPreference = 'Stop'

$NurPruefen    = ($Modus -eq 'pruefen')
$Fingerabdruck = '677866FB5CA8AA468CF69943C9030630F95DA09A'
$Basis         = 'https://tdvalhalla.blob.core.windows.net/apps/td-beta'
$PaketName     = 'TrackOfTheDay-8.0.0-Beta-x64.msix'

# ★★★ %~dp0 endet IMMER mit einem Backslash. Ein Backslash unmittelbar vor dem
#   schliessenden Anfuehrungszeichen maskiert dieses beim Weiterreichen an einen neuen
#   Prozess - im erhoehten Lauf kam der Ordner dann MIT Anfuehrungszeichen an, und
#   Test-Path warf "Illegales Zeichen im Pfad" (Nutzerbefund 08.09.2026, DELL XPS17).
#   Deshalb: Anfuehrungszeichen und Schluss-Backslashes weg, bevor der Wert benutzt
#   ODER weitergereicht wird.
if ($Ordner) { $Ordner = $Ordner.Trim().Trim('"').TrimEnd('\') }
if (-not $Ordner) { $Ordner = $PSScriptRoot }

$istAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
            ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

# ── Selbst erhöhen ──────────────────────────────────────────────────────────
# ★ Ohne Administratorrechte lässt sich nichts in den Speicher des lokalen
#   Rechners eintragen. Statt den Nutzer zu belehren, holt sich das Skript die
#   Rechte selbst - ein Klick auf "Ja" genügt.
# ★★★ NUR wenn es wirklich noetig ist, naemlich zum Eintragen des Zertifikats.
#   Add-AppxPackage braucht KEINE Administratorrechte. Wer das Zertifikat schon hat -
#   also jeder Tester ab dem zweiten Mal - soll fuer ein Update nicht wieder eine
#   Rueckfrage von Windows wegklicken muessen. Gerufen wird das erst weiter unten,
#   wenn die Pruefung ergeben hat, dass das Zertifikat fehlt.
function ErhoeheUndBeende {
    $eigenerPfad = if ($PSCommandPath) { $PSCommandPath } else { $MyInvocation.MyCommand.Path }
    Write-Host ""
    Write-Host "Für den Eintrag des Zertifikats werden Administratorrechte gebraucht." -ForegroundColor Yellow
    Write-Host "Windows fragt gleich nach - bitte mit 'Ja' bestätigen." -ForegroundColor Yellow
    # ★ $Ordner ist oben schon von Anfuehrungszeichen und Schluss-Backslash befreit -
    #   sonst maskiert der Backslash hier das schliessende Anfuehrungszeichen.
    $argumente = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-NoExit', '-File', ('"' + $eigenerPfad + '"'), ('"' + $Ordner + '"'))
    if ($Modus) { $argumente += ('"' + $Modus + '"') }
    try {
        Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList $argumente
    } catch {
        Write-Host "Abgebrochen - ohne Administratorrechte lässt sich das Zertifikat nicht eintragen." -ForegroundColor Red
        Read-Host "Eingabetaste zum Schließen"
    }
}

$desktop = [Environment]::GetFolderPath('Desktop')
if (-not (Test-Path $desktop)) { $desktop = $env:TEMP }
$Protokoll = Join-Path $desktop 'TD8-Beta-Einrichtung.txt'
"Track of the Day 8 - Beta einrichten, $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" |
    Set-Content -LiteralPath $Protokoll -Encoding UTF8

function Sag($text, $farbe = 'Gray') {
    Write-Host $text -ForegroundColor $farbe
    Add-Content -LiteralPath $Protokoll -Value $text -Encoding UTF8
}
function Titel($text) { Sag ""; Sag $text 'Cyan' }

Titel "Track of the Day 8 - Beta einrichten"
Sag   "Protokoll: $Protokoll"
if ($NurPruefen) { Sag "PRÜFMODUS - es wird nichts verändert." 'Yellow' }

$fehler = @()

# ── 1. Voraussetzungen ──────────────────────────────────────────────────────
Titel "1) Voraussetzungen"
$os = Get-CimInstance Win32_OperatingSystem
Sag "   Windows .................. $($os.Caption) $($os.Version)"
Sag "   Angemeldet als ........... $env:USERNAME"
Sag "   Administratorrechte ...... $(if ($istAdmin) { 'ja' } else { 'nein' })"

$appInst = Get-AppxPackage -Name Microsoft.DesktopAppInstaller -ErrorAction SilentlyContinue
Sag "   App-Installer ............ $(if ($appInst) { $appInst.Version } else { 'FEHLT' })"
if (-not $appInst) {
    $fehler += "Der 'App Installer' von Microsoft fehlt. Er kommt aus dem Microsoft Store (dort nach 'App Installer' suchen)."
}

# ── 2. Zertifikat ───────────────────────────────────────────────────────────
Titel "2) Zertifikat"

# ★★★ NICHT ueber das Cert:-Laufwerk pruefen. Gemessen am 08.09.2026: Wird
#   PowerShell aus cmd.exe heraus gestartet - also genau ueber die Startrampe -,
#   liefert der Cert:-Anbieter fuer JEDEN Speicher 0 Treffer, obwohl die
#   Zertifikate da sind (Root meldete 0 statt 76). Die Store-Schnittstelle
#   liefert im selben Lauf korrekt 7. Eine Pruefung, die im Ernstfall luegt,
#   ist schlimmer als gar keine - deshalb hier ausschliesslich die Store-API.
# ★★★ SIGNATUR OHNE Get-AuthenticodeSignature. Das Cmdlet steckt im Modul
#   Microsoft.PowerShell.Security, und genau dieses Modul laesst sich auf manchen
#   Rechnern in diesem Startweg NICHT laden - gemessen am 08.09.2026:
#   "Fehler beim Laden der Datendatei des erweiterten Typs ... AuditToString ist
#   bereits vorhanden". Dasselbe Modul stellt auch das Cert:-Laufwerk; deshalb
#   meldete das vorher 0 Zertifikate, obwohl welche da waren.
#   Der Signierer wird darum direkt aus AppxSignature.p7x im Paket gelesen:
#   4 Byte Kennung "PKCX", dahinter das PKCS#7.
# ★ Was hier NICHT geprueft wird, ist die Vertrauenskette - das macht Windows beim
#   Installieren selbst und lehnt sonst mit 0x800B0109 ab. Hier geht es nur um die
#   Frage "stammt das Paket von UNSEREM Zertifikat".
function SignaturFingerabdruck($msixPfad) {
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    Add-Type -AssemblyName System.Security
    $zip = [System.IO.Compression.ZipFile]::OpenRead($msixPfad)
    try {
        $eintrag = $zip.Entries | Where-Object { $_.FullName -eq 'AppxSignature.p7x' }
        if (-not $eintrag) { throw "Das Paket enthält keine Signatur (AppxSignature.p7x fehlt)." }
        $ms = New-Object System.IO.MemoryStream
        $strom = $eintrag.Open(); $strom.CopyTo($ms); $strom.Close()
        $roh = $ms.ToArray()
        if ($roh.Length -lt 5) { throw "Die Signatur im Paket ist unbrauchbar." }
        $cms = New-Object System.Security.Cryptography.Pkcs.SignedCms
        $cms.Decode($roh[4..($roh.Length - 1)])
        return $cms.SignerInfos[0].Certificate.Thumbprint
    } finally {
        $zip.Dispose()
    }
}

# ★ Ein "liegt daneben"-Pfad darf nie werfen: Der Ordner kommt von aussen.
function LiegtDatei($ordner, $name) {
    try {
        if (-not $ordner) { return $null }
        $pfad = Join-Path $ordner $name
        if (Test-Path -LiteralPath $pfad) { return $pfad }
    } catch { }
    return $null
}

function Liegt($speicher, $ort) {
    try {
        $s = New-Object System.Security.Cryptography.X509Certificates.X509Store($speicher, $ort)
        $s.Open('ReadOnly')
        $treffer = [bool]($s.Certificates | Where-Object { $_.Thumbprint -eq $Fingerabdruck })
        $s.Close()
        return $treffer
    } catch {
        return $false
    }
}

$imRechner  = (Liegt 'TrustedPeople' 'LocalMachine')
$imStamm    = (Liegt 'Root'          'LocalMachine')
$richtig    = $imRechner -or $imStamm
$falsch     = (Liegt 'TrustedPeople' 'CurrentUser') -or (Liegt 'Root' 'CurrentUser')

Sag "   Lokaler Computer / Vertrauenswürdige Personen ... $(if ($imRechner) { 'ja' } else { 'nein' })"
Sag "   Lokaler Computer / Vertrauenswürdige Stämme ..... $(if ($imStamm) { 'ja' } else { 'nein' })"
Sag "   Aktueller Benutzer (hilft NICHT) ................ $(if ($falsch) { 'ja' } else { 'nein' })"

if ($richtig) {
    Sag "   -> Das Zertifikat liegt richtig." 'Green'
} elseif ($NurPruefen) {
    Sag "   -> Das Zertifikat fehlt im richtigen Speicher." 'Yellow'
    if ($falsch) { Sag "      (Es liegt beim aktuellen Benutzer - genau die Falle des Assistenten.)" 'Yellow' }
} elseif (-not $istAdmin) {
    Sag "   -> Das Zertifikat fehlt im richtigen Speicher; dafür braucht es Administratorrechte." 'Yellow'
    ErhoeheUndBeende
    return
} else {
    try {
        $cer = Join-Path $env:TEMP 'TrackOfTheDay-Beta.cer'
        $daneben = LiegtDatei $Ordner 'TrackOfTheDay-Beta.cer'
        if ($daneben) {
            Copy-Item -LiteralPath $daneben -Destination $cer -Force
            Sag "   Zertifikat liegt neben dem Skript - wird von dort genommen."
        } else {
            Sag "   Zertifikat wird geladen ..."
            [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
            (New-Object Net.WebClient).DownloadFile("$Basis/TrackOfTheDay-Beta.cer", $cer)
        }

        # ★ Erst prüfen, dann eintragen.
        $geladen = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cer
        Sag "   Fingerabdruck ............ $($geladen.Thumbprint)"
        if ($geladen.Thumbprint -ne $Fingerabdruck) {
            throw "Der Fingerabdruck stimmt NICHT mit dem erwarteten überein. Es wird nichts eingetragen."
        }

        # ★ Auch das Eintragen ueber die Store-API - Import-Certificate haengt am
        #   Cert:-Anbieter und wuerde sich denselben Ausfall einfangen.
        $zielSpeicher = New-Object System.Security.Cryptography.X509Certificates.X509Store('TrustedPeople', 'LocalMachine')
        $zielSpeicher.Open('ReadWrite')
        $zielSpeicher.Add($geladen)
        $zielSpeicher.Close()
        Sag "   -> Eingetragen unter: Lokaler Computer / Vertrauenswürdige Personen" 'Green'
        $richtig = $true
    } catch {
        Sag "   -> Fehlgeschlagen: $($_.Exception.Message)" 'Red'
        $fehler += "Das Zertifikat konnte nicht eingetragen werden: $($_.Exception.Message)"
    }
}

# ── 3. Paket ────────────────────────────────────────────────────────────────
Titel "3) Programmpaket"
$vorher = Get-AppxPackage -Name "*Trackoftheday.Beta*" -ErrorAction SilentlyContinue
Sag "   Bereits installiert ...... $(if ($vorher) { $vorher.Version } else { 'nein' })"

if ($NurPruefen) {
    Sag "   (Prüfmodus - es wird nichts installiert.)" 'Yellow'
} elseif (-not $richtig) {
    Sag "   Übersprungen - ohne eingetragenes Zertifikat lehnt Windows die Installation ab." 'Yellow'
} else {
    try {
        $msix = Join-Path $env:TEMP $PaketName
        $daneben = LiegtDatei $Ordner $PaketName
        if ($daneben) {
            $msix = $daneben
            Sag "   Paket liegt neben dem Skript - wird von dort genommen."
        } else {
            Sag "   Paket wird geladen (rund 190 MB, das dauert je nach Leitung einige Minuten) ..."
            [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
            (New-Object Net.WebClient).DownloadFile("$Basis/$PaketName", $msix)
        }
        Sag "   Größe .................... $('{0:N0}' -f (Get-Item $msix).Length) Bytes"

        # ★ Signatur prüfen, BEVOR installiert wird - gegen genau das Zertifikat,
        #   das oben eingetragen wurde.
        $sigDaumen = SignaturFingerabdruck $msix
        Sag "   Signiert von ............. $sigDaumen"
        if ($sigDaumen -ne $Fingerabdruck) {
            throw "Das Paket ist nicht mit dem erwarteten Zertifikat signiert."
        }

        Sag "   Wird installiert ..."
        Add-AppxPackage -Path $msix -ForceUpdateFromAnyVersion
        Sag "   -> Installiert." 'Green'
    } catch {
        $meldung = $_.Exception.Message
        Sag "   -> Fehlgeschlagen: $meldung" 'Red'
        if ($meldung -match '0x800B0109') {
            $fehler += "Windows vertraut dem Zertifikat nicht (0x800B0109). Rechner neu starten und das Skript noch einmal ausführen."
        } elseif ($meldung -match '0x80073CFF') {
            $fehler += "Windows lehnt das Paket ab (0x80073CFF) - meist fehlt das Zertifikat im Speicher 'Lokaler Computer'."
        } else {
            $fehler += "Die Installation ist fehlgeschlagen: $meldung"
        }
    }
}

# ── 4. Ergebnis ─────────────────────────────────────────────────────────────
Titel "4) Ergebnis"
$nachher = Get-AppxPackage -Name "*Trackoftheday.Beta*" -ErrorAction SilentlyContinue
$unveraendert = $nachher -and $vorher -and ($nachher.Version -eq $vorher.Version)
if ($nachher -and -not $unveraendert) {
    Sag "   Track of the Day 8 Beta $($nachher.Version) ist installiert." 'Green'
    Sag "   Zu finden im Startmenü unter 'Track of the Day 8 Beta'."
} elseif ($nachher -and $NurPruefen) {
    Sag "   Track of the Day 8 Beta $($nachher.Version) ist installiert." 'Green'
    Sag "   Zu finden im Startmenü unter 'Track of the Day 8 Beta'."
} elseif ($nachher) {
    # ★ Nicht gruen faerben: Es liegt noch die BISHERIGE Fassung da, die Installation
    #   hat nichts bewirkt. Sonst liest sich ein Fehlschlag wie ein Erfolg.
    Sag "   Unverändert - es liegt weiterhin die bisherige Fassung $($nachher.Version) vor." 'Yellow'
} else {
    Sag "   Es ist nichts installiert." 'Yellow'
}

if ($fehler.Count) {
    Titel "Es hat nicht geklappt - das ist zu tun:"
    foreach ($f in $fehler) { Sag "   * $f" 'Yellow' }
    Sag ""
    Sag "   Bitte die Datei '$Protokoll' an den Support schicken." 'Yellow'
} elseif (-not $NurPruefen -and $nachher) {
    Sag ""
    Sag "   Fertig - viel Spaß beim Testen!" 'Green'
}

Sag ""
Sag "Protokoll liegt hier: $Protokoll"
Write-Host ""
Write-Host "Dieses Fenster kann geschlossen werden." -ForegroundColor DarkGray
