Version 3.2.3

This commit is contained in:
Neo committed 2025-12-19 00:34:03 -08:00
1 parent ae5c01cc78
commit 78706903a2
2641 files changed
+228363 -201264

No files matched your search

+237 -95
View File
@@ -1,25 +1,26 @@
<?php
/**
* <pre>
* Invision Power Services
* IP.Board v3.1.4
* IP.Board v3.2.3
* Parse Incoming Emails
* Last Updated: $Date: 2010-11-15 10:45:42 -0500 (Mon, 15 Nov 2010) $
* Last Updated: $Date: 2011-10-03 11:31:57 -0400 (Mon, 03 Oct 2011) $
* </pre>
*
* @author $Author: mark $
* @copyright (c) 2010 Invision Power Services, Inc.
* @license http://www.invisionpower.com/community/board/license.html
* @license This is NULLED!
* @package IP.Board
* @subpackage Kernel
* @link http://www.invisionpower.com
* @link http://hatynka.in
* @since 25th June 2010
* @version $Revision: 486 $
* @version $Revision: 9573 $
*/
class classIncomingEmail
{
public $ignore = FALSE;
/**
* Recipient Address
*
@@ -69,8 +70,10 @@ class classIncomingEmail
*
* @param string Raw message with headers
*/
public function __construct( $email )
public function __construct( $email, $override=array() )
{
$this->raw = $email;
//--------------------------------------
// Get some stuff
//--------------------------------------
@@ -121,58 +124,128 @@ class classIncomingEmail
@error_reporting( E_NONE );
@ini_set( 'display_errors', 'off' );
require_once ( IPS_KERNEL_PATH . 'PEAR/Mail/mimeDecode.php' );
require_once ( IPS_KERNEL_PATH . 'PEAR/Mail/mimeDecode.php' );/*noLibHook*/
$decoder = new Mail_mimeDecode( $email );
$mail = $decoder->decode( array(
'include_bodies' => TRUE,
'decode_bodies' => TRUE,
'decode_headers' => TRUE,
) );
//--------------------------------------
// Parse Headers
//--------------------------------------
/* To */
if ( $mail->headers['delivered-to'] )
if ( isset( $override['to'] ) )
{
$mail->headers['to'] = $mail->headers['delivered-to'];
}
$to = array();
if ( strpos( $mail->headers['to'], ',' ) === FALSE )
{
$mail->headers['to'] = array( $mail->headers['to'] );
$this->to = $override['to'];
}
else
{
$mail->headers['to'] = explode( ',', $mail->headers['to'] );
}
foreach ( $mail->headers['to'] as $_to )
{
if ( preg_match( "/.+? <(.+?)>/", $_to, $matches ) )
if ( $mail->headers['delivered-to'] )
{
$to[] = $matches[1];
$mail->headers['to'] = $mail->headers['delivered-to'];
}
$to = array();
if ( strpos( $mail->headers['to'], ',' ) === FALSE )
{
$mail->headers['to'] = array( $mail->headers['to'] );
}
else
{
$to[] = trim( $_to, '<>' );
$mail->headers['to'] = explode( ',', $mail->headers['to'] );
}
foreach ( $mail->headers['to'] as $_to )
{
if ( preg_match( "/.+? <(.+?)>/", $_to, $matches ) )
{
$to[] = htmlentities( $matches[1] );
}
else
{
$to[] = htmlentities( trim( $_to, '<>' ) );
}
}
$this->to = implode( ',', $to );
}
$this->to = implode( ',', $to );
/* From */
if ( preg_match( "/.+? <(.+?)>/", $mail->headers['from'], $matches ) )
if ( isset( $override['from'] ) )
{
$this->from = $matches[1];
$this->from = $override['from'];
}
else
{
$this->from = trim( $mail->headers['from'], '<>' );
if ( preg_match( "/.+? <(.+?)>/", $mail->headers['from'], $matches ) )
{
$this->from = htmlentities( $matches[1] );
}
else
{
$this->from = htmlentities( trim( $mail->headers['from'], '<>' ) );
}
}
/* Subject */
$this->subject = $mail->headers['subject'] ? $mail->headers['subject'] : '(No Subject)';
if ( isset( $override['subject'] ) )
{
$this->subject = $override['subject'];
}
else
{
$this->subject = ( (bool) trim( $mail->headers['subject'] ) ) ? $mail->headers['subject'] : '(No Subject)';
$this->subject = htmlentities( $this->subject );
}
/* CC */
if ( strpos( $mail->headers['cc'], ',' ) === FALSE )
{
$mail->headers['cc'] = array( $mail->headers['cc'] );
}
else
{
$mail->headers['cc'] = explode( ',', $mail->headers['cc'] );
}
foreach ( $mail->headers['cc'] as $_cc )
{
if ( preg_match( "/.+? <(.+?)>/", $_cc, $matches ) )
{
$cc[] = htmlentities( $matches[1] );
}
else
{
$cc[] = htmlentities( trim( $_cc, '<>' ) );
}
}
$this->cc = str_replace( array( '&gt;', '&lt;' ), '', implode( ',', $cc ) );
//-----------------------------------------
// Ignore?
//-----------------------------------------
$escapedFrom = $this->DB->addSlashes( $this->from );
$log = $this->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 )
{
$this->ignore = TRUE;
}
$this->DB->update( 'core_incoming_email_log', array( 'log_time' => time() ), "log_id={$log['log_id']}" );
if ( $this->ignore )
{
return;
}
}
else
{
$this->DB->insert( 'core_incoming_email_log', array( 'log_email' => $this->from, 'log_time' => time() ) );
}
//--------------------------------------
// Parse Body
//--------------------------------------
@@ -180,33 +253,23 @@ class classIncomingEmail
$this->message = '';
$this->attachments = array();
$akey = 0;
/* Normal message */
if ( $mail->ctype_primary == 'text' )
{
if ( $mail->ctype_secondary == 'html' )
{
$this->message = str_replace( array( "\n", "\r", "\r\n" ), '<br />', htmlentities( strip_tags( $mail->body ) ) );
}
else
{
$this->message = str_replace( array( "\n", "\r", "\r\n" ), '<br />', htmlentities( $mail->body ) );;
}
$this->message = str_replace( array( "\n", "\r", "\r\n" ), '<br />', $this->cleanMessage( $mail->body ) );
}
/* Normal message, sent in both HTML and normal */
elseif ( $mail->ctype_primary == 'multipart' AND $mail->ctype_secondary == 'alternative' )
elseif ( strtolower( $mail->ctype_primary ) == 'multipart' AND strtolower( $mail->ctype_secondary ) == 'alternative' )
{
$this->message = '';
$this->message = '';
foreach ( $mail->parts as $part )
{
if ( $part->ctype_secondary == 'html' )
if ( in_array( $part->ctype_secondary, array( 'plain', 'html' ) ) )
{
$this->message = str_replace( array( "\n", "\r", "\r\n" ), '<br />', htmlentities( strip_tags( $part->body ) ) );;
}
else
{
$this->message = str_replace( array( "\n", "\r", "\r\n" ), '<br />', htmlentities( $part->body ) );;
if ( $part->ctype_secondary == 'plain' )
$this->message = str_replace( array( "\n", "\r", "\r\n" ), '<br />', $this->cleanMessage( $part->body ) );
if ( strtolower( $part->ctype_secondary ) == 'plain' ) // We prefer plain text
{
break;
}
@@ -218,13 +281,13 @@ class classIncomingEmail
{
foreach ( $mail->parts as $part )
{
if ( $part->ctype_primary == 'multipart' AND $part->ctype_secondary == 'alternative' )
if ( strtolower( $part->ctype_primary ) == 'multipart' AND strtolower( $part->ctype_secondary ) == 'alternative' )
{
$add = '';
foreach ( $part->parts as $subpart )
{
$add = str_replace( array( "\n", "\r", "\r\n" ), '<br />', $subpart->body );
if ( $subpart->ctype_secondary == 'plain' )
if ( strtolower( $subpart->ctype_secondary ) == 'plain' )
{
break;
}
@@ -233,49 +296,45 @@ class classIncomingEmail
}
elseif ( $part->ctype_primary == 'text' )
{
if ( $part->ctype_secondary == 'html' )
{
$this->message .= str_replace( array( "\n", "\r", "\r\n" ), '<br />', htmlentities( strip_tags( $part->body ) ) );;
}
else
{
$this->message .= str_replace( array( "\n", "\r", "\r\n" ), '<br />', htmlentities( $part->body ) );;
}
$this->message .= str_replace( array( "\n", "\r", "\r\n" ), '<br />', $this->cleanMessage( $part->body ) );
}
else
{
$mime = "{$part->ctype_primary}/{$part->ctype_secondary}";
foreach ( $this->types[ $mime ] as $data )
if ( isset( $this->types[ $mime ] ) )
{
$name_parts = explode( '.', $part->ctype_parameters['name'] );
$ext = array_pop( $name_parts );
if ( $data['atype_extension'] == $ext and $data['atype_post'] )
foreach ( $this->types[ $mime ] as $data )
{
/* Create the file */
$masked_name = md5( uniqid( 'email' ) ) . "-{$part->ctype_parameters['name']}";
while ( file_exists( $this->upload_dir . "/{$masked_name}" ) )
$name_parts = explode( '.', $part->ctype_parameters['name'] );
$ext = array_pop( $name_parts );
if ( $data['atype_extension'] == $ext and $data['atype_post'] )
{
$masked_name = md5( uniqid( 'email' ) . microtime() ) . "-{$part->ctype_parameters['name']}";
/* Create the file */
$masked_name = md5( uniqid( 'email' ) ) . "-{$part->ctype_parameters['name']}";
while ( is_file( $this->upload_dir . "/{$masked_name}" ) )
{
$masked_name = md5( uniqid( 'email' ) . microtime() ) . "-{$part->ctype_parameters['name']}";
}
file_put_contents( $this->upload_dir . "/{$masked_name}", $part->body );
/* Store attachment data */
$akey++;
$this->attachments[ $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'],
);
$this->message .= "<!--ATTACHMENT:{$akey}-->";
break;
}
file_put_contents( $this->upload_dir . "/{$masked_name}", $part->body );
/* Store attachment data */
$akey++;
$this->attachments[ $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'],
);
$this->message .= "<!--ATTACHMENT:{$akey}-->";
break;
}
}
}
}
}
}
}
/**
@@ -307,7 +366,7 @@ class classIncomingEmail
$analyse = $this->message;
break;
}
$match = false;
switch ( $row['rule_criteria_type'] )
{
@@ -330,7 +389,7 @@ class classIncomingEmail
$match = (bool) ( preg_match( "/{$row['rule_criteria_value']}/", $analyse ) == 1 );
break;
}
if ( $match )
{
$unrouted = FALSE;
@@ -346,24 +405,24 @@ class classIncomingEmail
$appdir = IPS_ROOT_PATH . 'applications/' . $row['rule_app'];
}
if ( file_exists( $appdir . '/extensions/incomingEmails.php' ) )
if ( is_file( $appdir . '/extensions/incomingEmails.php' ) )
{
$class = 'incomingEmails_' . $row['rule_app'];
require_once $appdir . '/extensions/incomingEmails.php';
require_once( $appdir . '/extensions/incomingEmails.php' );/*noLibHook*/
$class = new $class;
$class->process( $this->DB, $this->to, $this->from, $this->subject, $this->message, $this->attachments );
$class->process( $this->DB, $this->to, $this->from, $this->subject, $this->message, $this->attachments, $this->raw, $this->cc );
}
}
break;
}
}
if ( $unrouted )
{
$apps = array();
foreach ( glob( IPS_ROOT_PATH . 'applications/*' ) as $f )
{
if ( file_exists( $f . '/extensions/incomingEmails.php' ) )
if ( is_file( $f . '/extensions/incomingEmails.php' ) )
{
$bits = explode( '/', $f );
$_appdir = array_pop( $bits );
@@ -372,7 +431,7 @@ class classIncomingEmail
}
foreach ( glob( IPS_ROOT_PATH . 'applications_addon/ips/*' ) as $f )
{
if ( file_exists( $f . '/extensions/incomingEmails.php' ) )
if ( is_file( $f . '/extensions/incomingEmails.php' ) )
{
$bits = explode( '/', $f );
$_appdir = array_pop( $bits );
@@ -381,24 +440,47 @@ class classIncomingEmail
}
foreach ( glob( IPS_ROOT_PATH . 'applications_addon/other/*' ) as $f )
{
if ( file_exists( $f . '/extensions/incomingEmails.php' ) )
if ( is_file( $f . '/extensions/incomingEmails.php' ) )
{
$bits = explode( '/', $f );
$_appdir = array_pop( $bits );
$apps[ $_appdir ] = $f;
}
}
$routed = FALSE;
foreach ( $apps as $_appdir => $appdir )
{
require_once $appdir . '/extensions/incomingEmails.php';
require_once( $appdir . '/extensions/incomingEmails.php' );/*noLibHook*/
$class = 'incomingEmails_' . $_appdir;
$i = new $class;
if ( $i->handleUnrouted( $this->DB, $this->to, $this->from, $this->subject, $this->message, $this->attachments ) )
if ( $routed = $i->handleUnrouted( $this->DB, $this->to, $this->from, $this->subject, $this->message, $this->attachments, $this->raw, $this->cc ) )
{
break;
}
}
if ( !$routed )
{
$unroutedMessage = @file_get_contents( DOC_IPS_ROOT_PATH . 'interface/email/unrouted.txt' );
if ( $unroutedMessage )
{
$unroutedMessage = nl2br( $unroutedMessage ) . '<br /><br />';
foreach ( explode( "<br />", $this->message ) as $line )
{
$unroutedMessage .= "> {$line}<br />";
}
$this->DB->insert( 'mail_queue', array(
'mail_date' => time(),
'mail_to' => $this->from,
'mail_from' => $this->to,
'mail_subject' => "Re: {$this->subject}",
'mail_content' => $unroutedMessage,
'mail_html_on' => TRUE
) );
}
}
}
}
@@ -409,14 +491,14 @@ class classIncomingEmail
*
* @see route()
*/
private function initDB()
protected function initDB()
{
/* Init */
$INFO = array();
@require( DOC_IPS_ROOT_PATH . 'conf_global.php' );
@require( DOC_IPS_ROOT_PATH . 'conf_global.php' );/*noLibHook*/
$this->DB_driver = strtolower( $INFO['sql_driver'] );
require_once ( IPS_KERNEL_PATH . 'classDb' . ucwords( $this->DB_driver ) . ".php" );
require_once( IPS_KERNEL_PATH . 'classDb' . ucwords( $this->DB_driver ) . '.php' );/*noLibHook*/
$classname = "db_driver_" . $this->DB_driver;
$this->DB = new $classname;
@@ -429,5 +511,65 @@ class classIncomingEmail
$this->DB->connect();
}
/**
* Clean message
* Parses message and removes any nasty stuff
*
* @param string Message
* @param string Cleaned Message
*/
protected function cleanMessage( $txt )
{
//-----------------------------------------
// First try gracfully removing stuff
//-----------------------------------------
$txt = preg_replace( "/<html(.*)>/U", '', $txt );
$txt = preg_replace( "/<head(.+?)>(.+?)<\/head>/ms", '', $txt );
$txt = preg_replace( "/<body(.+?)>/", '', $txt );
$txt = str_replace( array( '</html>', '</body>' ), '', $txt );
$txt = preg_replace( "/<script(.+?)>(.+?)<\/script>/i", '', $txt );
$txt = preg_replace( "/style=['\"](.+?)['\"]/i", '', $txt );
$txt = str_replace( "<!--", '', $txt );
//-----------------------------------------
// If anything nasty is still there, strip it out
//-----------------------------------------
$txt = preg_replace( '#<(\s+?)?s(\s+?)?c(\s+?)?r(\s+?)?i(\s+?)?p(\s+?)?t#is' , "&lt;script" , $txt );
$txt = preg_replace( '#<(\s+?)?/(\s+?)?s(\s+?)?c(\s+?)?r(\s+?)?i(\s+?)?p(\s+?)?t#is', "&lt;/script", $txt );
$txt = preg_replace( "/(j)avascript/i" , "\\1&#097;v&#097;script", $txt );
$txt = preg_replace( '/(e)((\/\*.*?\*\/)*)x((\/\*.*?\*\/)*)p((\/\*.*?\*\/)*)r((\/\*.*?\*\/)*)e((\/\*.*?\*\/)*)s((\/\*.*?\*\/)*)s((\/\*.*?\*\/)*)i((\/\*.*?\*\/)*)o((\/\*.*?\*\/)*)n/is' , "\\1xp<b></b>ressi&#111;n" , $txt );
$txt = preg_replace( '/(e)((\\\|&#092;)*)x((\\\|&#092;)*)p((\\\|&#092;)*)r((\\\|&#092;)*)e((\\\|&#092;)*)s((\\\|&#092;)*)s((\\\|&#092;)*)i((\\\|&#092;)*)o((\\\|&#092;)*)n/is' , "\\1xp<b></b>ressi&#111;n" , $txt );
$txt = preg_replace( '/m((\\\|&#092;)*)o((\\\|&#092;)*)z((\\\|&#092;)*)\-((\\\|&#092;)*)b((\\\|&#092;)*)i((\\\|&#092;)*)n((\\\|&#092;)*)d((\\\|&#092;)*)i((\\\|&#092;)*)n((\\\|&#092;)*)g/is' , "moz-<b></b>b&#105;nding" , $txt );
$txt = str_ireplace( "about:" , "&#097;bout:" , $txt );
$txt = str_ireplace( "document." , "&#100;ocument." , $txt );
$txt = str_ireplace( "window." , "wind&#111;w." , $txt );
$event_handlers = array( 'mouseover', 'mouseout', 'mouseup', 'mousemove', 'mousedown', 'mouseenter', 'mouseleave', 'mousewheel',
'contextmenu', 'click', 'dblclick', 'load', 'unload', 'submit', 'blur', 'focus', 'resize', 'scroll',
'change', 'reset', 'select', 'selectionchange', 'selectstart', 'start', 'stop', 'keydown', 'keyup',
'keypress', 'abort', 'error', 'dragdrop', 'move', 'moveend', 'movestart', 'activate', 'afterprint',
'afterupdate', 'beforeactivate', 'beforecopy', 'beforecut', 'beforedeactivate', 'beforeeditfocus',
'beforepaste', 'beforeprint', 'beforeunload', 'begin', 'bounce', 'cellchange', 'controlselect',
'copy', 'cut', 'paste', 'dataavailable', 'datasetchanged', 'datasetcomplete', 'deactivate', 'drag',
'dragend', 'dragleave', 'dragenter', 'dragover', 'drop', 'end', 'errorupdate', 'filterchange', 'finish',
'focusin', 'focusout', 'help', 'layoutcomplete', 'losecapture', 'mediacomplete', 'mediaerror', 'outofsync',
'pause', 'propertychange', 'progress', 'readystatechange', 'repeat', 'resizeend', 'resizestart', 'resume',
'reverse', 'rowsenter', 'rowexit', 'rowdelete', 'rowinserted', 'seek', 'syncrestored', 'timeerror',
'trackchange', 'urlflip',
);
foreach( $event_handlers as $handler )
{
$txt = str_ireplace( 'on' . $handler, '&#111;n' . $handler, $txt );
}
return $txt;
}
}