956 lines
45 KiB
HTML
956 lines
45 KiB
HTML
{{template "base.html" .}}
|
|
|
|
{{define "title"}}Email Header Analyzer{{end}}
|
|
|
|
{{define "head"}}
|
|
<script src="https://unpkg.com/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
|
<script src="https://unpkg.com/jspdf@2.5.1/dist/jspdf.umd.min.js"></script>
|
|
<script src="https://unpkg.com/html2pdf.js@0.10.1/dist/html2pdf.bundle.min.js"></script>
|
|
{{end}}
|
|
|
|
{{define "content"}}
|
|
<div class="container mx-auto px-4 py-8 max-w-6xl">
|
|
<div class="text-center mb-8">
|
|
<a href="/analyze" class="inline-block">
|
|
<h1 class="text-2xl md:text-3xl font-bold text-gray-100 hover:text-blue-400 transition-colors cursor-pointer mb-4">
|
|
📧 Email Header Analyzer
|
|
</h1>
|
|
</a>
|
|
</div>
|
|
|
|
{{if not .From}}
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700 max-w-4xl mx-auto">
|
|
<form method="POST" class="space-y-4">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-200 mb-2">📝 Email Headers:</label>
|
|
<textarea name="headers"
|
|
placeholder="Paste email headers here..."
|
|
required
|
|
rows="12"
|
|
class="w-full p-4 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 font-mono text-sm placeholder-gray-400 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none resize-y"></textarea>
|
|
</div>
|
|
<div class="text-center">
|
|
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-8 py-3 rounded-lg transition-colors">
|
|
🔍 Analyze Headers
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .From}}
|
|
<!-- Hidden form for refresh functionality -->
|
|
<form id="refreshForm" method="POST" style="display: none;">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
<textarea name="headers" style="display: none;">{{.OriginalHeaders}}</textarea>
|
|
</form>
|
|
|
|
<div id="report" class="space-y-6">
|
|
<!-- Sender Identification -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">👤 Sender Identification</h2>
|
|
<div class="grid grid-cols-2 gap-6" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem;">
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Envelope Sender (Return-Path):</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.EnvelopeSender}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">From Domain:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.FromDomain}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Sending Server:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.SendingServer}}</p>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<h3 class="text-lg font-semibold text-gray-200 mb-3">🔒 Security Status</h3>
|
|
<div class="flex flex-wrap gap-2">
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .SPFPass}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
SPF {{if .SPFPass}}✓{{else}}✗{{end}}
|
|
</span>
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .DMARCPass}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
DMARC {{if .DMARCPass}}✓{{else}}✗{{end}}
|
|
</span>
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .DKIMPass}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
DKIM {{if .DKIMPass}}✓{{else}}✗{{end}}
|
|
</span>
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .Encrypted}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
Encrypted {{if .Encrypted}}✓{{else}}✗{{end}}
|
|
</span>
|
|
{{if .Blacklists}}
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-red-900 text-red-200 border border-red-600">
|
|
Blacklisted {{len .Blacklists}} times
|
|
</span>
|
|
{{else}}
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-900 text-green-200 border border-green-600">
|
|
Not Blacklisted
|
|
</span>
|
|
{{end}}
|
|
</div>
|
|
{{if .SenderRep}}
|
|
<div class="mt-4">
|
|
<span class="text-sm font-medium text-gray-400">Sender Reputation:</span>
|
|
<div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium mt-1 {{if contains .SenderRep "EXCELLENT"}}bg-green-900 text-green-200 border border-green-600{{else if contains .SenderRep "GOOD"}}bg-green-900 text-green-200 border border-green-600{{else if contains .SenderRep "FAIR"}}bg-yellow-900 text-yellow-200 border border-yellow-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
{{.SenderRep}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 p-4 bg-gray-900 rounded-lg border border-gray-600">
|
|
<p class="text-sm text-gray-300">
|
|
<strong class="text-gray-200">Note:</strong> The <strong>Envelope Sender</strong> is the real sender used for delivery (can differ from From).
|
|
The <strong>From Domain</strong> is shown to the recipient. The <strong>Sending Server</strong> is the host/IP that sent the message.
|
|
If these differ, the message may be sent on behalf of another user or via a third-party service.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<!-- All Headers Table -->
|
|
<div class="bg-gray-800 rounded-lg border border-gray-700">
|
|
<details class="p-6">
|
|
<summary class="text-xl font-bold text-gray-100 cursor-pointer hover:text-blue-400 transition-colors">
|
|
📋 All Email Headers Table
|
|
</summary>
|
|
<div class="mt-4 space-y-4">
|
|
<div>
|
|
<input type="text"
|
|
id="headerSearch"
|
|
placeholder="Search headers..."
|
|
class="w-full max-w-md px-4 py-2 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 placeholder-gray-400 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table id="headersTable" class="w-full border border-gray-600 rounded-lg overflow-hidden">
|
|
<thead class="bg-gray-900">
|
|
<tr>
|
|
<th class="text-left px-4 py-3 text-gray-200 font-medium border-b border-gray-600 w-48">Header Name</th>
|
|
<th class="text-left px-4 py-3 text-gray-200 font-medium border-b border-gray-600">Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-600">
|
|
{{range $k, $v := .AllHeaders}}
|
|
<tr class="hover:bg-gray-700/50">
|
|
<td class="px-4 py-3 text-gray-300 font-mono text-sm break-words border-r border-gray-600">{{$k}}</td>
|
|
<td class="px-4 py-3 text-gray-100 font-mono text-sm whitespace-pre-wrap break-words">{{$v}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
{{if .PhishingRisk}}
|
|
<!-- Security Risk Assessment -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🔍 Security Risk Assessment</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div class="bg-gray-900 rounded-lg p-4 border border-gray-600">
|
|
<h3 class="text-lg font-semibold text-gray-200 mb-3">Phishing Risk</h3>
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if eq (index (splitString .PhishingRisk " ") 0) "HIGH"}}bg-red-900 text-red-200 border border-red-600{{else if eq (index (splitString .PhishingRisk " ") 0) "MEDIUM"}}bg-yellow-900 text-yellow-200 border border-yellow-600{{else}}bg-green-900 text-green-200 border border-green-600{{end}}">
|
|
{{.PhishingRisk}}
|
|
</span>
|
|
</div>
|
|
<div class="bg-gray-900 rounded-lg p-4 border border-gray-600">
|
|
<h3 class="text-lg font-semibold text-gray-200 mb-3">Spoofing Risk</h3>
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if contains .SpoofingRisk "POTENTIAL"}}bg-yellow-900 text-yellow-200 border border-yellow-600{{else}}bg-green-900 text-green-200 border border-green-600{{end}}">
|
|
{{.SpoofingRisk}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Basic Information -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">📧 Basic Information</h2>
|
|
<div class="grid grid-cols-2 gap-6">
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">From:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded break-all">{{.From}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">To:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded break-all">{{.To}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Subject:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded break-words">{{.Subject}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Date:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.Date}}</p>
|
|
</div>
|
|
{{if .ReplyTo}}
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Reply-To:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded break-all">{{.ReplyTo}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Message-ID:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded break-all">{{.MessageID}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Priority:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.Priority}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Content Type:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.ContentType}}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Encoding:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.Encoding}}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mail Flow -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🔄 Mail Flow</h2>
|
|
<div class="space-y-3">
|
|
{{range $index, $received := .Received}}
|
|
<div class="flex items-start space-x-3">
|
|
<div class="flex-shrink-0 w-8 h-8 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-bold">
|
|
{{add $index 1}}
|
|
</div>
|
|
<div class="flex-1 p-3 bg-gray-900 rounded-lg border border-gray-600">
|
|
<p class="text-gray-100 font-mono text-sm">{{$received}}</p>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delivery Analysis -->
|
|
{{if ne .DeliveryDelay "Insufficient data for delay analysis"}}
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">⏱️ Delivery Analysis</h2>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Delivery Timing:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.DeliveryDelay}}</p>
|
|
</div>
|
|
{{if .GeoLocation}}
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-400">Geographic Info:</span>
|
|
<p class="text-gray-100 font-mono text-sm bg-gray-900 p-2 rounded">{{.GeoLocation}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Security Analysis -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🔒 Security Analysis</h2>
|
|
<div class="grid grid-cols-3 gap-6">
|
|
<!-- SPF Authentication -->
|
|
<div class="bg-gray-900 rounded-lg p-4 border border-gray-600">
|
|
<h3 class="text-lg font-semibold text-gray-200 mb-3">SPF Authentication</h3>
|
|
<div class="mb-3">
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .SPFPass}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
{{if .SPFPass}}✓ Passed{{else}}✗ Failed{{end}}
|
|
</span>
|
|
</div>
|
|
<p class="text-gray-300 text-sm mb-3">{{.SPFDetails}}</p>
|
|
{{if .SPFRecord}}
|
|
<div class="mb-3">
|
|
<pre class="bg-gray-800 text-gray-100 p-3 rounded text-xs overflow-x-auto border border-gray-600">{{.SPFRecord}}</pre>
|
|
</div>
|
|
{{end}}
|
|
{{if .SPFHeader}}
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300">Show SPF Header</summary>
|
|
<pre class="bg-gray-800 text-gray-100 p-3 rounded text-xs overflow-x-auto border border-gray-600 mt-2">{{.SPFHeader}}</pre>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
|
|
<!-- DMARC Policy -->
|
|
<div class="bg-gray-900 rounded-lg p-4 border border-gray-600">
|
|
<h3 class="text-lg font-semibold text-gray-200 mb-3">DMARC Policy</h3>
|
|
<div class="mb-3">
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .DMARCPass}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
{{if .DMARCPass}}✓ Passed{{else}}✗ Failed{{end}}
|
|
</span>
|
|
</div>
|
|
<p class="text-gray-300 text-sm mb-3">{{.DMARCDetails}}</p>
|
|
{{if .DMARCRecord}}
|
|
<div class="mb-3">
|
|
<pre class="bg-gray-800 text-gray-100 p-3 rounded text-xs overflow-x-auto border border-gray-600">{{.DMARCRecord}}</pre>
|
|
</div>
|
|
{{end}}
|
|
{{if .DMARCHeader}}
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300">Show DMARC Header</summary>
|
|
<pre class="bg-gray-800 text-gray-100 p-3 rounded text-xs overflow-x-auto border border-gray-600 mt-2">{{.DMARCHeader}}</pre>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
|
|
<!-- DKIM Signature -->
|
|
<div class="bg-gray-900 rounded-lg p-4 border border-gray-600">
|
|
<h3 class="text-lg font-semibold text-gray-200 mb-3">DKIM Signature</h3>
|
|
<div class="mb-3">
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{if .DKIMPass}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
{{if .DKIMPass}}✓ Present{{else}}✗ Missing{{end}}
|
|
</span>
|
|
</div>
|
|
<p class="text-gray-300 text-sm mb-3">{{.DKIMDetails}}</p>
|
|
{{if .DKIM}}
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300">Show DKIM Header</summary>
|
|
<pre class="bg-gray-800 text-gray-100 p-3 rounded text-xs overflow-x-auto border border-gray-600 mt-2">{{.DKIM}}</pre>
|
|
</details>
|
|
{{else if .DKIMHeader}}
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300">Show DKIM Header</summary>
|
|
<pre class="bg-gray-800 text-gray-100 p-3 rounded text-xs overflow-x-auto border border-gray-600 mt-2">{{.DKIMHeader}}</pre>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Encryption -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🔐 Encryption</h2>
|
|
<div class="mb-4">
|
|
<span class="inline-flex items-center px-4 py-2 rounded-lg text-sm font-medium {{if .Encrypted}}bg-green-900 text-green-200 border border-green-600{{else}}bg-red-900 text-red-200 border border-red-600{{end}}">
|
|
{{if .Encrypted}}🔒 Encrypted (TLS){{else}}🔓 Not Encrypted{{end}}
|
|
</span>
|
|
</div>
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300 mb-2">Show Encryption Details</summary>
|
|
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto border border-gray-600">{{.EncryptionDetail}}</pre>
|
|
</details>
|
|
</div>
|
|
|
|
{{if .Warnings}}
|
|
<!-- Warnings -->
|
|
<div class="bg-yellow-900/20 rounded-lg p-6 border border-yellow-600">
|
|
<h2 class="text-2xl font-bold text-yellow-200 mb-4">⚠️ Warnings</h2>
|
|
<div class="space-y-2">
|
|
{{range .Warnings}}
|
|
<div class="flex items-start space-x-2">
|
|
<span class="text-yellow-400 mt-1">⚠️</span>
|
|
<p class="text-yellow-100">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .SecurityFlags}}
|
|
<!-- Security Flags -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🛡️ Security Flags</h2>
|
|
<div class="space-y-2">
|
|
{{range .SecurityFlags}}
|
|
<div class="flex items-start space-x-2">
|
|
<span class="text-green-400 mt-1">🔒</span>
|
|
<p class="text-gray-100">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Blacklists}}
|
|
<!-- Blacklist Status -->
|
|
<div class="bg-red-900/20 rounded-lg p-6 border border-red-600">
|
|
<h2 class="text-2xl font-bold text-red-200 mb-4">🚫 Blacklist Status</h2>
|
|
<div class="mb-4">
|
|
<span class="text-sm font-medium text-gray-400">Checked:</span>
|
|
<span class="text-gray-100 ml-2">
|
|
{{if .SendingServer}}IP {{.SendingServer}}{{else if .FromDomain}}Domain {{.FromDomain}}{{end}}
|
|
</span>
|
|
</div>
|
|
<div class="mb-4">
|
|
<span class="inline-flex items-center px-4 py-2 rounded-lg text-sm font-medium bg-red-900 text-red-200 border border-red-600">
|
|
⚠️ Listed on the following blacklists
|
|
</span>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
|
|
{{range .Blacklists}}
|
|
<div class="bg-red-900/30 p-3 rounded border border-red-600">
|
|
<p class="text-red-100 text-sm">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .SpamFlags}}
|
|
<!-- Spam Analysis -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🛡️ Spam Analysis</h2>
|
|
<div class="mb-4">
|
|
<span class="inline-flex items-center px-4 py-2 rounded-lg text-sm font-medium {{if gt (len .SpamFlags) 0}}bg-yellow-900 text-yellow-200 border border-yellow-600{{else}}bg-green-900 text-green-200 border border-green-600{{end}}">
|
|
{{if gt (len .SpamFlags) 0}}⚠️ Spam Indicators Found{{else}}✓ No Spam Indicators{{end}}
|
|
</span>
|
|
</div>
|
|
{{if .SpamScore}}
|
|
<div class="mb-4">
|
|
<span class="text-sm font-medium text-gray-400">Spam Score:</span>
|
|
<span class="text-gray-100 ml-2 font-mono">{{.SpamScore}}</span>
|
|
</div>
|
|
{{end}}
|
|
{{if .SpamFlags}}
|
|
<div class="space-y-2">
|
|
{{range .SpamFlags}}
|
|
<div class="bg-yellow-900/20 p-3 rounded border border-yellow-600">
|
|
<p class="text-yellow-100 text-sm">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if ne .VirusInfo "No virus scanning information found"}}
|
|
<!-- Virus Scanning -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🛡️ Virus Scanning</h2>
|
|
<div class="mb-4">
|
|
<span class="inline-flex items-center px-4 py-2 rounded-lg text-sm font-medium bg-green-900 text-green-200 border border-green-600">
|
|
🛡️ Virus Scanning Information
|
|
</span>
|
|
</div>
|
|
<p class="text-gray-100">{{.VirusInfo}}</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .ListInfo}}
|
|
<!-- Mailing List Information -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">📬 Mailing List Information</h2>
|
|
<div class="space-y-2 mb-4">
|
|
{{range .ListInfo}}
|
|
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
|
<p class="text-gray-100 text-sm">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
{{if .AutoReply}}
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-900 text-blue-200 border border-blue-600">
|
|
📧 Auto-reply message detected
|
|
</span>
|
|
{{end}}
|
|
{{if .BulkEmail}}
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-900 text-blue-200 border border-blue-600">
|
|
📬 Bulk/marketing email detected
|
|
</span>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Compliance}}
|
|
<!-- Compliance Information -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">✅ Compliance Information</h2>
|
|
<div class="space-y-2">
|
|
{{range .Compliance}}
|
|
<div class="flex items-start space-x-2">
|
|
<span class="text-green-400 mt-1">✓</span>
|
|
<p class="text-gray-100">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .ARC}}
|
|
<!-- ARC (Authenticated Received Chain) -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🔗 ARC (Authenticated Received Chain)</h2>
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300 mb-2">Show ARC Headers</summary>
|
|
<div class="space-y-3 mt-4">
|
|
{{range .ARC}}
|
|
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto border border-gray-600">{{.}}</pre>
|
|
{{end}}
|
|
</div>
|
|
</details>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if ne .BIMI "No BIMI record found"}}
|
|
<!-- Brand Indicators (BIMI) -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🎨 Brand Indicators (BIMI)</h2>
|
|
<p class="text-gray-100">{{.BIMI}}</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Attachments}}
|
|
<!-- Attachment Information -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">📎 Attachment Information</h2>
|
|
<div class="space-y-2">
|
|
{{range .Attachments}}
|
|
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
|
<p class="text-gray-100 text-sm font-mono">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .URLs}}
|
|
<!-- URL Information -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🔗 URL Information</h2>
|
|
<div class="space-y-2">
|
|
{{range .URLs}}
|
|
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
|
<p class="text-gray-100 text-sm font-mono break-all">{{.}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if ne .ThreadInfo "No threading information available"}}
|
|
<!-- Message Threading -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
<h2 class="text-2xl font-bold text-gray-100 mb-4">🧵 Message Threading</h2>
|
|
<details class="text-sm">
|
|
<summary class="text-blue-400 cursor-pointer hover:text-blue-300 mb-2">Show Threading Information</summary>
|
|
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto border border-gray-600 mt-4">{{.ThreadInfo}}</pre>
|
|
</details>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Export Options -->
|
|
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700 text-center">
|
|
<h2 class="text-xl font-bold text-gray-100 mb-4">📄 Export Options</h2>
|
|
<div class="flex flex-wrap justify-center gap-4">
|
|
<button onclick="exportPDF()"
|
|
type="button"
|
|
class="bg-red-600 hover:bg-red-700 text-white font-medium px-6 py-3 rounded-lg transition-colors flex items-center space-x-2">
|
|
<span>📄</span>
|
|
<span>Export as PDF</span>
|
|
</button>
|
|
<button onclick="exportImage()"
|
|
type="button"
|
|
class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-3 rounded-lg transition-colors flex items-center space-x-2">
|
|
<span>🖼️</span>
|
|
<span>Save as Image</span>
|
|
</button>
|
|
<button onclick="printReport()"
|
|
type="button"
|
|
class="bg-green-600 hover:bg-green-700 text-white font-medium px-6 py-3 rounded-lg transition-colors flex items-center space-x-2">
|
|
<span>🖨️</span>
|
|
<span>Print Report</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "scripts"}}
|
|
<script>
|
|
// Check if required libraries are loaded
|
|
function checkLibraries() {
|
|
if (typeof html2canvas === 'undefined') {
|
|
console.warn('html2canvas library not loaded');
|
|
return false;
|
|
}
|
|
const hasHtml2pdf = typeof html2pdf !== 'undefined';
|
|
const hasJsPDF = typeof window.jsPDF !== 'undefined';
|
|
|
|
if (!hasHtml2pdf && !hasJsPDF) {
|
|
console.warn('Neither html2pdf nor jsPDF library loaded');
|
|
return false;
|
|
}
|
|
|
|
if (hasHtml2pdf) {
|
|
console.log('Using html2pdf for PDF generation');
|
|
} else if (hasJsPDF) {
|
|
console.log('Using jsPDF fallback for PDF generation');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Template helper functions for the enhanced features
|
|
function splitString(str, delimiter) {
|
|
return str.split(delimiter);
|
|
}
|
|
|
|
function contains(str, substr) {
|
|
return str.includes(substr);
|
|
}
|
|
|
|
function add(a, b) {
|
|
return a + b;
|
|
}
|
|
|
|
function exportImage() {
|
|
// if (typeof html2canvas === 'undefined') {
|
|
// alert('Image export library not loaded. Please refresh the page and try again.');
|
|
// return;
|
|
// }
|
|
|
|
html2canvas(document.querySelector("#report")).then(canvas => {
|
|
let link = document.createElement("a");
|
|
link.download = "email-analysis.png";
|
|
link.href = canvas.toDataURL();
|
|
link.click();
|
|
}).catch(error => {
|
|
console.error('Image export failed:', error);
|
|
alert('Failed to export image. Please try again.');
|
|
});
|
|
}
|
|
|
|
function printReport() {
|
|
// Store original states
|
|
const originalDetailsStates = {};
|
|
document.querySelectorAll('#report details').forEach((detail, index) => {
|
|
originalDetailsStates[index] = detail.open;
|
|
});
|
|
|
|
// Expand details for printing (except ARC)
|
|
document.querySelectorAll('#report details').forEach(detail => {
|
|
const summary = detail.querySelector('summary');
|
|
if (summary && !summary.textContent.includes('Show ARC Headers')) {
|
|
detail.open = true;
|
|
}
|
|
});
|
|
|
|
// Open print dialog
|
|
window.print();
|
|
|
|
// Restore original states after a short delay
|
|
setTimeout(() => {
|
|
document.querySelectorAll('#report details').forEach((detail, index) => {
|
|
detail.open = originalDetailsStates[index] || false;
|
|
});
|
|
}, 1000);
|
|
}
|
|
|
|
function exportPDF() {
|
|
// Check if libraries are available
|
|
// if (typeof html2canvas === 'undefined') {
|
|
// alert('HTML2Canvas library not loaded. Please refresh the page and try again.');
|
|
// return;
|
|
// }
|
|
|
|
// Check if we have either html2pdf or jsPDF available
|
|
const hasHtml2pdf = typeof html2pdf !== 'undefined';
|
|
const hasJsPDF = typeof window.jsPDF !== 'undefined';
|
|
|
|
// if (!hasHtml2pdf && !hasJsPDF) {
|
|
// alert('PDF generation library not loaded. Please refresh the page and try again.');
|
|
// return;
|
|
// }
|
|
|
|
// Store original states
|
|
const originalDetailsStates = {};
|
|
document.querySelectorAll('#report details').forEach((detail, index) => {
|
|
originalDetailsStates[index] = detail.open;
|
|
});
|
|
|
|
// Expand only specific details that should be included in PDF (not ARC)
|
|
document.querySelectorAll('#report details').forEach(detail => {
|
|
const summary = detail.querySelector('summary');
|
|
if (summary && !summary.textContent.includes('Show ARC Headers')) {
|
|
detail.open = true;
|
|
}
|
|
});
|
|
|
|
// Make sure all-headers is expanded
|
|
const allHeaders = document.getElementById('all-headers');
|
|
if (allHeaders) {
|
|
allHeaders.open = true;
|
|
}
|
|
|
|
const element = document.getElementById('report');
|
|
if (!element) {
|
|
alert('Report element not found. Please ensure the analysis is complete.');
|
|
return;
|
|
}
|
|
|
|
// Add temporary styling for PDF export
|
|
const tempStyle = document.createElement('style');
|
|
tempStyle.id = 'temp-pdf-style';
|
|
tempStyle.innerHTML = `
|
|
body {
|
|
max-width: none !important;
|
|
width: 794px !important;
|
|
margin: 0 !important;
|
|
padding: 0 !important;
|
|
background: white !important;
|
|
color: black !important;
|
|
}
|
|
.container {
|
|
max-width: none !important;
|
|
width: 794px !important;
|
|
padding: 15px !important;
|
|
margin: 0 !important;
|
|
box-sizing: border-box !important;
|
|
}
|
|
#report {
|
|
max-width: none !important;
|
|
width: 100% !important;
|
|
padding: 10px !important;
|
|
margin: 0 !important;
|
|
background: white !important;
|
|
color: black !important;
|
|
box-sizing: border-box !important;
|
|
position: relative !important;
|
|
left: 0 !important;
|
|
top: 0 !important;
|
|
}
|
|
#report * {
|
|
background: white !important;
|
|
color: black !important;
|
|
max-width: none !important;
|
|
}
|
|
#report .section {
|
|
width: 100% !important;
|
|
max-width: none !important;
|
|
background: white !important;
|
|
border: 1px solid #ccc !important;
|
|
margin-bottom: 10px !important;
|
|
padding: 10px !important;
|
|
box-sizing: border-box !important;
|
|
page-break-inside: avoid !important;
|
|
}
|
|
#report .grid {
|
|
display: block !important;
|
|
width: 100% !important;
|
|
}
|
|
#report .grid > div {
|
|
display: block !important;
|
|
width: 100% !important;
|
|
margin-bottom: 10px !important;
|
|
}
|
|
#report .grid p {
|
|
margin: 5px 0 !important;
|
|
word-break: break-word !important;
|
|
overflow-wrap: break-word !important;
|
|
}
|
|
#report pre {
|
|
background: #f5f5f5 !important;
|
|
color: black !important;
|
|
border: 1px solid #ccc !important;
|
|
padding: 5px !important;
|
|
width: 100% !important;
|
|
max-width: none !important;
|
|
box-sizing: border-box !important;
|
|
white-space: pre-wrap !important;
|
|
word-break: break-word !important;
|
|
page-break-inside: avoid !important;
|
|
}
|
|
#report table {
|
|
width: 100% !important;
|
|
max-width: none !important;
|
|
border-collapse: collapse !important;
|
|
table-layout: fixed !important;
|
|
page-break-inside: avoid !important;
|
|
}
|
|
#report td, #report th {
|
|
border: 1px solid #333 !important;
|
|
background: white !important;
|
|
color: black !important;
|
|
padding: 4px !important;
|
|
word-wrap: break-word !important;
|
|
word-break: break-word !important;
|
|
overflow-wrap: break-word !important;
|
|
}
|
|
#report .status {
|
|
background: #f0f0f0 !important;
|
|
color: black !important;
|
|
border: 1px solid #999 !important;
|
|
display: inline-block !important;
|
|
padding: 2px 5px !important;
|
|
}
|
|
#report .status.good {
|
|
background: #e8f5e8 !important;
|
|
}
|
|
#report .status.warning {
|
|
background: #fff3cd !important;
|
|
}
|
|
#report .status.error {
|
|
background: #f8d7da !important;
|
|
}
|
|
#report details {
|
|
margin-bottom: 10px !important;
|
|
page-break-inside: avoid !important;
|
|
}
|
|
/* Hide buttons */
|
|
button {
|
|
display: none !important;
|
|
}
|
|
`;
|
|
document.head.appendChild(tempStyle);
|
|
|
|
// Force the element to have A4 dimensions
|
|
const originalWidth = element.style.width;
|
|
const originalMaxWidth = element.style.maxWidth;
|
|
|
|
element.style.width = '794px'; // A4 width in pixels
|
|
element.style.maxWidth = 'none';
|
|
|
|
// Store these for restoration
|
|
const restoreWidth = () => {
|
|
element.style.width = originalWidth;
|
|
element.style.maxWidth = originalMaxWidth;
|
|
};
|
|
|
|
// Show loading message
|
|
const button = document.querySelector('button[onclick="exportPDF()"]');
|
|
const originalText = button ? button.textContent : '';
|
|
if (button) button.textContent = 'Generating PDF...';
|
|
|
|
// Wait a moment for styles to be applied
|
|
setTimeout(() => {
|
|
performPDFExport();
|
|
}, 100);
|
|
|
|
function performPDFExport() {
|
|
try {
|
|
if (hasHtml2pdf) {
|
|
// Use html2pdf if available
|
|
const opt = {
|
|
margin: [5, 5, 5, 5], // Smaller margins in mm
|
|
filename: 'email-header-analysis.pdf',
|
|
image: { type: 'jpeg', quality: 0.95 },
|
|
html2canvas: {
|
|
scale: 2,
|
|
useCORS: true,
|
|
allowTaint: true,
|
|
scrollX: 0,
|
|
scrollY: 0,
|
|
x: 0,
|
|
y: 0,
|
|
width: 794, // A4 width in pixels at 96 DPI (210mm)
|
|
height: element.scrollHeight,
|
|
backgroundColor: '#ffffff',
|
|
windowWidth: 794, // A4 width at 96 DPI
|
|
windowHeight: 1123 // A4 height at 96 DPI
|
|
},
|
|
jsPDF: {
|
|
unit: 'mm',
|
|
format: 'a4',
|
|
orientation: 'portrait',
|
|
compress: true
|
|
}
|
|
};
|
|
|
|
html2pdf().set(opt).from(element).save().then(() => {
|
|
restoreOriginalState();
|
|
}).catch((error) => {
|
|
console.error('PDF generation failed:', error);
|
|
alert('Failed to generate PDF. Please try again.');
|
|
restoreOriginalState();
|
|
});
|
|
} else if (hasJsPDF) {
|
|
// Fallback to direct jsPDF + html2canvas approach
|
|
html2canvas(element, {
|
|
scale: 2,
|
|
useCORS: true,
|
|
allowTaint: true,
|
|
scrollX: 0,
|
|
scrollY: 0,
|
|
x: 0,
|
|
y: 0,
|
|
width: 794, // A4 width in pixels at 96 DPI (210mm)
|
|
height: element.scrollHeight,
|
|
backgroundColor: '#ffffff',
|
|
windowWidth: 794, // A4 width at 96 DPI
|
|
windowHeight: 1123 // A4 height at 96 DPI
|
|
}).then(canvas => {
|
|
const imgData = canvas.toDataURL('image/jpeg', 0.95);
|
|
const pdf = new window.jsPDF.jsPDF('p', 'mm', 'a4');
|
|
|
|
const pdfWidth = pdf.internal.pageSize.getWidth();
|
|
const pdfHeight = pdf.internal.pageSize.getHeight();
|
|
|
|
// Use full width with minimal margins
|
|
const imgWidth = pdfWidth - 10; // 5mm margin on each side
|
|
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
|
|
|
let heightLeft = imgHeight;
|
|
let position = 5; // 5mm top margin
|
|
|
|
pdf.addImage(imgData, 'JPEG', 5, position, imgWidth, imgHeight);
|
|
heightLeft -= (pdfHeight - 10); // Account for margins
|
|
|
|
while (heightLeft >= 0) {
|
|
position = heightLeft - imgHeight + 5;
|
|
pdf.addPage();
|
|
pdf.addImage(imgData, 'JPEG', 5, position, imgWidth, imgHeight);
|
|
heightLeft -= (pdfHeight - 10);
|
|
}
|
|
|
|
pdf.save('email-header-analysis.pdf');
|
|
restoreOriginalState();
|
|
}).catch(error => {
|
|
console.error('Canvas generation failed:', error);
|
|
alert('Failed to generate PDF. Please try again.');
|
|
restoreOriginalState();
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('PDF export error:', error);
|
|
restoreOriginalState();
|
|
}
|
|
|
|
function restoreOriginalState() {
|
|
document.querySelectorAll('#report details').forEach((detail, index) => {
|
|
detail.open = originalDetailsStates[index] || false;
|
|
});
|
|
// Remove temporary PDF styling
|
|
const tempStyle = document.getElementById('temp-pdf-style');
|
|
if (tempStyle) {
|
|
document.head.removeChild(tempStyle);
|
|
}
|
|
// Restore element width if restoreWidth function exists
|
|
if (typeof restoreWidth === 'function') {
|
|
restoreWidth();
|
|
}
|
|
if (button) button.textContent = originalText;
|
|
}
|
|
} // End of performPDFExport function
|
|
}
|
|
|
|
// Header table search
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Check if libraries are loaded
|
|
setTimeout(() => {
|
|
if (!checkLibraries()) {
|
|
console.warn('Some export libraries failed to load. PDF/Image export may not work.');
|
|
}
|
|
}, 1000);
|
|
|
|
var search = document.getElementById('headerSearch');
|
|
if (search) {
|
|
search.addEventListener('input', function() {
|
|
var filter = search.value.toLowerCase();
|
|
var rows = document.querySelectorAll('#headersTable tbody tr');
|
|
rows.forEach(function(row) {
|
|
var text = row.textContent.toLowerCase();
|
|
row.style.display = text.indexOf(filter) > -1 ? '' : 'none';
|
|
});
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{{end}}
|