mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-03 12:12:43 +01:00
Update mimemailparser from May 2011 version to 8.0.4
Summary: Bump to version 8.0.4 from 2024-09-11 per https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases before this ancient code copy falls apart. `scripts/mail/mail_handler.php` (used for incoming (!) mail) is the only consumer. Closes T15940 Test Plan: Feed `mail_handler.php` with various test emails (formats: plain text, HTML, multipart; encodings: UTF-8, ASCII, ISO-8859-something) by manually replacing `php://stdin` with corresponding text files and adding some `phlog`s for output checking as I don't have mail server glue handy. Get only expected errors for broken emails. Reviewers: O1 Blessed Committers, 20after4 Reviewed By: O1 Blessed Committers, 20after4 Subscribers: 20after4, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T15940 Differential Revision: https://we.phorge.it/D25829
This commit is contained in:
parent
ddf26e89c8
commit
a76444a8e2
15 changed files with 2130 additions and 769 deletions
276
externals/mimemailparser/Attachment.php
vendored
Normal file
276
externals/mimemailparser/Attachment.php
vendored
Normal file
|
@ -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.');
|
||||
}
|
||||
}
|
||||
}
|
338
externals/mimemailparser/Charset.php
vendored
Normal file
338
externals/mimemailparser/Charset.php
vendored
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
24
externals/mimemailparser/Contracts/CharsetManager.php
vendored
Normal file
24
externals/mimemailparser/Contracts/CharsetManager.php
vendored
Normal file
|
@ -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);
|
||||
}
|
23
externals/mimemailparser/Contracts/Middleware.php
vendored
Normal file
23
externals/mimemailparser/Contracts/Middleware.php
vendored
Normal file
|
@ -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);
|
||||
}
|
8
externals/mimemailparser/Exception.php
vendored
Normal file
8
externals/mimemailparser/Exception.php
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace PhpMimeMailParser;
|
||||
|
||||
class Exception extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
138
externals/mimemailparser/LICENSE
vendored
138
externals/mimemailparser/LICENSE
vendored
|
@ -1,125 +1,21 @@
|
|||
The "Artistic License"
|
||||
The MIT License (MIT)
|
||||
|
||||
Preamble
|
||||
Copyright (c) 2016 Vincent Dauce
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
Definitions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
"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 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.
|
||||
|
|
29
externals/mimemailparser/Middleware.php
vendored
Normal file
29
externals/mimemailparser/Middleware.php
vendored
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
89
externals/mimemailparser/MiddlewareStack.php
vendored
Normal file
89
externals/mimemailparser/MiddlewareStack.php
vendored
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
494
externals/mimemailparser/MimeMailParser.class.php
vendored
494
externals/mimemailparser/MimeMailParser.class.php
vendored
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
119
externals/mimemailparser/MimePart.php
vendored
Normal file
119
externals/mimemailparser/MimePart.php
vendored
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
923
externals/mimemailparser/Parser.php
vendored
Normal file
923
externals/mimemailparser/Parser.php
vendored
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
3
externals/mimemailparser/README
vendored
3
externals/mimemailparser/README
vendored
|
@ -1,3 +0,0 @@
|
|||
From:
|
||||
|
||||
http://code.google.com/p/php-mime-mail-parser/
|
267
externals/mimemailparser/README.md
vendored
Normal file
267
externals/mimemailparser/README.md
vendored
Normal file
|
@ -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&utm_medium=referral&utm_content=php-mime-mail-parser/php-mime-mail-parser&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)
|
136
externals/mimemailparser/attachment.class.php
vendored
136
externals/mimemailparser/attachment.class.php
vendored
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -14,7 +14,15 @@ if ($argc > 1) {
|
|||
|
||||
$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 @@ $args->parse(
|
|||
),
|
||||
));
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue