param( [Parameter(Mandatory = $true)][int]$TemplateId, [string]$ConfigPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'config.ini'), [string]$OutputRoot ) $ErrorActionPreference = 'Stop' . (Join-Path $PSScriptRoot 'template-common.ps1') if (-not $OutputRoot) { $OutputRoot = Join-Path (Split-Path -Parent $ConfigPath) 'temp\previews' } if ($TemplateId -lt 1) { throw 'TemplateId must be positive' } $context = Get-MailContext -ConfigPath $ConfigPath $connection = Open-MailDatabase $context try { $command = $connection.CreateCommand() $command.CommandText = 'SELECT [HtmlContent],[ContentHash],[ValidationStatus] FROM [TemplateVersion] WHERE [Id]=?' $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer) $command.Parameters[0].Value = $TemplateId $reader = $command.ExecuteReader() try { if (-not $reader.Read()) { throw "Template ID $TemplateId was not found" } $html = [string]$reader.GetValue(0) $hash = [string]$reader.GetValue(1) if ([string]$reader.GetValue(2) -ne 'validated' -or (Get-HashHex ([System.Text.Encoding]::UTF8.GetBytes($html))) -ne $hash) { throw 'Template validation or content hash failed' } } finally { $reader.Close() } $document = [xml]$html Assert-TemplateNode $document.DocumentElement $previewPath = Join-Path $OutputRoot ([guid]::NewGuid().ToString('N')) $null = New-Item -ItemType Directory -Path $previewPath -Force $command = $connection.CreateCommand() $command.CommandText = 'SELECT [OriginalRelativePath],[ContentType],[ContentHash],[SnapshotBytes],[ContentId] FROM [TemplateImage] WHERE [TemplateVersionId]=? ORDER BY [Id]' $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer) $command.Parameters[0].Value = $TemplateId $reader = $command.ExecuteReader() try { $index = 0 while ($reader.Read()) { $index++ $bytes = [byte[]]$reader.GetValue(3) if ((Get-HashHex $bytes) -ne [string]$reader.GetValue(2)) { throw 'Stored image hash failed' } $type = [string]$reader.GetValue(1) $ext = switch ($type) { 'image/png' { 'png' } 'image/jpeg' { 'jpg' } 'image/gif' { 'gif' } default { throw 'Unsupported stored image type' } } $name = "image-$index.$ext" [System.IO.File]::WriteAllBytes((Join-Path $previewPath $name), $bytes) $cid = 'cid:' + [string]$reader.GetValue(4) $found = $false foreach ($node in $document.GetElementsByTagName('img')) { if ($node.GetAttribute('src') -eq $cid) { $node.SetAttribute('src', $name); $found = $true } } if (-not $found) { throw 'Stored image is not referenced by the template' } } } finally { $reader.Close() } foreach ($node in $document.GetElementsByTagName('img')) { if ($node.GetAttribute('src').StartsWith('cid:')) { throw 'Template image snapshot is missing' } } $file = Join-Path $previewPath 'preview.html' [System.IO.File]::WriteAllText($file, $document.OuterXml, (New-Object System.Text.UTF8Encoding($false))) Write-Output $file } finally { $connection.Dispose() }