* Invision Power Services * IP.Board v3.1.4 * Parse Incoming Emails * Last Updated: $Date: 2010-11-15 10:45:42 -0500 (Mon, 15 Nov 2010) $ * * * @author $Author: mark $ * @copyright (c) 2010 Invision Power Services, Inc. * @license http://www.invisionpower.com/community/board/license.html * @package IP.Board * @subpackage Kernel * @link http://www.invisionpower.com * @since 25th June 2010 * @version $Revision: 486 $ */ class classIncomingEmail { /** * Recipient Address * * @var string * @see __construct */ public $to; /** * Sender Address * * @var string * @see __construct */ public $from; /** * Subject * * @var string * @see __construct */ public $subject; /** * Message Body * * @var string * @see __construct */ public $message; /** * Attachments * This is an associative array of attachment data. * The keys are IDs which are associated with taks in $this->body. * The values are an array of data to insert into the attachments database table after attach_member_id and attach_rel_module have been set. * * @var array * @see __construct */ public $attachments; /** * Constructor * Sets $this->to, $this->from, $this->subject, $this->message and $this->attachments * * @param string Raw message with headers */ public function __construct( $email ) { //-------------------------------------- // Get some stuff //-------------------------------------- if ( class_exists( 'ipsRegistry' ) ) { $this->DB = ipsRegistry::DB(); // Fetch upload path $this->upload_dir = ipsRegistry::$settings['upload_dir']; $this->upload_url = ipsRegistry::$settings['upload_url']; } else { $this->initDB(); // Fetch upload path $this->DB->build( array( 'select' => 'conf_key,conf_value', 'from' => 'core_sys_conf_settings', 'where' => "conf_key='upload_dir' OR conf_key='upload_url'" ) ); $this->DB->execute(); while ( $r = $this->DB->fetch() ) { if ( $r['conf_key'] == 'upload_dir' ) { $this->upload_dir = $r['conf_value']; } elseif ( $r['conf_key'] == 'upload_url' ) { $this->upload_url = $r['conf_value']; } } } // And allowed attachment types $this->types = array(); $this->DB->build( array( 'select' => '*', 'from' => 'attachments_type' ) ); $this->DB->execute(); while ( $r = $this->DB->fetch() ) { $this->types[ $r['atype_mimetype'] ][] = $r; } //-------------------------------------- // Pass to PEAR //-------------------------------------- // It raises strict warnings @error_reporting( E_NONE ); @ini_set( 'display_errors', 'off' ); require_once ( IPS_KERNEL_PATH . 'PEAR/Mail/mimeDecode.php' ); $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'] ) { $mail->headers['to'] = $mail->headers['delivered-to']; } $to = array(); if ( strpos( $mail->headers['to'], ',' ) === FALSE ) { $mail->headers['to'] = array( $mail->headers['to'] ); } else { $mail->headers['to'] = explode( ',', $mail->headers['to'] ); } foreach ( $mail->headers['to'] as $_to ) { if ( preg_match( "/.+? <(.+?)>/", $_to, $matches ) ) { $to[] = $matches[1]; } else { $to[] = trim( $_to, '<>' ); } } $this->to = implode( ',', $to ); /* From */ if ( preg_match( "/.+? <(.+?)>/", $mail->headers['from'], $matches ) ) { $this->from = $matches[1]; } else { $this->from = trim( $mail->headers['from'], '<>' ); } /* Subject */ $this->subject = $mail->headers['subject'] ? $mail->headers['subject'] : '(No Subject)'; //-------------------------------------- // Parse Body //-------------------------------------- $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" ), '
', htmlentities( strip_tags( $mail->body ) ) ); } else { $this->message = str_replace( array( "\n", "\r", "\r\n" ), '
', htmlentities( $mail->body ) );; } } /* Normal message, sent in both HTML and normal */ elseif ( $mail->ctype_primary == 'multipart' AND $mail->ctype_secondary == 'alternative' ) { $this->message = ''; foreach ( $mail->parts as $part ) { if ( $part->ctype_secondary == 'html' ) { $this->message = str_replace( array( "\n", "\r", "\r\n" ), '
', htmlentities( strip_tags( $part->body ) ) );; } else { $this->message = str_replace( array( "\n", "\r", "\r\n" ), '
', htmlentities( $part->body ) );; if ( $part->ctype_secondary == 'plain' ) { break; } } } } /* Multipart message (with attachments and stuff) */ else { foreach ( $mail->parts as $part ) { if ( $part->ctype_primary == 'multipart' AND $part->ctype_secondary == 'alternative' ) { $add = ''; foreach ( $part->parts as $subpart ) { $add = str_replace( array( "\n", "\r", "\r\n" ), '
', $subpart->body ); if ( $subpart->ctype_secondary == 'plain' ) { break; } } $this->message .= $add; } elseif ( $part->ctype_primary == 'text' ) { if ( $part->ctype_secondary == 'html' ) { $this->message .= str_replace( array( "\n", "\r", "\r\n" ), '
', htmlentities( strip_tags( $part->body ) ) );; } else { $this->message .= str_replace( array( "\n", "\r", "\r\n" ), '
', htmlentities( $part->body ) );; } } else { $mime = "{$part->ctype_primary}/{$part->ctype_secondary}"; foreach ( $this->types[ $mime ] as $data ) { $name_parts = explode( '.', $part->ctype_parameters['name'] ); $ext = array_pop( $name_parts ); if ( $data['atype_extension'] == $ext and $data['atype_post'] ) { /* Create the file */ $masked_name = md5( uniqid( 'email' ) ) . "-{$part->ctype_parameters['name']}"; while ( file_exists( $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 .= ""; break; } } } } } } /** * Route * Uses incoming Email rules to route Email */ public function route() { $unrouted = TRUE; $this->DB->build( array( 'select' => '*', 'from' => 'core_incoming_emails' ) ); $this->DB->execute(); while ( $row = $this->DB->fetch() ) { switch ( $row['rule_criteria_field'] ) { case 'to': $analyse = $this->to; break; case 'from': $analyse = $this->from; break; case 'sbjt': $analyse = $this->subject; break; case 'body': $analyse = $this->message; break; } $match = false; switch ( $row['rule_criteria_type'] ) { case 'ctns': $match = (bool) ( strpos( $analyse, $row['rule_criteria_value'] ) !== FALSE ); break; case 'eqls': if ( strpos( $analyse, ',' ) !== FALSE ) { $match = (bool) in_array( $analyse, explode( ',', $analyse ) ); } else { $match = (bool) ( $analyse == $row['rule_criteria_value'] ); } break; case 'regx': $match = (bool) ( preg_match( "/{$row['rule_criteria_value']}/", $analyse ) == 1 ); break; } if ( $match ) { $unrouted = FALSE; if ( $row['rule_app'] != '--' ) { $appdir = IPS_ROOT_PATH . 'applications_addon/ips/' . $row['rule_app']; if ( !is_dir( $appdir ) ) { $appdir = IPS_ROOT_PATH . 'applications_addon/other/' . $row['rule_app']; } if ( !is_dir( $appdir ) ) { $appdir = IPS_ROOT_PATH . 'applications/' . $row['rule_app']; } if ( file_exists( $appdir . '/extensions/incomingEmails.php' ) ) { $class = 'incomingEmails_' . $row['rule_app']; require_once $appdir . '/extensions/incomingEmails.php'; $class = new $class; $class->process( $this->DB, $this->to, $this->from, $this->subject, $this->message, $this->attachments ); } } break; } } if ( $unrouted ) { $apps = array(); foreach ( glob( IPS_ROOT_PATH . 'applications/*' ) as $f ) { if ( file_exists( $f . '/extensions/incomingEmails.php' ) ) { $bits = explode( '/', $f ); $_appdir = array_pop( $bits ); $apps[ $_appdir ] = $f; } } foreach ( glob( IPS_ROOT_PATH . 'applications_addon/ips/*' ) as $f ) { if ( file_exists( $f . '/extensions/incomingEmails.php' ) ) { $bits = explode( '/', $f ); $_appdir = array_pop( $bits ); $apps[ $_appdir ] = $f; } } foreach ( glob( IPS_ROOT_PATH . 'applications_addon/other/*' ) as $f ) { if ( file_exists( $f . '/extensions/incomingEmails.php' ) ) { $bits = explode( '/', $f ); $_appdir = array_pop( $bits ); $apps[ $_appdir ] = $f; } } foreach ( $apps as $_appdir => $appdir ) { require_once $appdir . '/extensions/incomingEmails.php'; $class = 'incomingEmails_' . $_appdir; $i = new $class; if ( $i->handleUnrouted( $this->DB, $this->to, $this->from, $this->subject, $this->message, $this->attachments ) ) { break; } } } } /** * Init DB * Since this class may be being called when ipsRegistry * is not set up, we may need to create a DB object * * @see route() */ private function initDB() { /* Init */ $INFO = array(); @require( DOC_IPS_ROOT_PATH . 'conf_global.php' ); $this->DB_driver = strtolower( $INFO['sql_driver'] ); require_once ( IPS_KERNEL_PATH . 'classDb' . ucwords( $this->DB_driver ) . ".php" ); $classname = "db_driver_" . $this->DB_driver; $this->DB = new $classname; $this->DB->obj['sql_database'] = $INFO['sql_database']; $this->DB->obj['sql_user'] = $INFO['sql_user']; $this->DB->obj['sql_pass'] = $INFO['sql_pass']; $this->DB->obj['sql_host'] = $INFO['sql_host']; $this->DB->obj['sql_charset'] = $INFO['sql_charset']; $this->DB->obj['sql_tbl_prefix'] = $INFO['sql_tbl_prefix'] ? $INFO['sql_tbl_prefix'] : ''; $this->DB->connect(); } }