Selaa lähdekoodia

Added support for inline images #210

Will Browning 3 vuotta sitten
vanhempi
commit
1ea9ddefae

+ 13 - 0
app/CustomMailDriver/CustomSmtpTransport.php

@@ -84,6 +84,19 @@ class CustomSmtpTransport extends Swift_Transport_EsmtpTransport
             $message->getHeaders()->remove('Alias-To');
         }
 
+        // Update Content IDs for inline image attachments
+        if ($oldCids = $message->getHeaders()->get('X-Old-Cids')) {
+            $oldCidsArray = explode(',', $oldCids->getFieldBodyModel());
+
+            $newCids = $message->getHeaders()->get('X-New-Cids');
+            $newCidsArray = explode(',', $newCids->getFieldBodyModel());
+
+            $message->getHeaders()->remove('X-Old-Cids');
+            $message->getHeaders()->remove('X-New-Cids');
+
+            $message->setBody(str_replace($oldCidsArray, $newCidsArray, $message->getBody()));
+        }
+
         try {
             $sent += $this->sendTo($message, $reversePath, $tos, $failedRecipients);
             $sent += $this->sendBcc($message, $reversePath, $bcc, $failedRecipients);

+ 25 - 0
app/Mail/ForwardEmail.php

@@ -17,6 +17,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Support\Facades\URL;
 use Illuminate\Support\Str;
+use Swift_Image;
 use Swift_Signers_DKIMSigner;
 use Swift_SwiftException;
 
@@ -34,6 +35,7 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
     protected $emailText;
     protected $emailHtml;
     protected $emailAttachments;
+    protected $emailInlineAttachments;
     protected $deactivateUrl;
     protected $bannerLocation;
     protected $fingerprint;
@@ -64,6 +66,7 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
         $this->emailText = $emailData->text;
         $this->emailHtml = $emailData->html;
         $this->emailAttachments = $emailData->attachments;
+        $this->emailInlineAttachments = $emailData->inlineAttachments;
         $this->deactivateUrl = URL::signedRoute('deactivate', ['alias' => $alias->id]);
         $this->size = $emailData->size;
         $this->messageId = $emailData->messageId;
@@ -187,6 +190,21 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
                 if ($this->dkimSigner) {
                     $message->attachSigner($this->dkimSigner);
                 }
+
+                if ($this->emailInlineAttachments) {
+                    foreach ($this->emailInlineAttachments as $attachment) {
+                        $image = new Swift_Image(base64_decode($attachment['stream']), base64_decode($attachment['file_name']), base64_decode($attachment['mime']));
+
+                        $cids[] = 'cid:' . base64_decode($attachment['contentId']);
+                        $newCids[] = $message->embed($image);
+                    }
+
+                    $message->getHeaders()
+                            ->addTextHeader('X-Old-Cids', implode(',', $cids));
+
+                    $message->getHeaders()
+                            ->addTextHeader('X-New-Cids', implode(',', $newCids));
+                }
             });
 
         if ($this->emailText) {
@@ -201,6 +219,13 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
             ]);
         }
 
+        // To prevent invalid view error where no text or html is present...
+        if (! $this->emailHtml && ! $this->emailText) {
+            $this->email->text('emails.forward.text')->with([
+                'text' => base64_decode($this->emailText)
+            ]);
+        }
+
         foreach ($this->emailAttachments as $attachment) {
             $this->email->attachData(
                 base64_decode($attachment['stream']),

+ 25 - 0
app/Mail/ReplyToEmail.php

@@ -13,6 +13,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
+use Swift_Image;
 use Swift_Signers_DKIMSigner;
 
 class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
@@ -27,6 +28,7 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
     protected $emailText;
     protected $emailHtml;
     protected $emailAttachments;
+    protected $emailInlineAttachments;
     protected $dkimSigner;
     protected $encryptedParts;
     protected $displayFrom;
@@ -49,6 +51,7 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
         $this->emailText = $emailData->text;
         $this->emailHtml = $emailData->html;
         $this->emailAttachments = $emailData->attachments;
+        $this->emailInlineAttachments = $emailData->inlineAttachments;
         $this->encryptedParts = $emailData->encryptedParts ?? null;
         $this->displayFrom = $user->from_name ?? null;
         $this->size = $emailData->size;
@@ -119,6 +122,21 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
                 if ($this->dkimSigner) {
                     $message->attachSigner($this->dkimSigner);
                 }
+
+                if ($this->emailInlineAttachments) {
+                    foreach ($this->emailInlineAttachments as $attachment) {
+                        $image = new Swift_Image(base64_decode($attachment['stream']), base64_decode($attachment['file_name']), base64_decode($attachment['mime']));
+
+                        $cids[] = 'cid:' . base64_decode($attachment['contentId']);
+                        $newCids[] = $message->embed($image);
+                    }
+
+                    $message->getHeaders()
+                            ->addTextHeader('X-Old-Cids', implode(',', $cids));
+
+                    $message->getHeaders()
+                            ->addTextHeader('X-New-Cids', implode(',', $newCids));
+                }
             });
 
         if ($this->emailText) {
@@ -133,6 +151,13 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
             ]);
         }
 
+        // To prevent invalid view error where no text or html is present...
+        if (! $this->emailHtml && ! $this->emailText) {
+            $this->email->text('emails.forward.text')->with([
+                'text' => base64_decode($this->emailText)
+            ]);
+        }
+
         foreach ($this->emailAttachments as $attachment) {
             $this->email->attachData(
                 base64_decode($attachment['stream']),

+ 25 - 0
app/Mail/SendFromEmail.php

@@ -13,6 +13,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
+use Swift_Image;
 use Swift_Signers_DKIMSigner;
 
 class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
@@ -27,6 +28,7 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
     protected $emailText;
     protected $emailHtml;
     protected $emailAttachments;
+    protected $emailInlineAttachments;
     protected $dkimSigner;
     protected $encryptedParts;
     protected $displayFrom;
@@ -47,6 +49,7 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
         $this->emailText = $emailData->text;
         $this->emailHtml = $emailData->html;
         $this->emailAttachments = $emailData->attachments;
+        $this->emailInlineAttachments = $emailData->inlineAttachments;
         $this->encryptedParts = $emailData->encryptedParts ?? null;
         $this->displayFrom = $user->from_name ?? null;
         $this->size = $emailData->size;
@@ -105,6 +108,21 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
                 if ($this->dkimSigner) {
                     $message->attachSigner($this->dkimSigner);
                 }
+
+                if ($this->emailInlineAttachments) {
+                    foreach ($this->emailInlineAttachments as $attachment) {
+                        $image = new Swift_Image(base64_decode($attachment['stream']), base64_decode($attachment['file_name']), base64_decode($attachment['mime']));
+
+                        $cids[] = 'cid:' . base64_decode($attachment['contentId']);
+                        $newCids[] = $message->embed($image);
+                    }
+
+                    $message->getHeaders()
+                            ->addTextHeader('X-Old-Cids', implode(',', $cids));
+
+                    $message->getHeaders()
+                            ->addTextHeader('X-New-Cids', implode(',', $newCids));
+                }
             });
 
         if ($this->emailText) {
@@ -119,6 +137,13 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
             ]);
         }
 
+        // To prevent invalid view error where no text or html is present...
+        if (! $this->emailHtml && ! $this->emailText) {
+            $this->email->text('emails.forward.text')->with([
+                'text' => base64_decode($this->emailText)
+            ]);
+        }
+
         foreach ($this->emailAttachments as $attachment) {
             $this->email->attachData(
                 base64_decode($attachment['stream']),

+ 16 - 5
app/Models/EmailData.php

@@ -17,6 +17,7 @@ class EmailData
         $this->text = base64_encode($parser->getMessageBody('text'));
         $this->html = base64_encode($parser->getMessageBody('html'));
         $this->attachments = [];
+        $this->inlineAttachments = [];
         $this->size = $size;
         $this->messageId = base64_encode($parser->getHeader('Message-ID'));
         $this->listUnsubscribe = base64_encode($parser->getHeader('List-Unsubscribe'));
@@ -27,11 +28,21 @@ class EmailData
             $this->encryptedParts = $parser->getAttachments();
         } else {
             foreach ($parser->getAttachments() as $attachment) {
-                $this->attachments[] = [
-                  'stream' => base64_encode(stream_get_contents($attachment->getStream())),
-                  'file_name' => base64_encode($attachment->getFileName()),
-                  'mime' => base64_encode($attachment->getContentType())
-              ];
+                if ($attachment->getContentDisposition() === 'inline') {
+                    $this->inlineAttachments[] = [
+                        'stream' => base64_encode(stream_get_contents($attachment->getStream())),
+                        'file_name' => base64_encode($attachment->getFileName()),
+                        'mime' => base64_encode($attachment->getContentType()),
+                        'contentDisposition' => base64_encode($attachment->getContentDisposition()),
+                        'contentId' => base64_encode($attachment->getContentID()),
+                    ];
+                } else {
+                    $this->attachments[] = [
+                      'stream' => base64_encode(stream_get_contents($attachment->getStream())),
+                      'file_name' => base64_encode($attachment->getFileName()),
+                      'mime' => base64_encode($attachment->getContentType())
+                  ];
+                }
             }
         }
     }

+ 2 - 2
config/version.yml

@@ -5,9 +5,9 @@ current:
   major: 0
   minor: 8
   patch: 6
-  prerelease: 1-g9d67f8e
+  prerelease: 2-g80e5faf
   buildmetadata: ''
-  commit: 9d67f8
+  commit: 80e5fa
   timestamp:
     year: 2020
     month: 10

+ 26 - 36
package-lock.json

@@ -488,9 +488,9 @@
             }
         },
         "node_modules/@babel/parser": {
-            "version": "7.16.0",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.0.tgz",
-            "integrity": "sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A==",
+            "version": "7.16.2",
+            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz",
+            "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==",
             "bin": {
                 "parser": "bin/babel-parser.js"
             },
@@ -499,9 +499,9 @@
             }
         },
         "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
-            "version": "7.16.0",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.0.tgz",
-            "integrity": "sha512-djyecbGMEh4rOb/Tc1M5bUW2Ih1IZRa9PoubnPOCzM+DRE89uGUHR1Y+3aDdTMW4drjGRZ2ol8dt1JUFg6hJLQ==",
+            "version": "7.16.2",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz",
+            "integrity": "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==",
             "dependencies": {
                 "@babel/helper-plugin-utils": "^7.14.5"
             },
@@ -3843,9 +3843,9 @@
             "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
         },
         "node_modules/electron-to-chromium": {
-            "version": "1.3.885",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz",
-            "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg=="
+            "version": "1.3.886",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz",
+            "integrity": "sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ=="
         },
         "node_modules/elliptic": {
             "version": "6.5.4",
@@ -6010,11 +6010,6 @@
             "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
             "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
         },
-        "node_modules/nanocolors": {
-            "version": "0.1.12",
-            "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz",
-            "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ=="
-        },
         "node_modules/nanoid": {
             "version": "3.1.30",
             "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
@@ -8300,16 +8295,16 @@
             }
         },
         "node_modules/svgo": {
-            "version": "2.7.0",
-            "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.7.0.tgz",
-            "integrity": "sha512-aDLsGkre4fTDCWvolyW+fs8ZJFABpzLXbtdK1y71CKnHzAnpDxKXPj2mNKj+pyOXUCzFHzuxRJ94XOFygOWV3w==",
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+            "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
             "dependencies": {
                 "@trysound/sax": "0.2.0",
                 "commander": "^7.2.0",
                 "css-select": "^4.1.3",
                 "css-tree": "^1.1.3",
                 "csso": "^4.2.0",
-                "nanocolors": "^0.1.12",
+                "picocolors": "^1.0.0",
                 "stable": "^0.1.8"
             },
             "bin": {
@@ -9677,14 +9672,14 @@
             }
         },
         "@babel/parser": {
-            "version": "7.16.0",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.0.tgz",
-            "integrity": "sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A=="
+            "version": "7.16.2",
+            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz",
+            "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw=="
         },
         "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
-            "version": "7.16.0",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.0.tgz",
-            "integrity": "sha512-djyecbGMEh4rOb/Tc1M5bUW2Ih1IZRa9PoubnPOCzM+DRE89uGUHR1Y+3aDdTMW4drjGRZ2ol8dt1JUFg6hJLQ==",
+            "version": "7.16.2",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz",
+            "integrity": "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==",
             "requires": {
                 "@babel/helper-plugin-utils": "^7.14.5"
             }
@@ -12204,9 +12199,9 @@
             "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
         },
         "electron-to-chromium": {
-            "version": "1.3.885",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz",
-            "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg=="
+            "version": "1.3.886",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz",
+            "integrity": "sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ=="
         },
         "elliptic": {
             "version": "6.5.4",
@@ -13810,11 +13805,6 @@
             "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
             "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
         },
-        "nanocolors": {
-            "version": "0.1.12",
-            "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz",
-            "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ=="
-        },
         "nanoid": {
             "version": "3.1.30",
             "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
@@ -15470,16 +15460,16 @@
             }
         },
         "svgo": {
-            "version": "2.7.0",
-            "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.7.0.tgz",
-            "integrity": "sha512-aDLsGkre4fTDCWvolyW+fs8ZJFABpzLXbtdK1y71CKnHzAnpDxKXPj2mNKj+pyOXUCzFHzuxRJ94XOFygOWV3w==",
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+            "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
             "requires": {
                 "@trysound/sax": "0.2.0",
                 "commander": "^7.2.0",
                 "css-select": "^4.1.3",
                 "css-tree": "^1.1.3",
                 "csso": "^4.2.0",
-                "nanocolors": "^0.1.12",
+                "picocolors": "^1.0.0",
                 "stable": "^0.1.8"
             }
         },