Page MenuHomePhorge

D25829.1730013360.diff
No OneTemporary

D25829.1730013360.diff

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;
}

File Metadata

Mime Type
text/plain
Expires
Sun, Oct 27, 07:16 (21 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
950577
Default Alt Text
D25829.1730013360.diff (94 KB)

Event Timeline