Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+42 -36
View File
@@ -11,7 +11,7 @@
namespace IPS\Email\Incoming;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -57,6 +57,11 @@ class _Email
* @brief Attachments
*/
public $attachments = array();
/**
* @brief Existing attachments to store a map for
*/
public $mappedAttachments = array();
/**
* @brief Raw email headers
@@ -121,7 +126,7 @@ class _Email
/* Save */
if ( isset( $headers[ $headerName ] ) )
{
if ( is_array( $headers[ $headerName ] ) )
if ( \is_array( $headers[ $headerName ] ) )
{
$headers[ $headerName ][] = $headerValue;
}
@@ -137,7 +142,7 @@ class _Email
}
/* Work out the content type */
if ( isset( $headers['content-type'] ) and is_string( $headers['content-type'] ) )
if ( isset( $headers['content-type'] ) and \is_string( $headers['content-type'] ) )
{
$contentType = $this->_parseStructuredHeader( $headers['content-type'] );
}
@@ -147,7 +152,7 @@ class _Email
}
/* Parse body: Multipart */
if ( in_array( $contentType['value'], array( 'multipart/parallel', 'multipart/appledouble', 'multipart/report', 'multipart/signed', 'multipart/digest', 'multipart/alternative', 'multipart/related', 'multipart/mixed', 'application/vnd.wap.multipart.related' ) ) and isset( $contentType['extra']['boundary'] ) )
if ( \in_array( $contentType['value'], array( 'multipart/parallel', 'multipart/appledouble', 'multipart/report', 'multipart/signed', 'multipart/digest', 'multipart/alternative', 'multipart/related', 'multipart/mixed', 'application/vnd.wap.multipart.related' ) ) and isset( $contentType['extra']['boundary'] ) )
{
$parts = array();
@@ -192,7 +197,7 @@ class _Email
$body = preg_replace_callback( '/=([a-f0-9]{2})/i', function( $matches )
{
return chr( hexdec( $matches[1] ) );
return \chr( hexdec( $matches[1] ) );
}, $body );
}
elseif( $encoding == 'base64' )
@@ -232,7 +237,7 @@ class _Email
preg_match_all( '/=([a-f0-9]{2})/i', $text, $matches );
foreach( $matches[1] as $value )
{
$text = str_replace( '=' . $value, chr( hexdec( $value ) ), $text );
$text = str_replace( '=' . $value, \chr( hexdec( $value ) ), $text );
}
break;
}
@@ -279,7 +284,7 @@ class _Email
* Parse basic headers (to, from, subject, cc)
*
* @param array $headers Raw headers
* @retrun void
* @return void
*/
protected function _parseBasicHeaders( $headers )
{
@@ -313,7 +318,7 @@ class _Email
/* CC */
if( isset( $headers['cc'] ) )
{
if( is_array( $headers['cc'] ) )
if( \is_array( $headers['cc'] ) )
{
$this->cc = $headers['cc'];
}
@@ -405,17 +410,17 @@ class _Email
{
if ( isset( $data['headers']['content-id'] ) and $contentId = trim( $data['headers']['content-id'], '<>' ) and isset( $this->imagePlaceholders[ $contentId ] ) )
{
$imageTag = "<img src='<fileStore.core_Attachment>/{$file}' class='ipsImage ipsImage_thumbnailed'>";
$imageTag = "<img src='<fileStore.core_Attachment>/{$file}' class='ipsImage ipsImage_thumbnailed'> ";
$this->message = preg_replace( "/<a href=\"#image-placeholder={$this->imagePlaceholders[ $contentId ]}\" rel=\"[^\"]*\"><\/a>/", $imageTag, $this->message );
}
else
{
$this->message .= "<img src='<fileStore.core_Attachment>/{$file}' class='ipsImage ipsImage_thumbnailed'>";
$this->message .= "<img src='<fileStore.core_Attachment>/{$file}' class='ipsImage ipsImage_thumbnailed'> ";
}
}
else
{
$this->message .= "<a class='ipsAttachLink' href='<fileStore.core_Attachment>/{$file}'>{$file->originalFilename}</a>";
$this->message .= "<a class='ipsAttachLink' href='<fileStore.core_Attachment>/{$file}'>{$file->originalFilename}</a><br><br>";
}
/* Save to the $attachments array and exit */
@@ -489,7 +494,7 @@ class _Email
*
* @param string $body Email body
* @param array $contentType Content-Type data (as returned by _parseStructuredHeader)
* @retrun void
* @return void
*/
protected function _parseBodyAsHtml( $body, $contentType )
{
@@ -502,7 +507,7 @@ class _Email
/* Convert to UTF-8 if possible */
if ( isset( $contentType['extra']['charset'] ) and mb_strtolower( $contentType['extra']['charset'] ) != 'utf-8' )
{
if( in_array( mb_strtolower( $contentType['extra']['charset'] ), array_map( 'mb_strtolower', mb_list_encodings() ) ) )
if( \in_array( mb_strtolower( $contentType['extra']['charset'] ), array_map( 'mb_strtolower', mb_list_encodings() ) ) )
{
$body = mb_convert_encoding( $body, 'UTF-8', $contentType['extra']['charset'] );
}
@@ -512,13 +517,14 @@ class _Email
$body = preg_replace_callback( '/<img[^>]*src=[\'"]cid:(.+?)[\'"][^>]*>/', array( $this, '_replaceImageWithPlaceholder' ), $body );
/* Clean */
$body = \IPS\Text\Parser::parseStatic( $body, FALSE, NULL, \IPS\Member::load( $this->from, 'email' ), TRUE, TRUE, TRUE, function( $config ) {
$parser = new \IPS\Text\Parser( FALSE, NULL, \IPS\Member::load( $this->from, 'email' ), TRUE, TRUE, TRUE, function( $config ) {
$config->set( 'HTML.TargetBlank', TRUE );
$config->set( 'URI.Munge', urldecode( (string) \IPS\Http\Url::internal( 'app=core&module=system&controller=redirect&url=%s&key=%t&resource=%r', 'front' ) ) );
$config->set( 'URI.MungeResources', TRUE );
$config->set( 'URI.MungeSecretKey', \IPS\Settings::i()->site_secret_key );
});
} );
$body = $parser->parse( $body );
/* Store any mapped attachments that will need a mapping stored in the individual incoming email handlers */
$this->mappedAttachments = array_merge( $this->mappedAttachments, $parser->mappedAttachments );
/* Return */
return $body;
}
@@ -540,7 +546,7 @@ class _Email
* Extract quote from message body
*
* @param string $body Email body (the quote will be removed from it and returned)
* @retrun string
* @return string
*/
protected function _extractQuoteFromBody( &$body )
{
@@ -549,20 +555,20 @@ class _Email
$_body = array();
$_seen = FALSE;
$inQuote = FALSE;
$exploded = explode( "<br />", $body );
if ( count( $exploded ) === 1 )
{
$exploded = explode( '<br>', $body );
}
/* It is possible the email is formatted with <p></p> and not <br> which can confuse the quote selection */
/* Split into lines - It is possible the email is formatted with <p></p> and not <br> which can confuse the quote selection */
if ( mb_stristr( $body, '</p>' ) or mb_stristr( $body, '</div>' ) )
{
{
$body = preg_replace( '#<(p|div)[^>]*?>#', '', $body );
$body = preg_replace( '#</(p|div)>#', '<br>', $body );
}
$exploded = explode( "<br />", $body );
if ( \count( $exploded ) === 1 )
{
$exploded = explode( '<br>', $body );
}
/* Loop over each line */
foreach ( $exploded as $k => $line )
{
$line = trim( $line );
@@ -634,7 +640,7 @@ class _Email
{
/* Check for common "on x so and so wrote:" type lines preceeding this position */
preg_match_all( '/<div([^>]+?)?>((?!<div).)*:(<br(>| \/>))*\s*<\/div>/sU', $message, $possibleHeaders, PREG_OFFSET_CAPTURE );
if ( count( $possibleHeaders ) )
if ( \count( $possibleHeaders ) )
{
foreach ( array_reverse( $possibleHeaders[0] ) as $header )
{
@@ -733,7 +739,7 @@ class _Email
case 'eqls':
if ( mb_strpos( $analyse, ',' ) !== FALSE )
{
$match = (bool) in_array( $analyse, explode( ',', $analyse ) );
$match = (bool) \in_array( $analyse, explode( ',', $analyse ) );
}
else
{
@@ -763,7 +769,7 @@ class _Email
/* Get all IncomingEmail extension classes for the app */
$extensions = $app->extensions( 'core', 'IncomingEmail' );
if( count( $extensions ) )
if( \count( $extensions ) )
{
/* Loop over the extensions */
foreach( $extensions as $_instance )
@@ -792,7 +798,7 @@ class _Email
* Is this an auto-reply? Try to detect to prevent auto-reply loops.
*
* @return bool
* @link https://github.com/opennorth/multi_mail/wiki/Detecting-autoresponders
* @see https://github.com/opennorth/multi_mail/wiki/Detecting-autoresponders
*/
protected function isAutoreply()
{
@@ -820,17 +826,17 @@ class _Email
}
/* Now check for specific headers with specific values */
if( !empty( $this->headers['x-autogenerated'] ) AND in_array( mb_strtolower( $this->headers['x-autogenerated'] ), array( 'forward', 'group', 'letter', 'mirror', 'redirect', 'reply' ) ) )
if( !empty( $this->headers['x-autogenerated'] ) AND \in_array( mb_strtolower( $this->headers['x-autogenerated'] ), array( 'forward', 'group', 'letter', 'mirror', 'redirect', 'reply' ) ) )
{
return TRUE;
}
if( !empty( $this->headers['precedence'] ) AND in_array( mb_strtolower( $this->headers['precedence'] ), array( 'auto_reply', 'list', 'bulk' ) ) )
if( !empty( $this->headers['precedence'] ) AND \in_array( mb_strtolower( $this->headers['precedence'] ), array( 'auto_reply', 'list', 'bulk' ) ) )
{
return TRUE;
}
if( !empty( $this->headers['x-precedence'] ) AND in_array( mb_strtolower( $this->headers['x-precedence'] ), array( 'auto_reply', 'list', 'bulk' ) ) )
if( !empty( $this->headers['x-precedence'] ) AND \in_array( mb_strtolower( $this->headers['x-precedence'] ), array( 'auto_reply', 'list', 'bulk' ) ) )
{
return TRUE;
}
@@ -845,7 +851,7 @@ class _Email
return TRUE;
}
if ( !empty( $this->headers['delivered-to'] ) AND is_array( $this->headers['delivered-to'] ) )
if ( !empty( $this->headers['delivered-to'] ) AND \is_array( $this->headers['delivered-to'] ) )
{
foreach( $this->headers['delivered-to'] AS $deliveredTo )
{
@@ -856,7 +862,7 @@ class _Email
}
}
if( !empty( $this->headers['delivered-to'] ) AND is_string( $this->headers['delivered-to'] ) AND mb_strtolower( $this->headers['delivered-to'] ) == 'autoresponder' )
if( !empty( $this->headers['delivered-to'] ) AND \is_string( $this->headers['delivered-to'] ) AND mb_strtolower( $this->headers['delivered-to'] ) == 'autoresponder' )
{
return TRUE;
}