Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix mail parsing issues #301

Merged
merged 2 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"require": {
"php": "^8.0",
"illuminate/contracts": "^8.0|^9.0|^10.0",
"opcodesio/mail-parser": "^0.1.1"
"opcodesio/mail-parser": "^0.1.6"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.2",
Expand Down
2 changes: 1 addition & 1 deletion public/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/app.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"/app.js": "/app.js?id=0c6cf8ac8d5abf3598949d9417bf56eb",
"/app.css": "/app.css?id=df9446e9defec9b688bb5566dc40e285",
"/app.js": "/app.js?id=3aebfa606492fb5d84145443e7a458cd",
"/app.css": "/app.css?id=46e730db84da71f61a3f42fe6b08d8eb",
"/img/log-viewer-128.png": "/img/log-viewer-128.png?id=d576c6d2e16074d3f064e60fe4f35166",
"/img/log-viewer-32.png": "/img/log-viewer-32.png?id=f8ec67d10f996aa8baf00df3b61eea6d",
"/img/log-viewer-64.png": "/img/log-viewer-64.png?id=8902d596fc883ca9eb8105bb683568c6"
Expand Down
4 changes: 4 additions & 0 deletions resources/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ html.dark {
@apply w-full border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-900 overflow-auto mb-4 lg:mb-6;
}

.mail-preview-text {
@apply w-full border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-900 text-sm whitespace-pre-wrap mb-4 lg:mb-6 p-4;
}

.mail-attachment-button {
@apply flex items-center justify-between px-2 py-1 lg:px-4 lg:py-2 bg-white dark:bg-gray-800 border dark:border-gray-700 rounded;
max-width: 460px;
Expand Down
33 changes: 23 additions & 10 deletions resources/js/components/BaseLogTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@
</div>

<tab-container v-if="logViewerStore.isOpen(index)" :tabs="getTabsForLog(log)">
<tab-content v-if="log.extra && log.extra.mail_preview" tab-value="mail_preview">
<mail-preview :mail="log.extra.mail_preview" />
<tab-content v-if="log.extra && log.extra.mail_preview && log.extra.mail_preview.html" tab-value="mail_html_preview">
<mail-html-preview :mail="log.extra.mail_preview" />
</tab-content>

<tab-content v-if="log.extra && log.extra.mail_preview && log.extra.mail_preview.text" tab-value="mail_text_preview">
<mail-text-preview :mail="log.extra.mail_preview" />
</tab-content>

<tab-content tab-value="raw">
Expand Down Expand Up @@ -145,7 +149,8 @@ import { handleLogToggleKeyboardNavigation } from '../keyboardNavigation';
import { useSeverityStore } from '../stores/severity.js';
import TabContainer from "./TabContainer.vue";
import TabContent from "./TabContent.vue";
import MailPreview from "./MailPreview.vue";
import MailHtmlPreview from "./MailHtmlPreview.vue";
import MailTextPreview from "./MailTextPreview.vue";

const fileStore = useFileStore();
const logViewerStore = useLogViewerStore();
Expand All @@ -170,14 +175,22 @@ const hasContext = (log) => {
return log.context && Object.keys(log.context).length > 0;
}

const hasPreviews = (log) => {
return getExtraTabsForLog(log).length > 0;
}

const getExtraTabsForLog = (log) => {
return [
log.extra && log.extra.mail_preview ? { name: 'Mail preview', value: 'mail_preview' } : null,
].filter(Boolean);
let tabs = [];

if (! log.extra || ! log.extra.mail_preview) {
return tabs;
}

if (log.extra.mail_preview.html) {
tabs.push({ name: 'HTML preview', value: 'mail_html_preview' });
}

if (log.extra.mail_preview.text) {
tabs.push({ name: 'Text preview', value: 'mail_text_preview' });
}

return tabs;
}

const getTabsForLog = (log) => {
Expand Down
34 changes: 34 additions & 0 deletions resources/js/components/MailHtmlPreview.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div class="mail-preview">
<!-- headers -->
<mail-preview-attributes :mail="mail"/>

<!-- HTML preview -->
<iframe
v-if="mail.html"
class="mail-preview-html"
:style="{height: `${iframeHeight}px`}"
:srcdoc="mail.html"
@load="setIframeHeight"
ref="iframe"
></iframe>
</div>
</template>

<script setup>
import {ref} from "vue";
import MailPreviewAttributes from "./MailPreviewAttributes.vue";

defineProps({
mail: {
type: Object,
},
})

const iframe = ref(null);
const iframeHeight = ref(600);

const setIframeHeight = () => {
iframeHeight.value = (iframe.value?.contentWindow?.document?.body?.clientHeight || 580) + 20;
}
</script>
85 changes: 0 additions & 85 deletions resources/js/components/MailPreview.vue

This file was deleted.

69 changes: 69 additions & 0 deletions resources/js/components/MailPreviewAttributes.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div class="mail-preview-attributes">
<table>
<tr v-if="mail.from">
<td class="font-semibold">From</td>
<td>{{ mail.from }}</td>
</tr>
<tr v-if="mail.to">
<td class="font-semibold">To</td>
<td>{{ mail.to }}</td>
</tr>
<tr v-if="mail.id">
<td class="font-semibold">Message ID</td>
<td>{{ mail.id }}</td>
</tr>
<tr v-if="mail.subject">
<td class="font-semibold">Subject</td>
<td>{{ mail.subject }}</td>
</tr>
<tr v-if="mail.attachments && mail.attachments.length > 0">
<td class="font-semibold">Attachments</td>
<td>
<div v-for="(attachment, index) in mail.attachments" :key="`mail-${mail.id}-attachment-${index}`"
class="mail-attachment-button"
>
<div class="flex items-center">
<PaperClipIcon class="h-4 w-4 text-gray-500 dark:text-gray-400 mr-1"/>
<span>{{ attachment.filename }} <span class="opacity-60">({{ attachment.size_formatted }})</span></span>
</div>
<div>
<a href="#" @click.prevent="downloadAttachment(attachment)"
class="text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400"
>Download</a>
</div>
</div>
</td>
</tr>
</table>
</div>
</template>

<script setup>
import { PaperClipIcon } from '@heroicons/vue/24/outline';

defineProps(['mail']);

const downloadAttachment = (attachment) => {
// Decode the base64 encoded string
const decodedContent = atob(attachment.content);

// Convert decoded base64 string to a Uint8Array
const byteNumbers = new Array(decodedContent.length);
for (let i = 0; i < decodedContent.length; i++) {
byteNumbers[i] = decodedContent.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);

const blob = new Blob([byteArray], { type: attachment.content_type || 'application/octet-stream' });
const blobUrl = URL.createObjectURL(blob);

const downloadLink = document.createElement('a');
downloadLink.href = blobUrl;
downloadLink.download = attachment.filename;
downloadLink.click();

// Clean up the temporary URL after the download
URL.revokeObjectURL(blobUrl);
}
</script>
31 changes: 31 additions & 0 deletions resources/js/components/MailTextPreview.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<div class="mail-preview">
<!-- headers -->
<mail-preview-attributes :mail="mail"/>

<!-- Text preview -->
<pre
v-if="mail.text"
class="mail-preview-text"
v-text="mail.text"
></pre>
</div>
</template>

<script setup>
import {ref} from "vue";
import MailPreviewAttributes from "./MailPreviewAttributes.vue";

defineProps({
mail: {
type: Object,
},
})

const iframe = ref(null);
const iframeHeight = ref(600);

const setIframeHeight = () => {
iframeHeight.value = (iframe.value?.contentWindow?.document?.body?.clientHeight || 580) + 20;
}
</script>
2 changes: 1 addition & 1 deletion src/Logs/LaravelLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ protected function extractMailPreview(): void
'from' => $message->getFrom(),
'to' => $message->getTo(),
'attachments' => array_map(fn ($attachment) => [
'content' => $attachment->getContent(),
'content' => base64_encode($attachment->getContent()),
'content_type' => $attachment->getContentType(),
'filename' => $attachment->getFilename(),
'size_formatted' => Utils::bytesForHumans($attachment->getSize()),
Expand Down
4 changes: 2 additions & 2 deletions tests/Unit/LaravelLogs/LaravelLogsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Subject: This is an email with common headers
Date: Thu, 24 Aug 2023 21:15:01 PST
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_Part_1_1234567890"
Content-Type: multipart/alternative; boundary="----=_Part_1_1234567890"

------=_Part_1_1234567890
Content-Type: text/plain; charset="utf-8"
Expand Down Expand Up @@ -52,7 +52,7 @@
'to' => 'recipient@example.com',
'attachments' => [
[
'content' => 'Example attachment content',
'content' => base64_encode('Example attachment content'),
'content_type' => 'text/plain; charset="utf-8"',
'filename' => 'example.txt',
'size_formatted' => Utils::bytesForHumans(strlen('Example attachment content')),
Expand Down
Loading