mirror of
https://github.com/itflow-org/itflow
synced 2026-02-28 19:04:52 +00:00
Migrated away from PHP Mail Parser to the new WebKlex PHP IMAP Mail Parser this will open the way to support OAUTH2 for Mail servers such as Microsoft 365 and Google Workspaces
This commit is contained in:
@@ -1,276 +0,0 @@
|
||||
<?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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
<?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' => 'macce',
|
||||
'x-mac-turkish' => 'macturkish',
|
||||
'x-mac-greek' => 'macgreek',
|
||||
'x-mac-icelandic' => 'macicelandic',
|
||||
'x-mac-croatian' => 'maccroatian',
|
||||
'x-mac-romanian' => 'macromanian',
|
||||
'x-mac-cyrillic' => 'maccyrillic',
|
||||
'x-mac-ukrainian' => 'macukrainian',
|
||||
'x-mac-hebrew' => 'machebrew',
|
||||
'x-mac-arabic' => 'macarabic',
|
||||
'x-mac-farsi' => 'macfarsi',
|
||||
'x-mac-devanagari' => 'macdevanagari',
|
||||
'x-mac-gujarati' => 'macgujarati',
|
||||
'x-mac-gurmukhi' => 'macgurmukhi',
|
||||
'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)
|
||||
{
|
||||
$charset = $this->getCharsetAlias($charset);
|
||||
|
||||
if ($charset == 'utf-8' || $charset == 'us-ascii') {
|
||||
return $encodedString;
|
||||
}
|
||||
|
||||
if (function_exists('mb_convert_encoding')) {
|
||||
if ($charset == 'iso-2022-jp') {
|
||||
return mb_convert_encoding($encodedString, 'utf-8', 'iso-2022-jp-ms');
|
||||
}
|
||||
|
||||
if (array_search($charset, $this->getSupportedEncodings())) {
|
||||
return mb_convert_encoding($encodedString, 'utf-8', $charset);
|
||||
}
|
||||
}
|
||||
|
||||
return iconv($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];
|
||||
}
|
||||
|
||||
return 'us-ascii';
|
||||
}
|
||||
|
||||
private function getSupportedEncodings()
|
||||
{
|
||||
return
|
||||
array_map(
|
||||
'strtolower',
|
||||
array_unique(
|
||||
array_merge(
|
||||
$enc = mb_list_encodings(),
|
||||
call_user_func_array(
|
||||
'array_merge',
|
||||
array_map(
|
||||
"mb_encoding_aliases",
|
||||
$enc
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PhpMimeMailParser;
|
||||
|
||||
class Exception extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -1,923 +0,0 @@
|
||||
<?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) || $meta['eof']) {
|
||||
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|bool
|
||||
*/
|
||||
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)') {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user