'1 (Highest)', 2 => '2 (High)', 3 => '3 (Normal)', 4 => '4 (Low)', 5 => '5 (Lowest)' ); /** * mbstring.func_overload flag * * @var bool */ protected static $func_overload; // -------------------------------------------------------------------- /** * Constructor - Sets Email Preferences * * The constructor can be passed an array of config values * * @param array $config = array() * @return void */ public function __construct(array $config = array()) { $this->charset = config_item('charset'); $this->initialize($config); $this->_safe_mode = (! is_php('5.4') && ini_get('safe_mode')); isset(self::$func_overload) or self::$func_overload = (! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload')); log_message('info', 'Email Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize preferences * * @param array $config * @return CI_Email */ public function initialize(array $config = array()) { $this->clear(); foreach ($config as $key => $val) { if (isset($this->$key)) { $method = 'set_' . $key; if (method_exists($this, $method)) { $this->$method($val); } else { $this->$key = $val; } } } $this->charset = strtoupper($this->charset); $this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]); return $this; } // -------------------------------------------------------------------- /** * Initialize the Email Data * * @param bool * @return CI_Email */ public function clear($clear_attachments = false) { $this->_subject = ''; $this->_body = ''; $this->_finalbody = ''; $this->_header_str = ''; $this->_replyto_flag = false; $this->_recipients = array(); $this->_cc_array = array(); $this->_bcc_array = array(); $this->_headers = array(); $this->_debug_msg = array(); $this->set_header('Date', $this->_set_date()); if ($clear_attachments !== false) { $this->_attachments = array(); } return $this; } // -------------------------------------------------------------------- /** * Set FROM * * @param string $from * @param string $name * @param string $return_path = NULL Return-Path * @return CI_Email */ public function from($from, $name = '', $return_path = null) { if (preg_match('/\<(.*)\>/', $from, $match)) { $from = $match[1]; } if ($this->validate) { $this->validate_email($this->_str_to_array($from)); if ($return_path) { $this->validate_email($this->_str_to_array($return_path)); } } // prepare the display name if ($name !== '') { // only use Q encoding if there are characters that would require it if (! preg_match('/[\200-\377]/', $name)) { // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes $name = '"' . addcslashes($name, "\0..\37\177'\"\\") . '"'; } else { $name = $this->_prep_q_encoding($name); } } $this->set_header('From', $name . ' <' . $from . '>'); isset($return_path) or $return_path = $from; $this->set_header('Return-Path', '<' . $return_path . '>'); return $this; } // -------------------------------------------------------------------- /** * Set Reply-to * * @param string * @param string * @return CI_Email */ public function reply_to($replyto, $name = '') { if (preg_match('/\<(.*)\>/', $replyto, $match)) { $replyto = $match[1]; } if ($this->validate) { $this->validate_email($this->_str_to_array($replyto)); } if ($name !== '') { // only use Q encoding if there are characters that would require it if (! preg_match('/[\200-\377]/', $name)) { // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes $name = '"' . addcslashes($name, "\0..\37\177'\"\\") . '"'; } else { $name = $this->_prep_q_encoding($name); } } $this->set_header('Reply-To', $name . ' <' . $replyto . '>'); $this->_replyto_flag = true; return $this; } // -------------------------------------------------------------------- /** * Set Recipients * * @param string * @return CI_Email */ public function to($to) { $to = $this->_str_to_array($to); $to = $this->clean_email($to); if ($this->validate) { $this->validate_email($to); } if ($this->_get_protocol() !== 'mail') { $this->set_header('To', implode(', ', $to)); } $this->_recipients = $to; return $this; } // -------------------------------------------------------------------- /** * Set CC * * @param string * @return CI_Email */ public function cc($cc) { $cc = $this->clean_email($this->_str_to_array($cc)); if ($this->validate) { $this->validate_email($cc); } $this->set_header('Cc', implode(', ', $cc)); if ($this->_get_protocol() === 'smtp') { $this->_cc_array = $cc; } return $this; } // -------------------------------------------------------------------- /** * Set BCC * * @param string * @param string * @return CI_Email */ public function bcc($bcc, $limit = '') { if ($limit !== '' && is_numeric($limit)) { $this->bcc_batch_mode = true; $this->bcc_batch_size = $limit; } $bcc = $this->clean_email($this->_str_to_array($bcc)); if ($this->validate) { $this->validate_email($bcc); } if ($this->_get_protocol() === 'smtp' or ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size)) { $this->_bcc_array = $bcc; } else { $this->set_header('Bcc', implode(', ', $bcc)); } return $this; } // -------------------------------------------------------------------- /** * Set Email Subject * * @param string * @return CI_Email */ public function subject($subject) { $subject = $this->_prep_q_encoding($subject); $this->set_header('Subject', $subject); return $this; } // -------------------------------------------------------------------- /** * Set Body * * @param string * @return CI_Email */ public function message($body) { $this->_body = rtrim(str_replace("\r", '', $body)); /* strip slashes only if magic quotes is ON if we do it with magic quotes OFF, it strips real, user-inputted chars. NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and it will probably not exist in future versions at all. */ if (! is_php('5.4') && get_magic_quotes_gpc()) { $this->_body = stripslashes($this->_body); } return $this; } // -------------------------------------------------------------------- /** * Assign file attachments * * @param string $file Can be local path, URL or buffered content * @param string $disposition = 'attachment' * @param string $newname = NULL * @param string $mime = '' * @return CI_Email */ public function attach($file, $disposition = '', $newname = null, $mime = '') { if ($mime === '') { if (strpos($file, '://') === false && ! file_exists($file)) { $this->_set_error_message('lang:email_attachment_missing', $file); return false; } if (! $fp = @fopen($file, 'rb')) { $this->_set_error_message('lang:email_attachment_unreadable', $file); return false; } $file_content = stream_get_contents($fp); $mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION)); fclose($fp); } else { $file_content = & $file; // buffered file } $this->_attachments[] = array( 'name' => array($file, $newname), 'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters 'type' => $mime, 'content' => chunk_split(base64_encode($file_content)), 'multipart' => 'mixed' ); return $this; } // -------------------------------------------------------------------- /** * Set and return attachment Content-ID * * Useful for attached inline pictures * * @param string $filename * @return string */ public function attachment_cid($filename) { for ($i = 0, $c = count($this->_attachments); $i < $c; $i++) { if ($this->_attachments[$i]['name'][0] === $filename) { $this->_attachments[$i]['multipart'] = 'related'; $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]) . '@'); return $this->_attachments[$i]['cid']; } } return false; } // -------------------------------------------------------------------- /** * Add a Header Item * * @param string * @param string * @return CI_Email */ public function set_header($header, $value) { $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value); return $this; } // -------------------------------------------------------------------- /** * Convert a String to an Array * * @param string * @return array */ protected function _str_to_array($email) { if (! is_array($email)) { return (strpos($email, ',') !== false) ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY) : (array) trim($email); } return $email; } // -------------------------------------------------------------------- /** * Set Multipart Value * * @param string * @return CI_Email */ public function set_alt_message($str) { $this->alt_message = (string) $str; return $this; } // -------------------------------------------------------------------- /** * Set Mailtype * * @param string * @return CI_Email */ public function set_mailtype($type = 'text') { $this->mailtype = ($type === 'html') ? 'html' : 'text'; return $this; } // -------------------------------------------------------------------- /** * Set Wordwrap * * @param bool * @return CI_Email */ public function set_wordwrap($wordwrap = true) { $this->wordwrap = (bool) $wordwrap; return $this; } // -------------------------------------------------------------------- /** * Set Protocol * * @param string * @return CI_Email */ public function set_protocol($protocol = 'mail') { $this->protocol = in_array($protocol, $this->_protocols, true) ? strtolower($protocol) : 'mail'; return $this; } // -------------------------------------------------------------------- /** * Set Priority * * @param int * @return CI_Email */ public function set_priority($n = 3) { $this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3; return $this; } // -------------------------------------------------------------------- /** * Set Newline Character * * @param string * @return CI_Email */ public function set_newline($newline = "\n") { $this->newline = in_array($newline, array("\n", "\r\n", "\r")) ? $newline : "\n"; return $this; } // -------------------------------------------------------------------- /** * Set CRLF * * @param string * @return CI_Email */ public function set_crlf($crlf = "\n") { $this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf; return $this; } // -------------------------------------------------------------------- /** * Get the Message ID * * @return string */ protected function _get_message_id() { $from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']); return '<' . uniqid('') . strstr($from, '@') . '>'; } // -------------------------------------------------------------------- /** * Get Mail Protocol * * @return mixed */ protected function _get_protocol() { $this->protocol = strtolower($this->protocol); in_array($this->protocol, $this->_protocols, true) or $this->protocol = 'mail'; return $this->protocol; } // -------------------------------------------------------------------- /** * Get Mail Encoding * * @return string */ protected function _get_encoding() { in_array($this->_encoding, $this->_bit_depths) or $this->_encoding = '8bit'; foreach ($this->_base_charsets as $charset) { if (strpos($this->charset, $charset) === 0) { $this->_encoding = '7bit'; } } return $this->_encoding; } // -------------------------------------------------------------------- /** * Get content type (text/html/attachment) * * @return string */ protected function _get_content_type() { if ($this->mailtype === 'html') { return empty($this->_attachments) ? 'html' : 'html-attach'; } elseif ($this->mailtype === 'text' && ! empty($this->_attachments)) { return 'plain-attach'; } return 'plain'; } // -------------------------------------------------------------------- /** * Set RFC 822 Date * * @return string */ protected function _set_date() { $timezone = date('Z'); $operator = ($timezone[0] === '-') ? '-' : '+'; $timezone = abs($timezone); $timezone = floor($timezone / 3600) * 100 + ($timezone % 3600) / 60; return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone); } // -------------------------------------------------------------------- /** * Mime message * * @return string */ protected function _get_mime_message() { return 'This is a multi-part message in MIME format.' . $this->newline . 'Your email application may not support this format.'; } // -------------------------------------------------------------------- /** * Validate Email Address * * @param string * @return bool */ public function validate_email($email) { if (! is_array($email)) { $this->_set_error_message('lang:email_must_be_array'); return false; } foreach ($email as $val) { if (! $this->valid_email($val)) { $this->_set_error_message('lang:email_invalid_address', $val); return false; } } return true; } // -------------------------------------------------------------------- /** * Email Validation * * @param string * @return bool */ public function valid_email($email) { if (function_exists('idn_to_ascii') && strpos($email, '@')) { list($account, $domain) = explode('@', $email, 2); $domain = defined('INTL_IDNA_VARIANT_UTS46') ? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : idn_to_ascii($domain); if ($domain !== false) { $email = $account . '@' . $domain; } } return (bool) filter_var($email, FILTER_VALIDATE_EMAIL); } // -------------------------------------------------------------------- /** * Clean Extended Email Address: Joe Smith * * @param string * @return string */ public function clean_email($email) { if (! is_array($email)) { return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email; } $clean_email = array(); foreach ($email as $addy) { $clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy; } return $clean_email; } // -------------------------------------------------------------------- /** * Build alternative plain text message * * Provides the raw message for use in plain-text headers of * HTML-formatted emails. * If the user hasn't specified his own alternative message * it creates one by stripping the HTML * * @return string */ protected function _get_alt_message() { if (! empty($this->alt_message)) { return ($this->wordwrap) ? $this->word_wrap($this->alt_message, 76) : $this->alt_message; } $body = preg_match('/\(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body; $body = str_replace("\t", '', preg_replace('#