Version 3.4.5

This commit is contained in:
Neo committed 2025-12-19 00:38:12 -08:00
1 parent fb6b5baabc
commit e4478369d8
1360 files changed
+228921 -179445

No files matched your search

@@ -2,19 +2,19 @@
/**
* <pre>
* Invision Power Services
* IP.Board v3.3.4
* IP.Board v3.4.5
* Parse Incoming Emails
* Last Updated: $Date: 2012-06-01 11:18:06 -0400 (Fri, 01 Jun 2012) $
* Last Updated: $Date: 2013-05-08 17:43:20 -0400 (Wed, 08 May 2013) $
* </pre>
*
* @author $Author: mark $
* @author $Author: bfarber $
* @copyright © 2010 Invision Power Services, Inc.
* @license http://www.invisionpower.com/company/standards.php#license
* @package IP.Board
* @subpackage Kernel
* @link http://www.invisionpower.com
* @since 25th June 2010
* @version $Revision: 10854 $
* @version $Revision: 12240 $
*/
class incomingEmail
@@ -35,7 +35,7 @@ class incomingEmail
/**
* Debug Mode
*/
protected $debug_mode = FALSE;
protected $debug_mode = false;
/**
* Preferred order for alterative parts
@@ -64,7 +64,7 @@ class incomingEmail
$this->DB->execute();
while ( $r = $this->DB->fetch() )
{
$this->types[ $r['atype_mimetype'] ][] = $r;
$this->types[ $r['atype_extension'] ] = $r;
}
/* Set our preferred format */
@@ -97,8 +97,8 @@ class incomingEmail
//-----------------------------------------
// It raises strict warnings
@error_reporting( E_NONE );
@ini_set( 'display_errors', 'off' );
//@error_reporting( E_NONE );
//@ini_set( 'display_errors', 'off' );
require_once ( IPS_KERNEL_PATH . 'PEAR/Mail/mimeDecode.php' );/*noLibHook*/
$decoder = new Mail_mimeDecode( $email );
@@ -107,7 +107,7 @@ class incomingEmail
'decode_bodies' => TRUE,
'decode_headers' => TRUE,
) );
//--------------------------------------
// Parse Headers
//--------------------------------------
@@ -188,11 +188,11 @@ class incomingEmail
{
if ( preg_match( "/.+? <(.+?)>/", $_cc, $matches ) )
{
$cc[] = htmlentities( $matches[1] );
$cc[] = $matches[1];
}
else
{
$cc[] = htmlentities( trim( $_cc, '<> ' ) );
$cc[] = trim( $_cc, '<> ' );
}
}
$obj->cc = str_replace( array( '&gt;', '&lt;' ), '', implode( ',', $cc ) );
@@ -201,14 +201,14 @@ class incomingEmail
// Ignore?
//-----------------------------------------
if ( !$obj->debug_mode )
if ( !$obj->debug_mode and !$doNotRoute )
{
$escapedFrom = $obj->DB->addSlashes( $obj->from );
$log = $obj->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_incoming_email_log', 'where' => "log_email='{$escapedFrom}'" ) );
if ( $log['log_id'] )
{
$oneMinuteAgo = time() - 60;
if ( $log['log_time'] > $oneMinuteAgo )
if ( $log['log_time'] > $oneMinuteAgo && $this->settings['sixty_second_rule'] )
{
$ignore = TRUE;
}
@@ -229,29 +229,34 @@ class incomingEmail
//-----------------------------------------
// Now destruct the message
//-----------------------------------------
$obj->message = '';
$obj->_parsePart( $mail );
//-----------------------------------------
// Purify It
//-----------------------------------------
// &nbsp; sometimes breaks
$obj->message = str_replace( '&nbsp;', ' ', $obj->message );
/* Load */
require_once( IPS_KERNEL_PATH . 'HTMLPurifier/HTMLPurifier.auto.php' );
$config = HTMLPurifier_Config::createDefault();
/* Set Configuration */
$config->set( 'AutoFormat.Linkify', TRUE );
$config->set( 'Core.Encoding', IPS_DOC_CHAR_SET );
$config->set( 'HTML.TargetBlank', TRUE );
$config->set( 'URI.Munge', ipsRegistry::getClass('output')->buildUrl('app=nexus&module=support&section=redirect&url=%s&key=%t&resource=%r', 'public' ) );
$config->set( 'URI.Munge', ipsRegistry::getClass('output')->buildUrl('app=nexus&module=support&section=redirect&url=%s&key=%t&resource=%r', 'publicNoSession' ) );
$config->set( 'URI.MungeResources', TRUE );
$config->set( 'URI.MungeSecretKey', md5( ipsRegistry::$settings['sql_pass'] . ipsRegistry::$settings['board_url'] . ipsRegistry::$settings['sql_database'] ) );
/* Retain collapse data attribute */
$def = $config->getHTMLDefinition(true);
$def->addAttribute( 'blockquote', 'data-author', 'CDATA' );
$def->addAttribute( 'blockquote', 'data-collapsed', 'Number' );
/* Purify */
$purifier = new HTMLPurifier( $config );
$obj->message = $purifier->purify( $obj->message );
@@ -381,6 +386,13 @@ class incomingEmail
/* Some clients use uppercase, but we check by lowercase */
$part->ctype_primary = strtolower( $part->ctype_primary );
$part->ctype_secondary = strtolower( $part->ctype_secondary );
/* Make sure .txt files get attached as .txt files */
if ( $part->ctype_primary == 'text' and $part->disposition == 'attachment' )
{
$part->ctype_primary = 'unknown';
$part->ctype_secondary = 'unknown';
}
/* What is this? */
switch ( $part->ctype_primary )
@@ -448,7 +460,7 @@ class incomingEmail
/* Text */
case 'text':
$body = $part->body;
$this->original_message .= $body;
@@ -474,9 +486,10 @@ class incomingEmail
}
// Convert
//$body = IPSText::convertCharsets( $body, $part->ctype_parameters['charset'], IPS_DOC_CHAR_SET );
$body = IPSText::convertCharsets( $body, $part->ctype_parameters['charset'], IPS_DOC_CHAR_SET );
$part->ctype_parameters['charset'] = IPS_DOC_CHAR_SET;
}
//-----------------------------------------
// Parse > style quotes
//-----------------------------------------
@@ -515,7 +528,7 @@ class incomingEmail
if ( $quotableLine )
{
$this->message = substr( $this->message, 0, strrpos( $this->message, $quotableLine ) );
$this->message .= "[quote collapse='{$quotableLine}']";
$this->message .= "[quote collapse='1' name='" . str_replace( array( "\r", "\n" ), ' ', $quotableLine ) . "']";
}
else
{
@@ -546,48 +559,48 @@ class incomingEmail
//-----------------------------------------
// Parse HTML block quotes
//-----------------------------------------
/* Loop */
preg_match_all( '/<blockquote.+?>/s', $this->message, $matches );
while ( !empty( $matches ) and !empty( $matches[0] ) )
{
$m = $matches[0][0];
// Get all the content before the quote
$stripped = substr( $this->message, 0, strpos( $this->message, $m ) );
// Knock off any breaks or closed divs after it
$stripped = preg_replace( '/<br.+?>/s', '', $stripped );
$stripped = str_replace( '</div>', '', $stripped );
$stripped = trim( str_replace( '</div>', '', $stripped ) );
$stripped = preg_replace( '/((<br(\s*\/)?>|&nbsp)*$)/s', '', $stripped );
// Now scan back until we hit some other html
$pos = strrpos( $stripped, '>' ) + 1;
$stripped = trim( substr( $stripped, $pos ) );
// If it's an acceptable header, use it
if ( substr( $stripped, -1, 1 ) == ':' )
{
// Chop it out
$this->message = substr_replace( $this->message, '', strpos( $this->message, $stripped ), strlen( $stripped ) );
// We have to encode the header so future tags don't replace it
$stripped = '{{QUOTE-ENCODE:' . base64_encode( $stripped ) . '}}';
// Replace the blockquote
$this->message = substr_replace( $this->message, "[quote collapse='{$stripped}']", strpos( $this->message, $m ), strlen( $m ) );
$this->message = substr_replace( $this->message, "[quote collapse='1' name='{$stripped}']", strpos( $this->message, $m ), strlen( $m ) );
}
else
{
// Replace the blockquote
// Replace the blockquote
$this->message = substr_replace( $this->message, "[quote collapse='1']", strpos( $this->message, $m ), strlen( $m ) );
}
// Do the scan again
preg_match_all( '/<blockquote.+?>/s', $this->message, $matches );
}
/* Decode the quote headers we made */
$this->message = preg_replace_callback( '/{{QUOTE-ENCODE:(.+?)}}/', create_function( '$m', 'return base64_decode( $m[1] );' ), $this->message );
$this->message = preg_replace_callback( '/{{QUOTE-ENCODE:(.+?)}}/', create_function( '$m', 'return str_replace( array( "\r", "\n" ), " ", base64_decode( $m[1] ) );' ), $this->message );
/* Add in closing [/quote] tags */
$this->message = preg_replace( '/<\/blockquote>/', '[/quote]', $this->message );
@@ -608,49 +621,139 @@ class incomingEmail
if ( preg_match( '/-{10,}\s+(.+?)\s-{10,}/', $this->message, $matches ) )
{
$this->message = str_replace( $matches[0], "[quote collapse='{$matches[1]}']", $this->message );
$this->message = str_replace( $matches[0], "[quote collapse='1']", $this->message );
$this->message .= '[/quote]';
}
//-----------------------------------------
// Look for common quote encapsulation methods
//-----------------------------------------
if( strpos( $this->message, 'OLK_SRC_BODY_SECTION' ) !== false )
{
if ( preg_match( '/<span id=["\']OLK_SRC_BODY_SECTION[\'"]>/ims', $this->message, $matches ) )
{
$this->message = str_replace( $matches[0], "[quote collapse='1']{$matches[0]}", $this->message );
if( strpos( $this->message, '</body>' ) !== false )
{
$this->message = str_replace( '</body>', "[/quote]</body>", $this->message );
}
else
{
$this->message .= '[/quote]';
}
}
}
if( strpos( $this->message, 'MsoNormal' ) !== false ) // This must be from Outlook
{
if ( preg_match( '/<div style=["\'].+?border-top:.+?[\'"]>.*?<p class=["\']MsoNormal["\']>(.+)<\/div>/ims', $this->message, $matches ) )
{
$this->message = str_replace( $matches[0], "[quote collapse='1']{$matches[0]}[/quote]", $this->message );
}
}
// This code does work, however:
// --Differing email clients have differing formats, so it's not reliable
// --There can be cases where you have [quote collapse='1' name='On date, at time, so and so wrote:'] already, which results in [quote name='[quote ...]'], breaking the parsing
/*if( preg_match( '/\bon\s+?.+?,\s*?at.+?,\s*?' . $this->settings['board_name'] . '.+?' . $this->settings['email_out'] . '.+?wrote:/ims', $this->message, $matches ) )
{
//-----------------------------------------
// If there's an opening <div> right after this, go there instead
// but only if we're not going to strip quotes (otherwise we get a stray "blah blah said:" line)
//-----------------------------------------
if( strpos( $this->message, "<div>", strpos( $this->message, $matches[0] ) ) !== false )
{
$matches[0] = substr( $this->message, strpos( $this->message, $matches[0] ), strpos( $this->message, "<div>", strpos( $this->message, $matches[0] ) ) - strpos( $this->message, $matches[0] ) + 5 );
$this->message = str_replace( $matches[0], "{$matches[0]}[quote collapse='1']", $this->message );
$this->message = preg_replace( "/<\/div>\s*?<\/body>/", "[/quote]</div></body>", $this->message );
}
else
{
$this->message = str_replace( $matches[0], "{$matches[0]}[quote collapse='1']", $this->message );
if( strpos( $this->message, '</body>' ) !== false )
{
$this->message = str_replace( '</body>', "[/quote]</body>", $this->message );
}
else
{
$this->message .= '[/quote]';
}
}
}*/
/* Are we missing any closing tags? */
$_opening = substr_count( $this->message, '[quote ' );
$_closing = substr_count( $this->message, '[/quote]' );
if( $_opening > $_closing )
{
for( $i = $_opening; $i < $_closing; $i++ )
{
$this->message .= '[/quote]';
}
}
//-----------------------------------------
// Parse the quote tag
//-----------------------------------------
//$this->message = preg_replace( "/\[quote collapse='(.*?)'\]/ims", "<blockquote class='ipsBlockquote' data-collapsed='\\1'>", $this->message );
//$this->message = str_replace( "[/quote]", "</blockquote>", $this->message );
/* Parse BBCode */
IPSText::getTextClass('bbcode')->parse_html = 0;
IPSText::getTextClass('bbcode')->parse_nl2br = 1;
IPSText::getTextClass('bbcode')->parse_smilies = 1;
IPSText::getTextClass('bbcode')->parse_bbcode = 1;
IPSText::getTextClass('bbcode')->parsing_section = 'support';
$this->message = IPSText::getTextClass('bbcode')->parseSingleBbcodes( $this->message, 'display', 'quote' );
return;
/* Attachments */
default:
if ( $part->ctype_primary == '_text' )
{
$part->ctype_primary = 'text';
}
$mime = "{$part->ctype_primary}/{$part->ctype_secondary}";
$content = "{ATTACHMENT WITH INCOMPATIBLE MIME TYPE: {$mime}}";
$name = $part->ctype_parameters['name'] ? $part->ctype_parameters['name'] : $part->d_parameters['filename'];
$name_parts = explode( '.', $name );
$ext = array_pop( $name_parts );
if ( isset( $this->types[ $mime ] ) )
$content = "{ATTACHMENT WITH EXTENSION: {$ext}}";
if ( isset( $this->types[ $ext ] ) and $this->types[ $ext ]['atype_post'] )
{
foreach ( $this->types[ $mime ] as $data )
/* Create the file */
$masked_name = md5( uniqid( 'email' ) ) . "-{$name}";
while ( is_file( $this->settings['upload_dir'] . "/{$masked_name}" ) )
{
$name_parts = explode( '.', $part->ctype_parameters['name'] );
$ext = array_pop( $name_parts );
if ( strtolower( $data['atype_extension'] ) == strtolower( $ext ) and $data['atype_post'] )
{
/* Create the file */
$masked_name = md5( uniqid( 'email' ) ) . "-{$part->ctype_parameters['name']}";
while ( is_file( $this->settings['upload_dir'] . "/{$masked_name}" ) )
{
$masked_name = md5( uniqid( 'email' ) . microtime() ) . "-{$part->ctype_parameters['name']}";
}
file_put_contents( $this->settings['upload_dir'] . "/{$masked_name}", $part->body );
/* Store attachment data */
$this->akey++;
$this->attachments[ $this->akey ] = array(
'attach_ext' => $ext,
'attach_file' => $part->ctype_parameters['name'],
'attach_location' => $masked_name,
'attach_is_image' => ( $part->ctype_primary == 'image' ) ? 1 : 0,
'attach_date' => time(),
'attach_filesize' => $part->d_parameters['size'],
);
$content = "{ATTACHMENT:{$this->akey}}";
break;
}
$masked_name = md5( uniqid( 'email' ) . microtime() ) . "-{$name}";
}
file_put_contents( $this->settings['upload_dir'] . "/{$masked_name}", $part->body );
/* Store attachment data */
$this->akey++;
$this->attachments[ $this->akey ] = array(
'attach_ext' => $ext,
'attach_file' => $name,
'attach_location' => $masked_name,
'attach_is_image' => ( $part->ctype_primary == 'image' ) ? 1 : 0,
'attach_date' => time(),
'attach_filesize' => $part->d_parameters['size'],
);
$content = "{ATTACHMENT:{$this->akey}}";
break;
}
$addedContent = FALSE;