diff --git a/externals/mimemailparser/Attachment.php b/externals/mimemailparser/Attachment.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Attachment.php
@@ -0,0 +1,276 @@
+<?php
+
+namespace PhpMimeMailParser;
+
+use function var_dump;
+
+/**
+ * Attachment of php-mime-mail-parser
+ *
+ * Fully Tested Mailparse Extension Wrapper for PHP 5.4+
+ *
+ */
+class Attachment
+{
+    /**
+     * @var string $filename Filename
+     */
+    protected $filename;
+
+    /**
+     * @var string $contentType Mime Type
+     */
+    protected $contentType;
+
+    /**
+     * @var string $content File Content
+     */
+    protected $content;
+
+    /**
+     * @var string $contentDisposition Content-Disposition (attachment or inline)
+     */
+    protected $contentDisposition;
+
+    /**
+     * @var string $contentId Content-ID
+     */
+    protected $contentId;
+
+    /**
+     * @var array $headers An Array of the attachment headers
+     */
+    protected $headers;
+
+    /**
+     * @var resource $stream
+     */
+    protected $stream;
+
+    /**
+     * @var string $mimePartStr
+     */
+    protected $mimePartStr;
+
+    /**
+     * @var integer $maxDuplicateNumber
+     */
+    public $maxDuplicateNumber = 100;
+
+    /**
+     * Attachment constructor.
+     *
+     * @param string   $filename
+     * @param string   $contentType
+     * @param resource $stream
+     * @param string   $contentDisposition
+     * @param string   $contentId
+     * @param array    $headers
+     * @param string   $mimePartStr
+     */
+    public function __construct(
+        $filename,
+        $contentType,
+        $stream,
+        $contentDisposition = 'attachment',
+        $contentId = '',
+        $headers = [],
+        $mimePartStr = ''
+    ) {
+        $this->filename = $filename;
+        $this->contentType = $contentType;
+        $this->stream = $stream;
+        $this->content = null;
+        $this->contentDisposition = $contentDisposition;
+        $this->contentId = $contentId;
+        $this->headers = $headers;
+        $this->mimePartStr = $mimePartStr;
+    }
+
+    /**
+     * retrieve the attachment filename
+     *
+     * @return string
+     */
+    public function getFilename()
+    {
+        return $this->filename;
+    }
+
+    /**
+     * Retrieve the Attachment Content-Type
+     *
+     * @return string
+     */
+    public function getContentType()
+    {
+        return $this->contentType;
+    }
+
+    /**
+     * Retrieve the Attachment Content-Disposition
+     *
+     * @return string
+     */
+    public function getContentDisposition()
+    {
+        return $this->contentDisposition;
+    }
+
+    /**
+     * Retrieve the Attachment Content-ID
+     *
+     * @return string
+     */
+    public function getContentID()
+    {
+        return $this->contentId;
+    }
+
+    /**
+     * Retrieve the Attachment Headers
+     *
+     * @return array
+     */
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Get a handle to the stream
+     *
+     * @return resource
+     */
+    public function getStream()
+    {
+        return $this->stream;
+    }
+
+    /**
+     * Rename a file if it already exists at its destination.
+     * Renaming is done by adding a duplicate number to the file name. E.g. existingFileName_1.ext.
+     * After a max duplicate number, renaming the file will switch over to generating a random suffix.
+     *
+     * @param string $fileName  Complete path to the file.
+     * @return string           The suffixed file name.
+     */
+    protected function suffixFileName(string $fileName): string
+    {
+        $pathInfo = pathinfo($fileName);
+        $dirname = $pathInfo['dirname'].DIRECTORY_SEPARATOR;
+        $filename = $pathInfo['filename'];
+        $extension  = empty($pathInfo['extension']) ? '' : '.'.$pathInfo['extension'];
+
+        $i = 0;
+        do {
+            $i++;
+
+            if ($i > $this->maxDuplicateNumber) {
+                $duplicateExtension = uniqid();
+            } else {
+                $duplicateExtension = $i;
+            }
+
+            $resultName = $dirname.$filename."_$duplicateExtension".$extension;
+        } while (file_exists($resultName));
+
+        return $resultName;
+    }
+
+    /**
+     * Read the contents a few bytes at a time until completed
+     * Once read to completion, it always returns false
+     *
+     * @param int $bytes (default: 2082)
+     *
+     * @return string|bool
+     */
+    public function read($bytes = 2082)
+    {
+        return feof($this->stream) ? false : fread($this->stream, $bytes);
+    }
+
+    /**
+     * Retrieve the file content in one go
+     * Once you retrieve the content you cannot use MimeMailParser_attachment::read()
+     *
+     * @return string
+     */
+    public function getContent()
+    {
+        if ($this->content === null) {
+            fseek($this->stream, 0);
+            while (($buf = $this->read()) !== false) {
+                $this->content .= $buf;
+            }
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * Get mime part string for this attachment
+     *
+     * @return string
+     */
+    public function getMimePartStr()
+    {
+        return $this->mimePartStr;
+    }
+
+    /**
+     * Save the attachment individually
+     *
+     * @param string $attach_dir
+     * @param string $filenameStrategy
+     *
+     * @return string
+     */
+    public function save(
+        $attach_dir,
+        $filenameStrategy = Parser::ATTACHMENT_DUPLICATE_SUFFIX
+    ) {
+        $attach_dir = rtrim($attach_dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
+        if (!is_dir($attach_dir)) {
+            mkdir($attach_dir);
+        }
+
+        // Determine filename
+        switch ($filenameStrategy) {
+            case Parser::ATTACHMENT_RANDOM_FILENAME:
+                $fileInfo = pathinfo($this->getFilename());
+                $extension  = empty($fileInfo['extension']) ? '' : '.'.$fileInfo['extension'];
+                $attachment_path = $attach_dir.uniqid().$extension;
+                break;
+            case Parser::ATTACHMENT_DUPLICATE_THROW:
+            case Parser::ATTACHMENT_DUPLICATE_SUFFIX:
+                $attachment_path = $attach_dir.$this->getFilename();
+                break;
+            default:
+                throw new Exception('Invalid filename strategy argument provided.');
+        }
+
+        // Handle duplicate filename
+        if (file_exists($attachment_path)) {
+            switch ($filenameStrategy) {
+                case Parser::ATTACHMENT_DUPLICATE_THROW:
+                    throw new Exception('Could not create file for attachment: duplicate filename.');
+                case Parser::ATTACHMENT_DUPLICATE_SUFFIX:
+                    $attachment_path = $this->suffixFileName($attachment_path);
+                    break;
+            }
+        }
+
+        /** @var resource $fp */
+        if ($fp = fopen($attachment_path, 'w')) {
+            while ($bytes = $this->read()) {
+                fwrite($fp, $bytes);
+            }
+            fclose($fp);
+            return realpath($attachment_path);
+        } else {
+            throw new Exception('Could not write attachments. Your directory may be unwritable by PHP.');
+        }
+    }
+}
diff --git a/externals/mimemailparser/Charset.php b/externals/mimemailparser/Charset.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Charset.php
@@ -0,0 +1,338 @@
+<?php namespace PhpMimeMailParser;
+
+use PhpMimeMailParser\Contracts\CharsetManager;
+
+class Charset implements CharsetManager
+{
+    /**
+     * Charset Aliases
+     */
+    private $charsetAlias = [
+        'ascii'                    => 'us-ascii',
+        'us-ascii'                 => 'us-ascii',
+        'ansi_x3.4-1968'           => 'us-ascii',
+        '646'                      => 'us-ascii',
+        'iso-8859-1'               => 'ISO-8859-1',
+        'iso-8859-2'               => 'ISO-8859-2',
+        'iso-8859-3'               => 'ISO-8859-3',
+        'iso-8859-4'               => 'ISO-8859-4',
+        'iso-8859-5'               => 'ISO-8859-5',
+        'iso-8859-6'               => 'ISO-8859-6',
+        'iso-8859-6-i'             => 'ISO-8859-6-I',
+        'iso-8859-6-e'             => 'ISO-8859-6-E',
+        'iso-8859-7'               => 'ISO-8859-7',
+        'iso-8859-8'               => 'ISO-8859-8',
+        'iso-8859-8-i'             => 'ISO-8859-8',
+        'iso-8859-8-e'             => 'ISO-8859-8-E',
+        'iso-8859-9'               => 'ISO-8859-9',
+        'iso-8859-10'              => 'ISO-8859-10',
+        'iso-8859-11'              => 'ISO-8859-11',
+        'iso-8859-13'              => 'ISO-8859-13',
+        'iso-8859-14'              => 'ISO-8859-14',
+        'iso-8859-15'              => 'ISO-8859-15',
+        'iso-8859-16'              => 'ISO-8859-16',
+        'iso-ir-111'               => 'ISO-IR-111',
+        'iso-2022-cn'              => 'ISO-2022-CN',
+        'iso-2022-cn-ext'          => 'ISO-2022-CN',
+        'iso-2022-kr'              => 'ISO-2022-KR',
+        'iso-2022-jp'              => 'ISO-2022-JP',
+        'utf-16be'                 => 'UTF-16BE',
+        'utf-16le'                 => 'UTF-16LE',
+        'utf-16'                   => 'UTF-16',
+        'windows-1250'             => 'windows-1250',
+        'windows-1251'             => 'windows-1251',
+        'windows-1252'             => 'windows-1252',
+        'windows-1253'             => 'windows-1253',
+        'windows-1254'             => 'windows-1254',
+        'windows-1255'             => 'windows-1255',
+        'windows-1256'             => 'windows-1256',
+        'windows-1257'             => 'windows-1257',
+        'windows-1258'             => 'windows-1258',
+        'ibm866'                   => 'IBM866',
+        'ibm850'                   => 'IBM850',
+        'ibm852'                   => 'IBM852',
+        'ibm855'                   => 'IBM855',
+        'ibm857'                   => 'IBM857',
+        'ibm862'                   => 'IBM862',
+        'ibm864'                   => 'IBM864',
+        'utf-8'                    => 'UTF-8',
+        'utf-7'                    => 'UTF-7',
+        'shift_jis'                => 'Shift_JIS',
+        'big5'                     => 'Big5',
+        'euc-jp'                   => 'EUC-JP',
+        'euc-kr'                   => 'EUC-KR',
+        'gb2312'                   => 'GB2312',
+        'gb18030'                  => 'gb18030',
+        'viscii'                   => 'VISCII',
+        'koi8-r'                   => 'KOI8-R',
+        'koi8_r'                   => 'KOI8-R',
+        'cskoi8r'                  => 'KOI8-R',
+        'koi'                      => 'KOI8-R',
+        'koi8'                     => 'KOI8-R',
+        'koi8-u'                   => 'KOI8-U',
+        'tis-620'                  => 'TIS-620',
+        't.61-8bit'                => 'T.61-8bit',
+        'hz-gb-2312'               => 'HZ-GB-2312',
+        'big5-hkscs'               => 'Big5-HKSCS',
+        'gbk'                      => 'gbk',
+        'cns11643'                 => 'x-euc-tw',
+        'x-imap4-modified-utf7'    => 'x-imap4-modified-utf7',
+        'x-euc-tw'                 => 'x-euc-tw',
+        'x-mac-ce'                 => 'x-mac-ce',
+        'x-mac-turkish'            => 'x-mac-turkish',
+        'x-mac-greek'              => 'x-mac-greek',
+        'x-mac-icelandic'          => 'x-mac-icelandic',
+        'x-mac-croatian'           => 'x-mac-croatian',
+        'x-mac-romanian'           => 'x-mac-romanian',
+        'x-mac-cyrillic'           => 'x-mac-cyrillic',
+        'x-mac-ukrainian'          => 'x-mac-cyrillic',
+        'x-mac-hebrew'             => 'x-mac-hebrew',
+        'x-mac-arabic'             => 'x-mac-arabic',
+        'x-mac-farsi'              => 'x-mac-farsi',
+        'x-mac-devanagari'         => 'x-mac-devanagari',
+        'x-mac-gujarati'           => 'x-mac-gujarati',
+        'x-mac-gurmukhi'           => 'x-mac-gurmukhi',
+        'armscii-8'                => 'armscii-8',
+        'x-viet-tcvn5712'          => 'x-viet-tcvn5712',
+        'x-viet-vps'               => 'x-viet-vps',
+        'iso-10646-ucs-2'          => 'UTF-16BE',
+        'x-iso-10646-ucs-2-be'     => 'UTF-16BE',
+        'x-iso-10646-ucs-2-le'     => 'UTF-16LE',
+        'x-user-defined'           => 'x-user-defined',
+        'x-johab'                  => 'x-johab',
+        'latin1'                   => 'ISO-8859-1',
+        'iso_8859-1'               => 'ISO-8859-1',
+        'iso8859-1'                => 'ISO-8859-1',
+        'iso8859-2'                => 'ISO-8859-2',
+        'iso8859-3'                => 'ISO-8859-3',
+        'iso8859-4'                => 'ISO-8859-4',
+        'iso8859-5'                => 'ISO-8859-5',
+        'iso8859-6'                => 'ISO-8859-6',
+        'iso8859-7'                => 'ISO-8859-7',
+        'iso8859-8'                => 'ISO-8859-8',
+        'iso8859-9'                => 'ISO-8859-9',
+        'iso8859-10'               => 'ISO-8859-10',
+        'iso8859-11'               => 'ISO-8859-11',
+        'iso8859-13'               => 'ISO-8859-13',
+        'iso8859-14'               => 'ISO-8859-14',
+        'iso8859-15'               => 'ISO-8859-15',
+        'iso_8859-1:1987'          => 'ISO-8859-1',
+        'iso-ir-100'               => 'ISO-8859-1',
+        'l1'                       => 'ISO-8859-1',
+        'ibm819'                   => 'ISO-8859-1',
+        'cp819'                    => 'ISO-8859-1',
+        'csisolatin1'              => 'ISO-8859-1',
+        'latin2'                   => 'ISO-8859-2',
+        'iso_8859-2'               => 'ISO-8859-2',
+        'iso_8859-2:1987'          => 'ISO-8859-2',
+        'iso-ir-101'               => 'ISO-8859-2',
+        'l2'                       => 'ISO-8859-2',
+        'csisolatin2'              => 'ISO-8859-2',
+        'latin3'                   => 'ISO-8859-3',
+        'iso_8859-3'               => 'ISO-8859-3',
+        'iso_8859-3:1988'          => 'ISO-8859-3',
+        'iso-ir-109'               => 'ISO-8859-3',
+        'l3'                       => 'ISO-8859-3',
+        'csisolatin3'              => 'ISO-8859-3',
+        'latin4'                   => 'ISO-8859-4',
+        'iso_8859-4'               => 'ISO-8859-4',
+        'iso_8859-4:1988'          => 'ISO-8859-4',
+        'iso-ir-110'               => 'ISO-8859-4',
+        'l4'                       => 'ISO-8859-4',
+        'csisolatin4'              => 'ISO-8859-4',
+        'cyrillic'                 => 'ISO-8859-5',
+        'iso_8859-5'               => 'ISO-8859-5',
+        'iso_8859-5:1988'          => 'ISO-8859-5',
+        'iso-ir-144'               => 'ISO-8859-5',
+        'csisolatincyrillic'       => 'ISO-8859-5',
+        'arabic'                   => 'ISO-8859-6',
+        'iso_8859-6'               => 'ISO-8859-6',
+        'iso_8859-6:1987'          => 'ISO-8859-6',
+        'iso-ir-127'               => 'ISO-8859-6',
+        'ecma-114'                 => 'ISO-8859-6',
+        'asmo-708'                 => 'ISO-8859-6',
+        'csisolatinarabic'         => 'ISO-8859-6',
+        'csiso88596i'              => 'ISO-8859-6-I',
+        'csiso88596e'              => 'ISO-8859-6-E',
+        'greek'                    => 'ISO-8859-7',
+        'greek8'                   => 'ISO-8859-7',
+        'sun_eu_greek'             => 'ISO-8859-7',
+        'iso_8859-7'               => 'ISO-8859-7',
+        'iso_8859-7:1987'          => 'ISO-8859-7',
+        'iso-ir-126'               => 'ISO-8859-7',
+        'elot_928'                 => 'ISO-8859-7',
+        'ecma-118'                 => 'ISO-8859-7',
+        'csisolatingreek'          => 'ISO-8859-7',
+        'hebrew'                   => 'ISO-8859-8',
+        'iso_8859-8'               => 'ISO-8859-8',
+        'visual'                   => 'ISO-8859-8',
+        'iso_8859-8:1988'          => 'ISO-8859-8',
+        'iso-ir-138'               => 'ISO-8859-8',
+        'csisolatinhebrew'         => 'ISO-8859-8',
+        'csiso88598i'              => 'ISO-8859-8',
+        'iso-8859-8i'              => 'ISO-8859-8',
+        'logical'                  => 'ISO-8859-8',
+        'csiso88598e'              => 'ISO-8859-8-E',
+        'latin5'                   => 'ISO-8859-9',
+        'iso_8859-9'               => 'ISO-8859-9',
+        'iso_8859-9:1989'          => 'ISO-8859-9',
+        'iso-ir-148'               => 'ISO-8859-9',
+        'l5'                       => 'ISO-8859-9',
+        'csisolatin5'              => 'ISO-8859-9',
+        'unicode-1-1-utf-8'        => 'UTF-8',
+        'utf8'                     => 'UTF-8',
+        'x-sjis'                   => 'Shift_JIS',
+        'shift-jis'                => 'Shift_JIS',
+        'ms_kanji'                 => 'Shift_JIS',
+        'csshiftjis'               => 'Shift_JIS',
+        'windows-31j'              => 'Shift_JIS',
+        'cp932'                    => 'Shift_JIS',
+        'sjis'                     => 'Shift_JIS',
+        'cseucpkdfmtjapanese'      => 'EUC-JP',
+        'x-euc-jp'                 => 'EUC-JP',
+        'csiso2022jp'              => 'ISO-2022-JP',
+        'iso-2022-jp-2'            => 'ISO-2022-JP',
+        'csiso2022jp2'             => 'ISO-2022-JP',
+        'csbig5'                   => 'Big5',
+        'cn-big5'                  => 'Big5',
+        'x-x-big5'                 => 'Big5',
+        'zh_tw-big5'               => 'Big5',
+        'cseuckr'                  => 'EUC-KR',
+        'ks_c_5601-1987'           => 'EUC-KR',
+        'iso-ir-149'               => 'EUC-KR',
+        'ks_c_5601-1989'           => 'EUC-KR',
+        'ksc_5601'                 => 'EUC-KR',
+        'ksc5601'                  => 'EUC-KR',
+        'korean'                   => 'EUC-KR',
+        'csksc56011987'            => 'EUC-KR',
+        '5601'                     => 'EUC-KR',
+        'windows-949'              => 'EUC-KR',
+        'gb_2312-80'               => 'GB2312',
+        'iso-ir-58'                => 'GB2312',
+        'chinese'                  => 'GB2312',
+        'csiso58gb231280'          => 'GB2312',
+        'csgb2312'                 => 'GB2312',
+        'zh_cn.euc'                => 'GB2312',
+        'gb_2312'                  => 'GB2312',
+        'x-cp1250'                 => 'windows-1250',
+        'x-cp1251'                 => 'windows-1251',
+        'x-cp1252'                 => 'windows-1252',
+        'x-cp1253'                 => 'windows-1253',
+        'x-cp1254'                 => 'windows-1254',
+        'x-cp1255'                 => 'windows-1255',
+        'x-cp1256'                 => 'windows-1256',
+        'x-cp1257'                 => 'windows-1257',
+        'x-cp1258'                 => 'windows-1258',
+        'windows-874'              => 'windows-874',
+        'ibm874'                   => 'windows-874',
+        'dos-874'                  => 'windows-874',
+        'macintosh'                => 'macintosh',
+        'x-mac-roman'              => 'macintosh',
+        'mac'                      => 'macintosh',
+        'csmacintosh'              => 'macintosh',
+        'cp866'                    => 'IBM866',
+        'cp-866'                   => 'IBM866',
+        '866'                      => 'IBM866',
+        'csibm866'                 => 'IBM866',
+        'cp850'                    => 'IBM850',
+        '850'                      => 'IBM850',
+        'csibm850'                 => 'IBM850',
+        'cp852'                    => 'IBM852',
+        '852'                      => 'IBM852',
+        'csibm852'                 => 'IBM852',
+        'cp855'                    => 'IBM855',
+        '855'                      => 'IBM855',
+        'csibm855'                 => 'IBM855',
+        'cp857'                    => 'IBM857',
+        '857'                      => 'IBM857',
+        'csibm857'                 => 'IBM857',
+        'cp862'                    => 'IBM862',
+        '862'                      => 'IBM862',
+        'csibm862'                 => 'IBM862',
+        'cp864'                    => 'IBM864',
+        '864'                      => 'IBM864',
+        'csibm864'                 => 'IBM864',
+        'ibm-864'                  => 'IBM864',
+        't.61'                     => 'T.61-8bit',
+        'iso-ir-103'               => 'T.61-8bit',
+        'csiso103t618bit'          => 'T.61-8bit',
+        'x-unicode-2-0-utf-7'      => 'UTF-7',
+        'unicode-2-0-utf-7'        => 'UTF-7',
+        'unicode-1-1-utf-7'        => 'UTF-7',
+        'csunicode11utf7'          => 'UTF-7',
+        'csunicode'                => 'UTF-16BE',
+        'csunicode11'              => 'UTF-16BE',
+        'iso-10646-ucs-basic'      => 'UTF-16BE',
+        'csunicodeascii'           => 'UTF-16BE',
+        'iso-10646-unicode-latin1' => 'UTF-16BE',
+        'csunicodelatin1'          => 'UTF-16BE',
+        'iso-10646'                => 'UTF-16BE',
+        'iso-10646-j-1'            => 'UTF-16BE',
+        'latin6'                   => 'ISO-8859-10',
+        'iso-ir-157'               => 'ISO-8859-10',
+        'l6'                       => 'ISO-8859-10',
+        'csisolatin6'              => 'ISO-8859-10',
+        'iso_8859-15'              => 'ISO-8859-15',
+        'csisolatin9'              => 'ISO-8859-15',
+        'l9'                       => 'ISO-8859-15',
+        'ecma-cyrillic'            => 'ISO-IR-111',
+        'csiso111ecmacyrillic'     => 'ISO-IR-111',
+        'csiso2022kr'              => 'ISO-2022-KR',
+        'csviscii'                 => 'VISCII',
+        'zh_tw-euc'                => 'x-euc-tw',
+        'iso88591'                 => 'ISO-8859-1',
+        'iso88592'                 => 'ISO-8859-2',
+        'iso88593'                 => 'ISO-8859-3',
+        'iso88594'                 => 'ISO-8859-4',
+        'iso88595'                 => 'ISO-8859-5',
+        'iso88596'                 => 'ISO-8859-6',
+        'iso88597'                 => 'ISO-8859-7',
+        'iso88598'                 => 'ISO-8859-8',
+        'iso88599'                 => 'ISO-8859-9',
+        'iso885910'                => 'ISO-8859-10',
+        'iso885911'                => 'ISO-8859-11',
+        'iso885912'                => 'ISO-8859-12',
+        'iso885913'                => 'ISO-8859-13',
+        'iso885914'                => 'ISO-8859-14',
+        'iso885915'                => 'ISO-8859-15',
+        'tis620'                   => 'TIS-620',
+        'cp1250'                   => 'windows-1250',
+        'cp1251'                   => 'windows-1251',
+        'cp1252'                   => 'windows-1252',
+        'cp1253'                   => 'windows-1253',
+        'cp1254'                   => 'windows-1254',
+        'cp1255'                   => 'windows-1255',
+        'cp1256'                   => 'windows-1256',
+        'cp1257'                   => 'windows-1257',
+        'cp1258'                   => 'windows-1258',
+        'x-gbk'                    => 'gbk',
+        'windows-936'              => 'gbk',
+        'ansi-1251'                => 'windows-1251',
+    ];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function decodeCharset($encodedString, $charset)
+    {
+        if (strtolower($charset) == 'utf-8' || strtolower($charset) == 'us-ascii') {
+            return $encodedString;
+        } else {
+            return iconv($this->getCharsetAlias($charset), 'UTF-8//TRANSLIT//IGNORE', $encodedString);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCharsetAlias($charset)
+    {
+        $charset = strtolower($charset);
+
+        if (array_key_exists($charset, $this->charsetAlias)) {
+            return $this->charsetAlias[$charset];
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/externals/mimemailparser/Contracts/CharsetManager.php b/externals/mimemailparser/Contracts/CharsetManager.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Contracts/CharsetManager.php
@@ -0,0 +1,24 @@
+<?php namespace PhpMimeMailParser\Contracts;
+
+interface CharsetManager
+{
+
+    /**
+     * Decode the string from Charset
+     *
+     * @param string $encodedString The string in its original encoded state
+     * @param string $charset       The Charset header of the part.
+     *
+     * @return string The decoded string
+     */
+    public function decodeCharset($encodedString, $charset);
+
+    /**
+     * Get charset alias
+     *
+     * @param string $charset .
+     *
+     * @return string The charset alias
+     */
+    public function getCharsetAlias($charset);
+}
diff --git a/externals/mimemailparser/Contracts/Middleware.php b/externals/mimemailparser/Contracts/Middleware.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Contracts/Middleware.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace PhpMimeMailParser\Contracts;
+
+use PhpMimeMailParser\MimePart;
+use PhpMimeMailParser\MiddlewareStack;
+
+/**
+ * Process Mime parts by either:
+ *  processing the part or calling the $next MiddlewareStack
+ */
+interface Middleware
+{
+    /**
+     *  Process a mime part, optionally delegating parsing to the $next MiddlewareStack
+     *
+     * @param MimePart $part
+     * @param MiddlewareStack $next
+     *
+     * @return MimePart
+     */
+    public function parse(MimePart $part, MiddlewareStack $next);
+}
diff --git a/externals/mimemailparser/Exception.php b/externals/mimemailparser/Exception.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Exception.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpMimeMailParser;
+
+class Exception extends \RuntimeException
+{
+
+}
diff --git a/externals/mimemailparser/LICENSE b/externals/mimemailparser/LICENSE
--- a/externals/mimemailparser/LICENSE
+++ b/externals/mimemailparser/LICENSE
@@ -1,125 +1,21 @@
-			 The "Artistic License"
-
-				Preamble
-
-The intent of this document is to state the conditions under which a
-Package may be copied, such that the Copyright Holder maintains some
-semblance of artistic control over the development of the package,
-while giving the users of the package the right to use and distribute
-the Package in a more-or-less customary fashion, plus the right to make
-reasonable modifications.
-
-Definitions:
-
-	"Package" refers to the collection of files distributed by the
-	Copyright Holder, and derivatives of that collection of files
-	created through textual modification.
-
-	"Standard Version" refers to such a Package if it has not been
-	modified, or has been modified in accordance with the wishes
-	of the Copyright Holder as specified below.
-
-	"Copyright Holder" is whoever is named in the copyright or
-	copyrights for the package.
-
-	"You" is you, if you're thinking about copying or distributing
-	this Package.
-
-	"Reasonable copying fee" is whatever you can justify on the
-	basis of media cost, duplication charges, time of people involved,
-	and so on.  (You will not be required to justify it to the
-	Copyright Holder, but only to the computing community at large
-	as a market that must bear the fee.)
-
-	"Freely Available" means that no fee is charged for the item
-	itself, though there may be fees involved in handling the item.
-	It also means that recipients of the item may redistribute it
-	under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the
-Standard Version of this Package without restriction, provided that you
-duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications
-derived from the Public Domain or from the Copyright Holder.  A Package
-modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided
-that you insert a prominent notice in each changed file stating how and
-when you changed that file, and provided that you do at least ONE of the
-following:
-
-    a) place your modifications in the Public Domain or otherwise make them
-    Freely Available, such as by posting said modifications to Usenet or
-    an equivalent medium, or placing the modifications on a major archive
-    site such as uunet.uu.net, or by allowing the Copyright Holder to include
-    your modifications in the Standard Version of the Package.
-
-    b) use the modified Package only within your corporation or organization.
-
-    c) rename any non-standard executables so the names do not conflict
-    with standard executables, which must also be provided, and provide
-    a separate manual page for each non-standard executable that clearly
-    documents how it differs from the Standard Version.
-
-    d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or
-executable form, provided that you do at least ONE of the following:
-
-    a) distribute a Standard Version of the executables and library files,
-    together with instructions (in the manual page or equivalent) on where
-    to get the Standard Version.
-
-    b) accompany the distribution with the machine-readable source of
-    the Package with your modifications.
-
-    c) give non-standard executables non-standard names, and clearly
-    document the differences in manual pages (or equivalent), together
-    with instructions on where to get the Standard Version.
-
-    d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this
-Package.  You may charge any fee you choose for support of this
-Package.  You may not charge a fee for this Package itself.  However,
-you may distribute this Package in aggregate with other (possibly
-commercial) programs as part of a larger (possibly commercial) software
-distribution provided that you do not advertise this Package as a
-product of your own.  You may embed this Package's interpreter within
-an executable of yours (by linking); this shall be construed as a mere
-form of aggregation, provided that the complete Standard Version of the
-interpreter is so embedded.
-
-6. The scripts and library files supplied as input to or produced as
-output from the programs of this Package do not automatically fall
-under the copyright of this Package, but belong to whoever generated
-them, and may be sold commercially, and may be aggregated with this
-Package.  If such scripts or library files are aggregated with this
-Package via the so-called "undump" or "unexec" methods of producing a
-binary executable image, then distribution of such an image shall
-neither be construed as a distribution of this Package nor shall it
-fall under the restrictions of Paragraphs 3 and 4, provided that you do
-not represent such an executable image as a Standard Version of this
-Package.
-
-7. C subroutines (or comparably compiled subroutines in other
-languages) supplied by you and linked into this Package in order to
-emulate subroutines and variables of the language defined by this
-Package shall not be considered part of this Package, but are the
-equivalent of input as in Paragraph 6, provided these subroutines do
-not change the language in any way that would cause it to fail the
-regression tests for the language.
-
-8. Aggregation of this Package with a commercial distribution is always
-permitted provided that the use of this Package is embedded; that is,
-when no overt attempt is made to make this Package's interfaces visible
-to the end user of the commercial distribution.  Such use shall not be
-construed as a distribution of this Package.
-
-9. The name of the Copyright Holder may not be used to endorse or promote
-products derived from this software without specific prior written permission.
-
-10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+The MIT License (MIT)
+
+Copyright (c) 2016 Vincent Dauce
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/externals/mimemailparser/Middleware.php b/externals/mimemailparser/Middleware.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Middleware.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace PhpMimeMailParser;
+
+/**
+ * Wraps a callable as a Middleware
+ */
+class Middleware implements Contracts\Middleware
+{
+    protected $parser;
+
+    /**
+     * Create a middleware using a callable $fn
+     *
+     * @param callable $fn
+     */
+    public function __construct(callable $fn)
+    {
+        $this->parser = $fn;
+    }
+
+    /**
+     * Process a mime part, optionally delegating parsing to the $next MiddlewareStack
+     */
+    public function parse(MimePart $part, MiddlewareStack $next)
+    {
+        return call_user_func($this->parser, $part, $next);
+    }
+}
diff --git a/externals/mimemailparser/MiddlewareStack.php b/externals/mimemailparser/MiddlewareStack.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/MiddlewareStack.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace PhpMimeMailParser;
+
+use PhpMimeMailParser\Contracts\MiddleWare as MiddleWareContracts;
+
+/**
+ * A stack of middleware chained together by (MiddlewareStack $next)
+ */
+class MiddlewareStack
+{
+    /**
+     * Next MiddlewareStack in chain
+     *
+     * @var MiddlewareStack
+     */
+    protected $next;
+
+    /**
+     * Middleware in this MiddlewareStack
+     *
+     * @var Middleware
+     */
+    protected $middleware;
+
+    /**
+     * Construct the first middleware in this MiddlewareStack
+     * The next middleware is chained through $MiddlewareStack->add($Middleware)
+     *
+     * @param Middleware $middleware
+     */
+    public function __construct(MiddleWareContracts $middleware = null)
+    {
+        $this->middleware = $middleware;
+    }
+
+    /**
+     * Creates a chained middleware in MiddlewareStack
+     *
+     * @param Middleware $middleware
+     * @return MiddlewareStack Immutable MiddlewareStack
+     */
+    public function add(MiddleWareContracts $middleware)
+    {
+        $stack = new static($middleware);
+        $stack->next = $this;
+        return $stack;
+    }
+
+    /**
+     * Parses the MimePart by passing it through the Middleware
+     * @param MimePart $part
+     * @return MimePart
+     */
+    public function parse(MimePart $part)
+    {
+        if (!$this->middleware) {
+            return $part;
+        }
+        $part = call_user_func(array($this->middleware, 'parse'), $part, $this->next);
+        return $part;
+    }
+
+    /**
+     * Creates a MiddlewareStack based on an array of middleware
+     *
+     * @param Middleware[] $middlewares
+     * @return MiddlewareStack
+     */
+    public static function factory(array $middlewares = array())
+    {
+        $stack = new static;
+        foreach ($middlewares as $middleware) {
+            $stack = $stack->add($middleware);
+        }
+        return $stack;
+    }
+
+    /**
+     * Allow calling MiddlewareStack instance directly to invoke parse()
+     *
+     * @param MimePart $part
+     * @return MimePart
+     */
+    public function __invoke(MimePart $part)
+    {
+        return $this->parse($part);
+    }
+}
diff --git a/externals/mimemailparser/MimeMailParser.class.php b/externals/mimemailparser/MimeMailParser.class.php
deleted file mode 100644
--- a/externals/mimemailparser/MimeMailParser.class.php
+++ /dev/null
@@ -1,494 +0,0 @@
-<?php
-
-require_once('attachment.class.php');
-
-/**
- * Fast Mime Mail parser Class using PHP's MailParse Extension
- * @author gabe@fijiwebdesign.com
- * @url http://www.fijiwebdesign.com/
- * @license http://creativecommons.org/licenses/by-sa/3.0/us/
- * @version $Id$
- */
-class MimeMailParser {
-
-	/**
-	 * PHP MimeParser Resource ID
-	 */
-	public $resource;
-
-	/**
-	 * A file pointer to email
-	 */
-	public $stream;
-
-	/**
-	 * A text of an email
-	 */
-	public $data;
-
-	/**
-	 * Stream Resources for Attachments
-	 */
-	public $attachment_streams;
-
-	/**
-	 * Parts of an email
-	 */
-	private $parts = array();
-
-	/**
-	 * Inialize some stuff
-	 * @return
-	 */
-	public function __construct() {
-		$this->attachment_streams = array();
-	}
-
-	/**
-	 * Free the held resouces
-	 * @return void
-	 */
-	public function __destruct() {
-		// clear the email file resource
-		if (is_resource($this->stream)) {
-			fclose($this->stream);
-		}
-		// clear the MailParse resource
-		if (is_resource($this->resource)) {
-			mailparse_msg_free($this->resource);
-		}
-		// remove attachment resources
-		foreach($this->attachment_streams as $stream) {
-			fclose($stream);
-		}
-	}
-
-	/**
-	 * Set the file path we use to get the email text
-	 * @return Object MimeMailParser Instance
-	 * @param $mail_path Object
-	 */
-	public function setPath($path) {
-		// should parse message incrementally from file
-		$this->resource = mailparse_msg_parse_file($path);
-		$this->stream = fopen($path, 'r');
-		$this->parse();
-		return $this;
-	}
-
-	/**
-	 * Set the Stream resource we use to get the email text
-	 * @return Object MimeMailParser Instance
-	 * @param $stream Resource
-	 */
-	public function setStream($stream) {
-
-		// streams have to be cached to file first
-		if (get_resource_type($stream) == 'stream') {
-			$tmp_fp = tmpfile();
-			if ($tmp_fp) {
-				while(!feof($stream)) {
-					fwrite($tmp_fp, fread($stream, 2028));
-				}
-				fseek($tmp_fp, 0);
-				$this->stream =& $tmp_fp;
-			} else {
-				throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.');
-				return false;
-			}
-			fclose($stream);
-		} else {
-			$this->stream = $stream;
-		}
-
-		$this->resource = mailparse_msg_create();
-		// parses the message incrementally low memory usage but slower
-		while(!feof($this->stream)) {
-			mailparse_msg_parse($this->resource, fread($this->stream, 2082));
-		}
-		$this->parse();
-		return $this;
-	}
-
-	/**
-	 * Set the email text
-	 * @return Object MimeMailParser Instance
-	 * @param $data String
-	 */
-	public function setText($data) {
-	// NOTE: This has been modified for Phabricator. If the input data does not
-	// end in a newline, Mailparse fails to include the last line in the mail
-	// body. This happens somewhere deep, deep inside the mailparse extension,
-	// so adding a newline here seems like the most straightforward fix.
-	if (!preg_match('/\n\z/', $data)) {
-	  $data = $data."\n";
-	}
-
-		$this->resource = mailparse_msg_create();
-		// does not parse incrementally, fast memory hog might explode
-		mailparse_msg_parse($this->resource, $data);
-		$this->data = $data;
-		$this->parse();
-		return $this;
-	}
-
-	/**
-	 * Parse the Message into parts
-	 * @return void
-	 * @private
-	 */
-	private function parse() {
-		$structure = mailparse_msg_get_structure($this->resource);
-		$this->parts = array();
-		foreach($structure as $part_id) {
-			$part = mailparse_msg_get_part($this->resource, $part_id);
-			$this->parts[$part_id] = mailparse_msg_get_part_data($part);
-		}
-	}
-
-	/**
-	 * Retrieve the Email Headers
-	 * @return Array
-	 */
-	public function getHeaders() {
-		if (isset($this->parts[1])) {
-			return $this->getPartHeaders($this->parts[1]);
-		} else {
-			throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.');
-		}
-		return false;
-	}
-	/**
-	 * Retrieve the raw Email Headers
-	 * @return string
-	 */
-	public function getHeadersRaw() {
-		if (isset($this->parts[1])) {
-			return $this->getPartHeaderRaw($this->parts[1]);
-		} else {
-			throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.');
-		}
-		return false;
-	}
-
-	/**
-	 * Retrieve a specific Email Header
-	 * @return String
-	 * @param $name String Header name
-	 */
-	public function getHeader($name) {
-		if (isset($this->parts[1])) {
-			$headers = $this->getPartHeaders($this->parts[1]);
-			if (isset($headers[$name])) {
-				return $headers[$name];
-			}
-		} else {
-			throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.');
-		}
-		return false;
-	}
-
-	/**
-	 * Returns the email message body in the specified format
-	 * @return Mixed String Body or False if not found
-	 * @param $type Object[optional]
-	 */
-	public function getMessageBody($type = 'text') {
-
-	  // NOTE: This function has been modified for Phabricator. The default
-	  // implementation returns the last matching part, which throws away text
-	  // for many emails. Instead, we concatenate all matching parts. See
-	  // issue 22 for discussion:
-	  // http://code.google.com/p/php-mime-mail-parser/issues/detail?id=22
-
-		$body = false;
-		$mime_types = array(
-			'text'=> 'text/plain',
-			'html'=> 'text/html'
-		);
-		if (in_array($type, array_keys($mime_types))) {
-			foreach($this->parts as $part) {
-			$disposition = $this->getPartContentDisposition($part);
-			if ($disposition == 'attachment') {
-			  // text/plain parts with "Content-Disposition: attachment" are
-			  // attachments, not part of the text body.
-			  continue;
-			}
-				if ($this->getPartContentType($part) == $mime_types[$type]) {
-		  $headers = $this->getPartHeaders($part);
-		  // Concatenate all the matching parts into the body text. For example,
-		  // if a user sends a message with some text, then an image, and then
-		  // some more text, the text body of the email gets split over several
-		  // attachments.
-					$body .= $this->decode(
-					  $this->getPartBody($part),
-					  array_key_exists('content-transfer-encoding', $headers)
-						? $headers['content-transfer-encoding']
-						: '');
-				}
-			}
-		} else {
-			throw new Exception('Invalid type specified for MimeMailParser::getMessageBody. "type" can either be text or html.');
-		}
-		return $body;
-	}
-
-	/**
-	 * get the headers for the message body part.
-	 * @return Array
-	 * @param $type Object[optional]
-	 */
-	public function getMessageBodyHeaders($type = 'text') {
-		$headers = false;
-		$mime_types = array(
-			'text'=> 'text/plain',
-			'html'=> 'text/html'
-		);
-		if (in_array($type, array_keys($mime_types))) {
-			foreach($this->parts as $part) {
-				if ($this->getPartContentType($part) == $mime_types[$type]) {
-					$headers = $this->getPartHeaders($part);
-				}
-			}
-		} else {
-			throw new Exception('Invalid type specified for MimeMailParser::getMessageBody. "type" can either be text or html.');
-		}
-		return $headers;
-	}
-
-	/**
-	 * Returns the attachments contents in order of appearance
-	 * @return Array
-	 * @param $type Object[optional]
-	 */
-	public function getAttachments() {
-    // NOTE: This has been modified for Phabricator. Some mail clients do not
-    // send attachments with "Content-Disposition" headers.
-		$attachments = array();
-		$dispositions = array("attachment","inline");
-		$non_attachment_types = array("text/plain", "text/html");
-		$nonameIter = 0;
-		foreach ($this->parts as $part) {
-			$disposition = $this->getPartContentDisposition($part);
-			$filename = 'noname';
-			if (isset($part['disposition-filename'])) {
-				$filename = $part['disposition-filename'];
-			} elseif (isset($part['content-name'])) {
-				// if we have no disposition but we have a content-name, it's a valid attachment.
-				// we simulate the presence of an attachment disposition with a disposition filename
-				$filename = $part['content-name'];
-				$disposition = 'attachment';
-			} elseif (!in_array($part['content-type'], $non_attachment_types, true)
-				&& substr($part['content-type'], 0, 10) !== 'multipart/'
-				) {
-				// if we cannot get it with getMessageBody, we assume it is an attachment
-				$disposition = 'attachment';
-			}
-
-			if (in_array($disposition, $dispositions) && isset($filename) === true) {
-				if ($filename == 'noname') {
-					$nonameIter++;
-					$filename = 'noname'.$nonameIter;
-				}
-				$attachments[] = new MimeMailParser_attachment(
-					$filename,
-					$this->getPartContentType($part),
-					$this->getAttachmentStream($part),
-					$disposition,
-					$this->getPartHeaders($part)
-				);
-			}
-		}
-		return $attachments;
-	}
-
-	/**
-	 * Return the Headers for a MIME part
-	 * @return Array
-	 * @param $part Array
-	 */
-	private function getPartHeaders($part) {
-		if (isset($part['headers']) && $part['headers']) {
-			return $part['headers'];
-		}
-		throw new Exception('MimeMailParser::getHeaders() could not parse any email headers.');
-	}
-
-	/**
-	 * Return a Specific Header for a MIME part
-	 * @return Array
-	 * @param $part Array
-	 * @param $header String Header Name
-	 */
-	private function getPartHeader($part, $header) {
-		if (isset($part['headers'][$header])) {
-			return $part['headers'][$header];
-		}
-		return false;
-	}
-
-	/**
-	 * Return the ContentType of the MIME part
-	 * @return String
-	 * @param $part Array
-	 */
-	private function getPartContentType($part) {
-		if (isset($part['content-type'])) {
-			return $part['content-type'];
-		}
-		return false;
-	}
-
-	/**
-	 * Return the Content Disposition
-	 * @return String
-	 * @param $part Array
-	 */
-	private function getPartContentDisposition($part) {
-		if (isset($part['content-disposition'])) {
-			return $part['content-disposition'];
-		}
-		return false;
-	}
-
-	/**
-	 * Retrieve the raw Header of a MIME part
-	 * @return String
-	 * @param $part Object
-	 */
-	private function getPartHeaderRaw(&$part) {
-		$header = '';
-		if ($this->stream) {
-			$header = $this->getPartHeaderFromFile($part);
-		} else if ($this->data) {
-			$header = $this->getPartHeaderFromText($part);
-		} else {
-			throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.');
-		}
-		return $header;
-	}
-	/**
-	 * Retrieve the Body of a MIME part
-	 * @return String
-	 * @param $part Object
-	 */
-	private function getPartBody(&$part) {
-		$body = '';
-		if ($this->stream) {
-			$body = $this->getPartBodyFromFile($part);
-		} else if ($this->data) {
-			$body = $this->getPartBodyFromText($part);
-		} else {
-			throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.');
-		}
-		return $body;
-	}
-
-	/**
-	 * Retrieve the Header from a MIME part from file
-	 * @return String Mime Header Part
-	 * @param $part Array
-	 */
-	private function getPartHeaderFromFile(&$part) {
-		$start = $part['starting-pos'];
-		$end = $part['starting-pos-body'];
-		fseek($this->stream, $start, SEEK_SET);
-		$header = fread($this->stream, $end-$start);
-		return $header;
-	}
-	/**
-	 * Retrieve the Body from a MIME part from file
-	 * @return String Mime Body Part
-	 * @param $part Array
-	 */
-	private function getPartBodyFromFile(&$part) {
-		$start = $part['starting-pos-body'];
-		$end = $part['ending-pos-body'];
-		fseek($this->stream, $start, SEEK_SET);
-		$body = fread($this->stream, $end-$start);
-		return $body;
-	}
-
-	/**
-	 * Retrieve the Header from a MIME part from text
-	 * @return String Mime Header Part
-	 * @param $part Array
-	 */
-	private function getPartHeaderFromText(&$part) {
-		$start = $part['starting-pos'];
-		$end = $part['starting-pos-body'];
-		$header = substr($this->data, $start, $end-$start);
-		return $header;
-	}
-	/**
-	 * Retrieve the Body from a MIME part from text
-	 * @return String Mime Body Part
-	 * @param $part Array
-	 */
-	private function getPartBodyFromText(&$part) {
-		$start = $part['starting-pos-body'];
-		$end = $part['ending-pos-body'];
-		$body = substr($this->data, $start, $end-$start);
-		return $body;
-	}
-
-	/**
-	 * Read the attachment Body and save temporary file resource
-	 * @return String Mime Body Part
-	 * @param $part Array
-	 */
-	private function getAttachmentStream(&$part) {
-		$temp_fp = tmpfile();
-
-		array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = '';
-
-		if ($temp_fp) {
-			if ($this->stream) {
-				$start = $part['starting-pos-body'];
-				$end = $part['ending-pos-body'];
-				fseek($this->stream, $start, SEEK_SET);
-				$len = $end-$start;
-				$written = 0;
-				$write = 2028;
-				$body = '';
-				while($written < $len) {
-					if (($written+$write < $len )) {
-						$write = $len - $written;
-					}
-					$part = fread($this->stream, $write);
-					fwrite($temp_fp, $this->decode($part, $encoding));
-					$written += $write;
-				}
-			} else if ($this->data) {
-				$attachment = $this->decode($this->getPartBodyFromText($part), $encoding);
-				fwrite($temp_fp, $attachment, strlen($attachment));
-			}
-			fseek($temp_fp, 0, SEEK_SET);
-		} else {
-			throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.');
-			return false;
-		}
-		return $temp_fp;
-	}
-
-
-	/**
-	 * Decode the string depending on encoding type.
-	 * @return String the decoded string.
-	 * @param $encodedString	The string in its original encoded state.
-	 * @param $encodingType		The encoding type from the Content-Transfer-Encoding header of the part.
-	 */
-	private function decode($encodedString, $encodingType) {
-		if (strtolower($encodingType) == 'base64') {
-			return base64_decode($encodedString);
-		} else if (strtolower($encodingType) == 'quoted-printable') {
-			 return quoted_printable_decode($encodedString);
-		} else {
-			return $encodedString;
-		}
-	}
-
-}
-
-
-?>
diff --git a/externals/mimemailparser/MimePart.php b/externals/mimemailparser/MimePart.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/MimePart.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace PhpMimeMailParser;
+
+/**
+ * Mime Part
+ * Represents the results of mailparse_msg_get_part_data()
+ *
+ * Note ArrayAccess::offsetSet() cannot modify deeply nestated arrays.
+ * When modifying use getPart() and setPart() for deep nested data modification
+ *
+ * @example
+ *
+ *     $MimePart['headers']['from'] = 'modified@example.com' // fails
+ *
+ *     // correct
+ *     $part = $MimePart->getPart();
+ *     $part['headers']['from'] = 'modified@example.com';
+ *     $MimePart->setPart($part);
+ */
+class MimePart implements \ArrayAccess
+{
+    /**
+     * Internal mime part
+     *
+     * @var array
+     */
+    protected $part = array();
+
+    /**
+     * Immutable Part Id
+     *
+     * @var string
+     */
+    private $id;
+
+    /**
+     * Create a mime part
+     *
+     * @param array $part
+     * @param string $id
+     */
+    public function __construct($id, array $part)
+    {
+        $this->part = $part;
+        $this->id = $id;
+    }
+
+    /**
+     * Retrieve the part Id
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Retrieve the part data
+     *
+     * @return array
+     */
+    public function getPart()
+    {
+        return $this->part;
+    }
+
+    /**
+     * Set the mime part data
+     *
+     * @param array $part
+     * @return void
+     */
+    public function setPart(array $part)
+    {
+        $this->part = $part;
+    }
+
+    /**
+     * ArrayAccess
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetSet($offset, $value)
+    {
+        if (is_null($offset)) {
+            $this->part[] = $value;
+            return;
+        }
+        $this->part[$offset] = $value;
+    }
+
+    /**
+     * ArrayAccess
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetExists($offset)
+    {
+        return isset($this->part[$offset]);
+    }
+
+    /**
+     * ArrayAccess
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetUnset($offset)
+    {
+        unset($this->part[$offset]);
+    }
+
+    /**
+     * ArrayAccess
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetGet($offset)
+    {
+        return isset($this->part[$offset]) ? $this->part[$offset] : null;
+    }
+}
diff --git a/externals/mimemailparser/Parser.php b/externals/mimemailparser/Parser.php
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/Parser.php
@@ -0,0 +1,923 @@
+<?php
+
+namespace PhpMimeMailParser;
+
+use PhpMimeMailParser\Contracts\CharsetManager;
+
+/**
+ * Parser of php-mime-mail-parser
+ *
+ * Fully Tested Mailparse Extension Wrapper for PHP 5.4+
+ *
+ */
+class Parser
+{
+    /**
+     * Attachment filename argument option for ->saveAttachments().
+     */
+    const ATTACHMENT_DUPLICATE_THROW  = 'DuplicateThrow';
+    const ATTACHMENT_DUPLICATE_SUFFIX = 'DuplicateSuffix';
+    const ATTACHMENT_RANDOM_FILENAME  = 'RandomFilename';
+
+    /**
+     * PHP MimeParser Resource ID
+     *
+     * @var resource $resource
+     */
+    protected $resource;
+
+    /**
+     * A file pointer to email
+     *
+     * @var resource $stream
+     */
+    protected $stream;
+
+    /**
+     * A text of an email
+     *
+     * @var string $data
+     */
+    protected $data;
+
+    /**
+     * Parts of an email
+     *
+     * @var array $parts
+     */
+    protected $parts;
+
+    /**
+     * @var CharsetManager object
+     */
+    protected $charset;
+
+    /**
+     * Valid stream modes for reading
+     *
+     * @var array
+     */
+    protected static $readableModes = [
+        'r', 'r+', 'w+', 'a+', 'x+', 'c+', 'rb', 'r+b', 'w+b', 'a+b',
+        'x+b', 'c+b', 'rt', 'r+t', 'w+t', 'a+t', 'x+t', 'c+t'
+    ];
+
+    /**
+     * Stack of middleware registered to process data
+     *
+     * @var MiddlewareStack
+     */
+    protected $middlewareStack;
+
+    /**
+     * Parser constructor.
+     *
+     * @param CharsetManager|null $charset
+     */
+    public function __construct(CharsetManager $charset = null)
+    {
+        if ($charset == null) {
+            $charset = new Charset();
+        }
+
+        $this->charset = $charset;
+        $this->middlewareStack = new MiddlewareStack();
+    }
+
+    /**
+     * Free the held resources
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        // clear the email file resource
+        if (is_resource($this->stream)) {
+            fclose($this->stream);
+        }
+        // clear the MailParse resource
+        if (is_resource($this->resource)) {
+            mailparse_msg_free($this->resource);
+        }
+    }
+
+    /**
+     * Set the file path we use to get the email text
+     *
+     * @param string $path File path to the MIME mail
+     *
+     * @return Parser MimeMailParser Instance
+     */
+    public function setPath($path)
+    {
+        if (is_writable($path)) {
+            $file = fopen($path, 'a+');
+            fseek($file, -1, SEEK_END);
+            if (fread($file, 1) != "\n") {
+                fwrite($file, PHP_EOL);
+            }
+            fclose($file);
+        }
+
+        // should parse message incrementally from file
+        $this->resource = mailparse_msg_parse_file($path);
+        $this->stream = fopen($path, 'r');
+        $this->parse();
+
+        return $this;
+    }
+
+    /**
+     * Set the Stream resource we use to get the email text
+     *
+     * @param resource $stream
+     *
+     * @return Parser MimeMailParser Instance
+     * @throws Exception
+     */
+    public function setStream($stream)
+    {
+        // streams have to be cached to file first
+        $meta = @stream_get_meta_data($stream);
+        if (!$meta || !$meta['mode'] || !in_array($meta['mode'], self::$readableModes, true)) {
+            throw new Exception(
+                'setStream() expects parameter stream to be readable stream resource.'
+            );
+        }
+
+        /** @var resource $tmp_fp */
+        $tmp_fp = tmpfile();
+        if ($tmp_fp) {
+            while (!feof($stream)) {
+                fwrite($tmp_fp, fread($stream, 2028));
+            }
+
+            if (fread($tmp_fp, 1) != "\n") {
+                fwrite($tmp_fp, PHP_EOL);
+            }
+
+            fseek($tmp_fp, 0);
+            $this->stream = &$tmp_fp;
+        } else {
+            throw new Exception(
+                'Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'
+            );
+        }
+        fclose($stream);
+
+        $this->resource = mailparse_msg_create();
+        // parses the message incrementally (low memory usage but slower)
+        while (!feof($this->stream)) {
+            mailparse_msg_parse($this->resource, fread($this->stream, 2082));
+        }
+        $this->parse();
+
+        return $this;
+    }
+
+    /**
+     * Set the email text
+     *
+     * @param string $data
+     *
+     * @return Parser MimeMailParser Instance
+     */
+    public function setText($data)
+    {
+        if (empty($data)) {
+            throw new Exception('You must not call MimeMailParser::setText with an empty string parameter');
+        }
+
+        if (substr($data, -1) != "\n") {
+            $data = $data.PHP_EOL;
+        }
+
+        $this->resource = mailparse_msg_create();
+        // does not parse incrementally, fast memory hog might explode
+        mailparse_msg_parse($this->resource, $data);
+        $this->data = $data;
+        $this->parse();
+
+        return $this;
+    }
+
+    /**
+     * Parse the Message into parts
+     *
+     * @return void
+     */
+    protected function parse()
+    {
+        $structure = mailparse_msg_get_structure($this->resource);
+        $this->parts = [];
+        foreach ($structure as $part_id) {
+            $part = mailparse_msg_get_part($this->resource, $part_id);
+            $part_data = mailparse_msg_get_part_data($part);
+            $mimePart = new MimePart($part_id, $part_data);
+            // let each middleware parse the part before saving
+            $this->parts[$part_id] = $this->middlewareStack->parse($mimePart)->getPart();
+        }
+    }
+
+    /**
+     * Retrieve a specific Email Header, without charset conversion.
+     *
+     * @param string $name Header name (case-insensitive)
+     *
+     * @return string|bool
+     * @throws Exception
+     */
+    public function getRawHeader($name)
+    {
+        $name = strtolower($name);
+        if (isset($this->parts[1])) {
+            $headers = $this->getPart('headers', $this->parts[1]);
+
+            return isset($headers[$name]) ? $headers[$name] : false;
+        } else {
+            throw new Exception(
+                'setPath() or setText() or setStream() must be called before retrieving email headers.'
+            );
+        }
+    }
+
+    /**
+     * Retrieve a specific Email Header
+     *
+     * @param string $name Header name (case-insensitive)
+     *
+     * @return string|false
+     */
+    public function getHeader($name)
+    {
+        $rawHeader = $this->getRawHeader($name);
+        if ($rawHeader === false) {
+            return false;
+        }
+
+        return $this->decodeHeader($rawHeader);
+    }
+
+    /**
+     * Retrieve all mail headers
+     *
+     * @return array
+     * @throws Exception
+     */
+    public function getHeaders()
+    {
+        if (isset($this->parts[1])) {
+            $headers = $this->getPart('headers', $this->parts[1]);
+            foreach ($headers as &$value) {
+                if (is_array($value)) {
+                    foreach ($value as &$v) {
+                        $v = $this->decodeSingleHeader($v);
+                    }
+                } else {
+                    $value = $this->decodeSingleHeader($value);
+                }
+            }
+
+            return $headers;
+        } else {
+            throw new Exception(
+                'setPath() or setText() or setStream() must be called before retrieving email headers.'
+            );
+        }
+    }
+
+    /**
+     * Retrieve the raw mail headers as a string
+     *
+     * @return string
+     * @throws Exception
+     */
+    public function getHeadersRaw()
+    {
+        if (isset($this->parts[1])) {
+            return $this->getPartHeader($this->parts[1]);
+        } else {
+            throw new Exception(
+                'setPath() or setText() or setStream() must be called before retrieving email headers.'
+            );
+        }
+    }
+
+    /**
+     * Retrieve the raw Header of a MIME part
+     *
+     * @return String
+     * @param $part Object
+     * @throws Exception
+     */
+    protected function getPartHeader(&$part)
+    {
+        $header = '';
+        if ($this->stream) {
+            $header = $this->getPartHeaderFromFile($part);
+        } elseif ($this->data) {
+            $header = $this->getPartHeaderFromText($part);
+        }
+        return $header;
+    }
+
+    /**
+     * Retrieve the Header from a MIME part from file
+     *
+     * @return String Mime Header Part
+     * @param $part Array
+     */
+    protected function getPartHeaderFromFile(&$part)
+    {
+        $start = $part['starting-pos'];
+        $end = $part['starting-pos-body'];
+        fseek($this->stream, $start, SEEK_SET);
+        $header = fread($this->stream, $end - $start);
+        return $header;
+    }
+
+    /**
+     * Retrieve the Header from a MIME part from text
+     *
+     * @return String Mime Header Part
+     * @param $part Array
+     */
+    protected function getPartHeaderFromText(&$part)
+    {
+        $start = $part['starting-pos'];
+        $end = $part['starting-pos-body'];
+        $header = substr($this->data, $start, $end - $start);
+        return $header;
+    }
+
+    /**
+     * Checks whether a given part ID is a child of another part
+     * eg. an RFC822 attachment may have one or more text parts
+     *
+     * @param string $partId
+     * @param string $parentPartId
+     * @return bool
+     */
+    protected function partIdIsChildOfPart($partId, $parentPartId)
+    {
+        $parentPartId = $parentPartId.'.';
+        return substr($partId, 0, strlen($parentPartId)) == $parentPartId;
+    }
+
+    /**
+     * Whether the given part ID is a child of any attachment part in the message.
+     *
+     * @param string $checkPartId
+     * @return bool
+     */
+    protected function partIdIsChildOfAnAttachment($checkPartId)
+    {
+        foreach ($this->parts as $partId => $part) {
+            if ($this->getPart('content-disposition', $part) == 'attachment') {
+                if ($this->partIdIsChildOfPart($checkPartId, $partId)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the email message body in the specified format
+     *
+     * @param string $type text, html or htmlEmbedded
+     *
+     * @return string Body
+     * @throws Exception
+     */
+    public function getMessageBody($type = 'text')
+    {
+        $mime_types = [
+            'text'         => 'text/plain',
+            'html'         => 'text/html',
+            'htmlEmbedded' => 'text/html',
+        ];
+
+        if (in_array($type, array_keys($mime_types))) {
+            $part_type = $type === 'htmlEmbedded' ? 'html' : $type;
+            $inline_parts = $this->getInlineParts($part_type);
+            $body = empty($inline_parts) ? '' : $inline_parts[0];
+        } else {
+            throw new Exception(
+                'Invalid type specified for getMessageBody(). Expected: text, html or htmlEmbeded.'
+            );
+        }
+
+        if ($type == 'htmlEmbedded') {
+            $attachments = $this->getAttachments();
+            foreach ($attachments as $attachment) {
+                if ($attachment->getContentID() != '') {
+                    $body = str_replace(
+                        '"cid:'.$attachment->getContentID().'"',
+                        '"'.$this->getEmbeddedData($attachment->getContentID()).'"',
+                        $body
+                    );
+                }
+            }
+        }
+
+        return $body;
+    }
+
+    /**
+     * Returns the embedded data structure
+     *
+     * @param string $contentId Content-Id
+     *
+     * @return string
+     */
+    protected function getEmbeddedData($contentId)
+    {
+        foreach ($this->parts as $part) {
+            if ($this->getPart('content-id', $part) == $contentId) {
+                $embeddedData = 'data:';
+                $embeddedData .= $this->getPart('content-type', $part);
+                $embeddedData .= ';'.$this->getPart('transfer-encoding', $part);
+                $embeddedData .= ','.$this->getPartBody($part);
+                return $embeddedData;
+            }
+        }
+        return '';
+    }
+
+    /**
+     * Return an array with the following keys display, address, is_group
+     *
+     * @param string $name Header name (case-insensitive)
+     *
+     * @return array
+     */
+    public function getAddresses($name)
+    {
+        $value = $this->getRawHeader($name);
+        $value = (is_array($value)) ? $value[0] : $value;
+        $addresses = mailparse_rfc822_parse_addresses($value);
+        foreach ($addresses as $i => $item) {
+            $addresses[$i]['display'] = $this->decodeHeader($item['display']);
+        }
+        return $addresses;
+    }
+
+    /**
+     * Returns the attachments contents in order of appearance
+     *
+     * @return Attachment[]
+     */
+    public function getInlineParts($type = 'text')
+    {
+        $inline_parts = [];
+        $mime_types = [
+            'text'         => 'text/plain',
+            'html'         => 'text/html',
+        ];
+
+        if (!in_array($type, array_keys($mime_types))) {
+            throw new Exception('Invalid type specified for getInlineParts(). "type" can either be text or html.');
+        }
+
+        foreach ($this->parts as $partId => $part) {
+            if ($this->getPart('content-type', $part) == $mime_types[$type]
+                && $this->getPart('content-disposition', $part) != 'attachment'
+                && !$this->partIdIsChildOfAnAttachment($partId)
+            ) {
+                $headers = $this->getPart('headers', $part);
+                $encodingType = array_key_exists('content-transfer-encoding', $headers) ?
+                    $headers['content-transfer-encoding'] : '';
+                $undecoded_body = $this->decodeContentTransfer($this->getPartBody($part), $encodingType);
+                $inline_parts[] = $this->charset->decodeCharset($undecoded_body, $this->getPartCharset($part));
+            }
+        }
+
+        return $inline_parts;
+    }
+
+    /**
+     * Returns the attachments contents in order of appearance
+     *
+     * @return Attachment[]
+     */
+    public function getAttachments($include_inline = true)
+    {
+        $attachments = [];
+        $dispositions = $include_inline ? ['attachment', 'inline'] : ['attachment'];
+        $non_attachment_types = ['text/plain', 'text/html'];
+        $nonameIter = 0;
+
+        foreach ($this->parts as $part) {
+            $disposition = $this->getPart('content-disposition', $part);
+            $filename = 'noname';
+
+            if (isset($part['disposition-filename'])) {
+                $filename = $this->decodeHeader($part['disposition-filename']);
+            } elseif (isset($part['content-name'])) {
+                // if we have no disposition but we have a content-name, it's a valid attachment.
+                // we simulate the presence of an attachment disposition with a disposition filename
+                $filename = $this->decodeHeader($part['content-name']);
+                $disposition = 'attachment';
+            } elseif (in_array($part['content-type'], $non_attachment_types, true)
+                && $disposition !== 'attachment') {
+                // it is a message body, no attachment
+                continue;
+            } elseif (substr($part['content-type'], 0, 10) !== 'multipart/'
+                && $part['content-type'] !== 'text/plain; (error)' && $disposition != 'inline') {
+                // if we cannot get it by getMessageBody(), we assume it is an attachment
+                $disposition = 'attachment';
+            }
+            if (in_array($disposition, ['attachment', 'inline']) === false && !empty($disposition)) {
+                $disposition = 'attachment';
+            }
+
+            if (in_array($disposition, $dispositions) === true) {
+                if ($filename == 'noname') {
+                    $nonameIter++;
+                    $filename = 'noname'.$nonameIter;
+                } else {
+                    // Escape all potentially unsafe characters from the filename
+                    $filename = preg_replace('((^\.)|\/|[\n|\r|\n\r]|(\.$))', '_', $filename);
+                }
+
+                $headersAttachments = $this->getPart('headers', $part);
+                $contentidAttachments = $this->getPart('content-id', $part);
+
+                $attachmentStream = $this->getAttachmentStream($part);
+                $mimePartStr = $this->getPartComplete($part);
+
+                $attachments[] = new Attachment(
+                    $filename,
+                    $this->getPart('content-type', $part),
+                    $attachmentStream,
+                    $disposition,
+                    $contentidAttachments,
+                    $headersAttachments,
+                    $mimePartStr
+                );
+            }
+        }
+
+        return $attachments;
+    }
+
+    /**
+     * Save attachments in a folder
+     *
+     * @param string $attach_dir directory
+     * @param bool $include_inline
+     * @param string $filenameStrategy How to generate attachment filenames
+     *
+     * @return array Saved attachments paths
+     * @throws Exception
+     */
+    public function saveAttachments(
+        $attach_dir,
+        $include_inline = true,
+        $filenameStrategy = self::ATTACHMENT_DUPLICATE_SUFFIX
+    ) {
+        $attachments = $this->getAttachments($include_inline);
+
+        $attachments_paths = [];
+        foreach ($attachments as $attachment) {
+            $attachments_paths[] = $attachment->save($attach_dir, $filenameStrategy);
+        }
+
+        return $attachments_paths;
+    }
+
+    /**
+     * Read the attachment Body and save temporary file resource
+     *
+     * @param array $part
+     *
+     * @return resource Mime Body Part
+     * @throws Exception
+     */
+    protected function getAttachmentStream(&$part)
+    {
+        /** @var resource $temp_fp */
+        $temp_fp = tmpfile();
+
+        $headers = $this->getPart('headers', $part);
+        $encodingType = array_key_exists('content-transfer-encoding', $headers) ?
+            $headers['content-transfer-encoding'] : '';
+
+        if ($temp_fp) {
+            if ($this->stream) {
+                $start = $part['starting-pos-body'];
+                $end = $part['ending-pos-body'];
+                fseek($this->stream, $start, SEEK_SET);
+                $len = $end - $start;
+                $written = 0;
+                while ($written < $len) {
+                    $write = $len;
+                    $data = fread($this->stream, $write);
+                    fwrite($temp_fp, $this->decodeContentTransfer($data, $encodingType));
+                    $written += $write;
+                }
+            } elseif ($this->data) {
+                $attachment = $this->decodeContentTransfer($this->getPartBodyFromText($part), $encodingType);
+                fwrite($temp_fp, $attachment, strlen($attachment));
+            }
+            fseek($temp_fp, 0, SEEK_SET);
+        } else {
+            throw new Exception(
+                'Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'
+            );
+        }
+
+        return $temp_fp;
+    }
+
+    /**
+     * Decode the string from Content-Transfer-Encoding
+     *
+     * @param string $encodedString The string in its original encoded state
+     * @param string $encodingType  The encoding type from the Content-Transfer-Encoding header of the part.
+     *
+     * @return string The decoded string
+     */
+    protected function decodeContentTransfer($encodedString, $encodingType)
+    {
+        if (is_array($encodingType)) {
+            $encodingType = $encodingType[0];
+        }
+
+        $encodingType = strtolower($encodingType);
+        if ($encodingType == 'base64') {
+            return base64_decode($encodedString);
+        } elseif ($encodingType == 'quoted-printable') {
+            return quoted_printable_decode($encodedString);
+        } else {
+            return $encodedString;
+        }
+    }
+
+    /**
+     * $input can be a string or array
+     *
+     * @param string|array $input
+     *
+     * @return string
+     */
+    protected function decodeHeader($input)
+    {
+        //Sometimes we have 2 label From so we take only the first
+        if (is_array($input)) {
+            return $this->decodeSingleHeader($input[0]);
+        }
+
+        return $this->decodeSingleHeader($input);
+    }
+
+    /**
+     * Decodes a single header (= string)
+     *
+     * @param string $input
+     *
+     * @return string
+     */
+    protected function decodeSingleHeader($input)
+    {
+        // For each encoded-word...
+        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)((\s+)=\?)?/i', $input, $matches)) {
+            $encoded = $matches[1];
+            $charset = $matches[2];
+            $encoding = $matches[3];
+            $text = $matches[4];
+            $space = isset($matches[6]) ? $matches[6] : '';
+
+            switch (strtolower($encoding)) {
+                case 'b':
+                    $text = $this->decodeContentTransfer($text, 'base64');
+                    break;
+
+                case 'q':
+                    $text = str_replace('_', ' ', $text);
+                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
+                    foreach ($matches[1] as $value) {
+                        $text = str_replace('='.$value, chr(hexdec($value)), $text);
+                    }
+                    break;
+            }
+
+            $text = $this->charset->decodeCharset($text, $this->charset->getCharsetAlias($charset));
+            $input = str_replace($encoded.$space, $text, $input);
+        }
+
+        return $input;
+    }
+
+    /**
+     * Return the charset of the MIME part
+     *
+     * @param array $part
+     *
+     * @return string
+     */
+    protected function getPartCharset($part)
+    {
+        if (isset($part['charset'])) {
+            return $this->charset->getCharsetAlias($part['charset']);
+        } else {
+            return 'us-ascii';
+        }
+    }
+
+    /**
+     * Retrieve a specified MIME part
+     *
+     * @param string $type
+     * @param array  $parts
+     *
+     * @return string|array
+     */
+    protected function getPart($type, $parts)
+    {
+        return (isset($parts[$type])) ? $parts[$type] : false;
+    }
+
+    /**
+     * Retrieve the Body of a MIME part
+     *
+     * @param array $part
+     *
+     * @return string
+     */
+    protected function getPartBody(&$part)
+    {
+        $body = '';
+        if ($this->stream) {
+            $body = $this->getPartBodyFromFile($part);
+        } elseif ($this->data) {
+            $body = $this->getPartBodyFromText($part);
+        }
+
+        return $body;
+    }
+
+    /**
+     * Retrieve the Body from a MIME part from file
+     *
+     * @param array $part
+     *
+     * @return string Mime Body Part
+     */
+    protected function getPartBodyFromFile(&$part)
+    {
+        $start = $part['starting-pos-body'];
+        $end = $part['ending-pos-body'];
+        $body = '';
+        if ($end - $start > 0) {
+            fseek($this->stream, $start, SEEK_SET);
+            $body = fread($this->stream, $end - $start);
+        }
+
+        return $body;
+    }
+
+    /**
+     * Retrieve the Body from a MIME part from text
+     *
+     * @param array $part
+     *
+     * @return string Mime Body Part
+     */
+    protected function getPartBodyFromText(&$part)
+    {
+        $start = $part['starting-pos-body'];
+        $end = $part['ending-pos-body'];
+
+        return substr($this->data, $start, $end - $start);
+    }
+
+    /**
+     * Retrieve the content of a MIME part
+     *
+     * @param array $part
+     *
+     * @return string
+     */
+    protected function getPartComplete(&$part)
+    {
+        $body = '';
+        if ($this->stream) {
+            $body = $this->getPartFromFile($part);
+        } elseif ($this->data) {
+            $body = $this->getPartFromText($part);
+        }
+
+        return $body;
+    }
+
+    /**
+     * Retrieve the content from a MIME part from file
+     *
+     * @param array $part
+     *
+     * @return string Mime Content
+     */
+    protected function getPartFromFile(&$part)
+    {
+        $start = $part['starting-pos'];
+        $end = $part['ending-pos'];
+        $body = '';
+        if ($end - $start > 0) {
+            fseek($this->stream, $start, SEEK_SET);
+            $body = fread($this->stream, $end - $start);
+        }
+
+        return $body;
+    }
+
+    /**
+     * Retrieve the content from a MIME part from text
+     *
+     * @param array $part
+     *
+     * @return string Mime Content
+     */
+    protected function getPartFromText(&$part)
+    {
+        $start = $part['starting-pos'];
+        $end = $part['ending-pos'];
+
+        return substr($this->data, $start, $end - $start);
+    }
+
+    /**
+     * Retrieve the resource
+     *
+     * @return resource resource
+     */
+    public function getResource()
+    {
+        return $this->resource;
+    }
+
+    /**
+     * Retrieve the file pointer to email
+     *
+     * @return resource stream
+     */
+    public function getStream()
+    {
+        return $this->stream;
+    }
+
+    /**
+     * Retrieve the text of an email
+     *
+     * @return string data
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * Retrieve the parts of an email
+     *
+     * @return array parts
+     */
+    public function getParts()
+    {
+        return $this->parts;
+    }
+
+    /**
+     * Retrieve the charset manager object
+     *
+     * @return CharsetManager charset
+     */
+    public function getCharset()
+    {
+        return $this->charset;
+    }
+
+    /**
+     * Add a middleware to the parser MiddlewareStack
+     * Each middleware is invoked when:
+     *   a MimePart is retrieved by mailparse_msg_get_part_data() during $this->parse()
+     * The middleware will receive MimePart $part and the next MiddlewareStack $next
+     *
+     * Eg:
+     *
+     * $Parser->addMiddleware(function(MimePart $part, MiddlewareStack $next) {
+     *      // do something with the $part
+     *      return $next($part);
+     * });
+     *
+     * @param callable $middleware Plain Function or Middleware Instance to execute
+     * @return void
+     */
+    public function addMiddleware(callable $middleware)
+    {
+        if (!$middleware instanceof Middleware) {
+            $middleware = new Middleware($middleware);
+        }
+        $this->middlewareStack = $this->middlewareStack->add($middleware);
+    }
+}
diff --git a/externals/mimemailparser/README b/externals/mimemailparser/README
deleted file mode 100644
--- a/externals/mimemailparser/README
+++ /dev/null
@@ -1,3 +0,0 @@
-From:
-
-  http://code.google.com/p/php-mime-mail-parser/
diff --git a/externals/mimemailparser/README.md b/externals/mimemailparser/README.md
new file mode 100644
--- /dev/null
+++ b/externals/mimemailparser/README.md
@@ -0,0 +1,267 @@
+# php-mime-mail-parser
+
+A fully tested email parser for PHP 8.0+ (mailparse extension wrapper).
+
+It's the most effective PHP email parser around in terms of performance, foreign character encoding, attachment handling, and ease of use.
+Internet Message Format RFC [822](https://tools.ietf.org/html/rfc822), [2822](https://tools.ietf.org/html/rfc2822), [5322](https://tools.ietf.org/html/rfc5322).
+
+[![Latest Version](https://img.shields.io/packagist/v/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases)
+[![Total Downloads](https://img.shields.io/packagist/dt/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://packagist.org/packages/php-mime-mail-parser/php-mime-mail-parser)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+
+## Why?
+
+This extension can be used to...
+ * Parse and read email from Postfix
+ * Read messages (Filename extension: `.eml`)
+ * Create webmail 
+ * Store email information such a subject, HTML body, attachments, etc. into a database
+
+## Is it reliable?
+
+Yes. All known issues have been reproduced, fixed and tested.
+
+We use GitHub Actions, Codecov, Codacy to help ensure code quality. You can see real-time statistics below:
+
+[![CI](https://github.com/php-mime-mail-parser/php-mime-mail-parser/actions/workflows/main.yml/badge.svg?style=flat-square)](https://github.com/php-mime-mail-parser/php-mime-mail-parser/actions/workflows/main.yml)
+[![Coverage](https://codecov.io/gh/php-mime-mail-parser/php-mime-mail-parser/branch/main/graph/badge.svg?token=wTSIbXJDL0)](https://codecov.io/gh/php-mime-mail-parser/php-mime-mail-parser)
+[![Code Quality](https://app.codacy.com/project/badge/Grade/8cbfe0fcd84c4b2b9282b9a0b4467607)](https://www.codacy.com/gh/php-mime-mail-parser/php-mime-mail-parser/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=php-mime-mail-parser/php-mime-mail-parser&amp;utm_campaign=Badge_Grade)
+
+## How do I install it?
+
+The easiest way is via [Composer](https://getcomposer.org/).
+
+To install the latest version of PHP MIME Mail Parser, run the command below:
+
+	composer require php-mime-mail-parser/php-mime-mail-parser
+
+## Requirements
+
+The following versions of PHP are supported:
+
+* PHP 8.0
+* PHP 8.1
+* PHP 8.2
+* PHP 8.3
+
+Previous Versions:
+
+| PHP Compatibility | Version                     |
+|-------------------|-----------------------------|
+| HHVM              | [php-mime-mail-parser 2.11.1](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/2.11.1) |
+| PHP 5.4           | [php-mime-mail-parser 2.11.1](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/2.11.1) |
+| PHP 5.5           | [php-mime-mail-parser 2.11.1](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/2.11.1) |
+| PHP 5.6           | [php-mime-mail-parser 3.0.4](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/3.0.4)  |
+| PHP 7.0           | [php-mime-mail-parser 3.0.4](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/3.0.4)  |
+| PHP 7.1           | [php-mime-mail-parser 5.0.5](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/5.0.5)  |
+| PHP 7.2           | [php-mime-mail-parser 7.1.2](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/7.1.2)  |
+| PHP 7.3           | [php-mime-mail-parser 7.1.2](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/7.1.2)  |
+| PHP 7.4           | [php-mime-mail-parser 7.1.2](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases/tag/7.1.2)  |
+
+Make sure you have the mailparse extension (http://php.net/manual/en/book.mailparse.php) properly installed. The command line `php -m | grep mailparse` needs to return "mailparse".
+
+
+### Install mailparse extension
+
+#### Debian, Ubuntu & derivatives
+```
+sudo apt install php-cli php-mailparse
+```
+
+#### MacOS
+```
+brew install php
+pecl install mailparse
+```
+
+#### Other platforms
+```
+sudo apt install php-cli php-pear php-dev php-mbstring
+pecl install mailparse
+```
+
+#### From source
+
+AAAAMMDD should be `php-config --extension-dir`
+```
+git clone https://github.com/php/pecl-mail-mailparse.git
+cd pecl-mail-mailparse
+phpize
+./configure
+sed -i 's/#if\s!HAVE_MBSTRING/#ifndef MBFL_MBFILTER_H/' ./mailparse.c
+make
+sudo mv modules/mailparse.so /usr/lib/php/AAAAMMDD/
+echo "extension=mailparse.so" | sudo tee /etc/php/7.1/mods-available/mailparse.ini
+sudo phpenmod mailparse
+```
+
+#### Windows
+You need to download mailparse DLL from http://pecl.php.net/package/mailparse and add the line `extension=php_mailparse.dll` to `php.ini` accordingly.
+
+## How do I use it?
+
+### Loading an email
+
+You can load an email in 4 differents ways:
+
+```php
+require_once __DIR__.'/vendor/autoload.php';
+
+$path = 'path/to/email.eml';
+$parser = new PhpMimeMailParser\Parser();
+
+// 1. Either specify a file path (string)
+$parser->setPath($path); 
+
+// 2. or specify the raw mime mail text (string)
+$parser->setText(file_get_contents($path));
+
+// 3. or specify a php file resource (stream)
+$parser->setStream(fopen($path, "r"));
+
+// 4. or specify a stream to work with a mail server (stream)
+$parser->setStream(fopen("php://stdin", "r"));
+```
+
+### Get the metadata of the message
+
+Get the sender and the receiver:
+
+```php
+$rawHeaderTo = $parser->getHeader('to');
+// return "test" <test@example.com>, "test2" <test2@example.com>
+
+$arrayHeaderTo = $parser->getAddresses('to');
+// return [["display"=>"test", "address"=>"test@example.com", false]]
+
+$rawHeaderFrom = $parser->getHeader('from');
+// return "test" <test@example.com>
+
+$arrayHeaderFrom = $parser->getAddresses('from');
+// return [["display"=>"test", "address"=>"test@example.com", "is_group"=>false]]
+```
+
+Get the subject:
+
+```php
+$subject = $parser->getHeader('subject');
+```
+
+Get other headers:
+
+```php
+$stringHeaders = $parser->getHeadersRaw();
+// return all headers as a string, no charset conversion
+
+$arrayHeaders = $parser->getHeaders();
+// return all headers as an array, with charset conversion
+```
+
+### Get the body of the message
+
+```php
+$text = $parser->getMessageBody('text');
+// return the text version
+
+$html = $parser->getMessageBody('html');
+// return the html version
+
+$htmlEmbedded = $parser->getMessageBody('htmlEmbedded');
+// return the html version with the embedded contents like images
+
+```
+
+### Get attachments
+
+Save all attachments in a directory
+
+```php
+$parser->saveAttachments('/path/to/save/attachments/');
+// return all attachments saved in the directory (include inline attachments)
+
+$parser->saveAttachments('/path/to/save/attachments/', false);
+// return all attachments saved in the directory (exclude inline attachments)
+
+// Save all attachments with the strategy ATTACHMENT_DUPLICATE_SUFFIX (default)
+$parser->saveAttachments('/path/to/save/attachments/', false, Parser::ATTACHMENT_DUPLICATE_SUFFIX);
+// return all attachments saved in the directory: logo.jpg, logo_1.jpg, ..., logo_100.jpg, YY34UFHBJ.jpg
+
+// Save all attachments with the strategy ATTACHMENT_RANDOM_FILENAME
+$parser->saveAttachments('/path/to/save/attachments/', false, Parser::ATTACHMENT_RANDOM_FILENAME);
+// return all attachments saved in the directory: YY34UFHBJ.jpg and F98DBZ9FZF.jpg
+
+// Save all attachments with the strategy ATTACHMENT_DUPLICATE_THROW
+$parser->saveAttachments('/path/to/save/attachments/', false, Parser::ATTACHMENT_DUPLICATE_THROW);
+// return an exception when there is attachments duplicate.
+
+```
+
+Get all attachments
+
+```php
+$attachments = $parser->getAttachments();
+// return an array of all attachments (include inline attachments)
+
+$attachments = $parser->getAttachments(false);
+// return an array of all attachments (exclude inline attachments)
+```
+
+
+Loop through all attachments
+```php
+foreach ($attachments as $attachment) {
+    echo 'Filename : '.$attachment->getFilename().'<br>';
+    // return logo.jpg
+    
+    echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br>';
+    // return 1000
+    
+    echo 'Filetype : '.$attachment->getContentType().'<br>';
+    // return image/jpeg
+    
+    echo 'MIME part string : '.$attachment->getMimePartStr().'<br>';
+    // return the whole MIME part of the attachment
+    
+    $stream = $attachment->getStream();
+    // get the stream of the attachment file
+
+    $attachment->save('/path/to/save/myattachment/', Parser::ATTACHMENT_DUPLICATE_SUFFIX);
+    // return the path and the filename saved (same strategy available than saveAttachments)
+}
+```
+
+## Postfix configuration to manage email from a mail server
+
+To forward mails from [Postfix](http://www.postfix.org/) to the PHP script above, add this line at the end of your `/etc/postfix/master.cf`
+(to specify myhook to send all emails to the script `test.php`):
+
+```
+myhook unix - n n - - pipe
+  				flags=F user=www-data argv=php -c /etc/php5/apache2/php.ini -f /var/www/test.php ${sender} ${size} ${recipient}
+```
+
+Edit this line (register myhook)
+```
+smtp      inet  n       -       -       -       -       smtpd
+        			-o content_filter=myhook:dummy
+```
+
+The PHP script must use the fourth method (see above) to work with this configuration.
+
+And finally the easiest way is to use my SaaS https://mailcare.io
+
+
+## Can I contribute?
+
+Feel free to contribute!
+
+	git clone https://github.com/php-mime-mail-parser/php-mime-mail-parser
+	cd php-mime-mail-parser
+	composer install
+	./vendor/bin/phpunit
+
+If you report an issue, please provide the raw email that triggered it. This helps us reproduce the issue and fix it more quickly.
+
+## License
+
+The php-mime-mail-parser/php-mime-mail-parser is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
diff --git a/externals/mimemailparser/attachment.class.php b/externals/mimemailparser/attachment.class.php
deleted file mode 100644
--- a/externals/mimemailparser/attachment.class.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * Model of an Attachment
- */
-class MimeMailParser_attachment {
-
-	/**
-	 * @var $filename Filename
-	 */
-	public  $filename;
-	/**
-	 * @var $content_type Mime Type
-	 */
-	public  $content_type;
-	/**
-	 * @var $content File Content
-	 */
-	private  $content;
-	/**
-	 * @var $extension Filename extension
-	 */
-	private $extension;
-	/**
-	 * @var $content_disposition Content-Disposition (attachment or inline)
-	 */
-	public $content_disposition;
-	/**
-	 * @var $headers An Array of the attachment headers
-	 */
-	public $headers;
-	
-	private  $stream;
-
-	public function __construct($filename, $content_type, $stream, $content_disposition = 'attachment', $headers = array()) {
-		$this->filename = $filename;
-		$this->content_type = $content_type;
-		$this->stream = $stream;
-		$this->content = null;
-		$this->content_disposition = $content_disposition;
-		$this->headers = $headers;
-	}
-	
-	/**
-	 * retrieve the attachment filename
-	 * @return String
-	 */
-	public function getFilename() {
-		return $this->filename;
-	}
-	
-	/**
-	 * Retrieve the Attachment Content-Type
-	 * @return String
-	 */
-	public function getContentType() {
-		return $this->content_type;
-	}
-	
-	/**
-	 * Retrieve the Attachment Content-Disposition
-	 * @return String
-	 */
-	public function getContentDisposition() {
-		return $this->content_disposition;
-	}
-	
-	/**
-	 * Retrieve the Attachment Headers
-	 * @return String
-	 */
-	public function getHeaders() {
-		return $this->headers;
-	}
-	
-	/**
-	 * Retrieve the file extension
-	 * @return String
-	 */
-	public function getFileExtension() {
-		if (!$this->extension) {
-			$ext = substr(strrchr($this->filename, '.'), 1);
-			if ($ext == 'gz') {
-				// special case, tar.gz
-				// todo: other special cases?
-				$ext = preg_match("/\.tar\.gz$/i", $ext) ? 'tar.gz' : 'gz';
-			}
-			$this->extension = $ext;
-		}
-		return $this->extension;
-	}
-	
-	/**
-	 * Read the contents a few bytes at a time until completed
-	 * Once read to completion, it always returns false
-	 * @return String
-	 * @param $bytes Int[optional]
-	 */
-	public function read($bytes = 2082) {
-		return feof($this->stream) ? false : fread($this->stream, $bytes);
-	}
-	
-	/**
-	 * Retrieve the file content in one go
-	 * Once you retrieve the content you cannot use MimeMailParser_attachment::read()
-	 * @return String
-	 */
-	public function getContent() {
-		if ($this->content === null) {
-			fseek($this->stream, 0);
-			while(($buf = $this->read()) !== false) { 
-				$this->content .= $buf; 
-			}
-		}
-		return $this->content;
-	}
-	
-	/**
-	 * Allow the properties 
-	 * 	MimeMailParser_attachment::$name,
-	 * 	MimeMailParser_attachment::$extension 
-	 * to be retrieved as public properties
-	 * @param $name Object
-	 */
-	public function __get($name) {
-		if ($name == 'content') {
-			return $this->getContent();
-		} else if ($name == 'extension') {
-			return $this->getFileExtension();
-		}
-		return null;
-	}
-	
-}
-
-?>
diff --git a/scripts/mail/mail_handler.php b/scripts/mail/mail_handler.php
--- a/scripts/mail/mail_handler.php
+++ b/scripts/mail/mail_handler.php
@@ -14,7 +14,15 @@
 
 $root = dirname(dirname(dirname(__FILE__)));
 require_once $root.'/scripts/__init_script__.php';
-require_once $root.'/externals/mimemailparser/MimeMailParser.class.php';
+require_once $root.'/externals/mimemailparser/Contracts/CharsetManager.php';
+require_once $root.'/externals/mimemailparser/Contracts/Middleware.php';
+require_once $root.'/externals/mimemailparser/Parser.php';
+require_once $root.'/externals/mimemailparser/Charset.php';
+require_once $root.'/externals/mimemailparser/Attachment.php';
+require_once $root.'/externals/mimemailparser/Exception.php';
+require_once $root.'/externals/mimemailparser/Middleware.php';
+require_once $root.'/externals/mimemailparser/MiddlewareStack.php';
+require_once $root.'/externals/mimemailparser/MimePart.php';
 
 $args = new PhutilArgumentParser($argv);
 $args->parseStandardArguments();
@@ -32,25 +40,19 @@
     ),
   ));
 
-$parser = new MimeMailParser();
+if (!extension_loaded('mailparse')) {
+  throw new Exception(
+    pht(
+      'PhpMimeMailParser for handling incoming mail requires the PHP '.
+      'mailparse extension to be installed.'));
+}
+
+$parser = new \PhpMimeMailParser\Parser();
 $parser->setText(file_get_contents('php://stdin'));
 
 $content = array();
 foreach (array('text', 'html') as $part) {
   $part_body = $parser->getMessageBody($part);
-
-  if (strlen($part_body) && !phutil_is_utf8($part_body)) {
-    $part_headers = $parser->getMessageBodyHeaders($part);
-    if (!is_array($part_headers)) {
-      $part_headers = array();
-    }
-    $content_type = idx($part_headers, 'content-type');
-    if (preg_match('/charset="(.*?)"/', $content_type, $matches) ||
-        preg_match('/charset=(\S+)/', $content_type, $matches)) {
-      $part_body = phutil_utf8_convert($part_body, 'UTF-8', $matches[1]);
-    }
-  }
-
   $content[$part] = $part_body;
 }