source: trunk/lib/Email.inc.php @ 774

Last change on this file since 774 was 774, checked in by anonymous, 22 months ago

Add Email::setRawBody() for messages manually composed and encoded outside of this Email class, and need to bypass replacements, detection of mail header injection attacks, and the encoding provided by mb_send_mail().

File size: 23.6 KB
RevLine 
[23]1<?php
2/**
[362]3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
[396]5 * Copyright 2001-2012 Strangecode, LLC
[454]6 *
[362]7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
[454]13 *
[362]14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
[454]18 *
[362]19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
[460]23/*
24* Email.inc.php
25*
26* Easy email template usage.
27*
28* @author  Quinn Comendant <quinn@strangecode.com>
29* @version 1.0
30*
31* Example of use:
32---------------------------------------------------------------------
33// Setup email object.
[23]34$email = new Email(array(
35    'to' => array($frm['email'], 'q@lovemachine.local'),
[550]36    'from' => sprintf('"%s" <%s>', addcslashes($app->getParam('site_name'), '"'), $app->getParam('site_email')),
[24]37    'subject' => 'Your account has been activated',
[23]38));
[767]39$email->setTemplate('email_registration_confirm.eml');
[41]40// $email->setString('Or you can pass your message body as a string, also with {VARIABLES}.');
[23]41$email->replace(array(
[136]42    'site_name' => $app->getParam('site_name'),
43    'site_url' => $app->getParam('site_url'),
[23]44    'username' => $frm['username'],
45    'password' => $frm['password1'],
46));
47if ($email->send()) {
[136]48    $app->raiseMsg(sprintf(_("A confirmation email has been sent to %s."), $frm['email']), MSG_SUCCESS, __FILE__, __LINE__);
[23]49} else {
[136]50    $app->logMsg(sprintf('Error sending confirmation email to address %s', $frm['email']), LOG_NOTICE, __FILE__, __LINE__);
[23]51}
[460]52---------------------------------------------------------------------
53*/
[136]54
[502]55class Email
56{
[23]57
[24]58    // Default parameters, to be overwritten by setParam() and read with getParam()
[484]59    protected $_params = array(
[23]60        'to' => null,
61        'from' => null,
62        'subject' => null,
[774]63        'headers' => [],
[490]64        'envelope_sender_address' => null, // AKA the bounce-to address. Will default to 'from' if left null.
[212]65        'regex' => null,
[454]66
[212]67        // A single carriage return (\n) should terminate lines for locally injected mail.
68        // A carriage return + line-feed (\r\n) should be used if sending mail directly with SMTP.
69        'crlf' => "\n",
[454]70
[212]71        // RFC 2822 says line length MUST be no more than 998 characters, and SHOULD be no more than 78 characters, excluding the CRLF.
72        // http://mailformat.dan.info/body/linelength.html
73        'wrap' => true,
74        'line_length' => 75,
[622]75
76        'sandbox_mode' => null,
77        'sandbox_to_addr' => null,
[23]78    );
[42]79
[630]80    // String that contains filename of the template used (for logging).
81    protected $_template_filename;
82
[24]83    // String that contains the email body.
[484]84    protected $_template;
[42]85
[24]86    // String that contains the email body after replacements.
[484]87    protected $_template_replaced;
[23]88
[774]89    // String that contains the final email body.
90    // Use only when email is manually composed and encoded outside of this Email class, and need to bypass replacements, detection of mail header injection attacks, and the encoding provided by mb_send_mail().
91    protected $_raw_body;
92
[622]93    // Email debug modes.
94    const SANDBOX_MODE_REDIRECT = 1; // Send all mail to 'sandbox_to_addr'
95    const SANDBOX_MODE_STDERR = 2; // Log all mail to stderr
[628]96    const SANDBOX_MODE_LOG = 3; // Log all mail using $app->logMsg(
)
[622]97
[23]98    /**
99     * Constructor.
100     *
101     * @access  public
102     * @param   array   $params     Array of object parameters.
103     * @author  Quinn Comendant <quinn@strangecode.com>
104     * @since   28 Nov 2005 12:59:41
105     */
[468]106    public function __construct($params=null)
[23]107    {
[724]108        $app =& App::getInstance();
109
[334]110        // The regex used in validEmail(). Set here instead of in the default _params above so we can use the concatenation . dot.
[357]111        // This matches a (valid) email address as complex as:
[399]112        //      "Jane & Bob Smith" <bob&smith's/dep=sales!@smith-wick.ca.us > (Sales department)
[24]113        // ...and something as simple as:
114        //      x@x.com
[399]115        $this->setParam(array('regex' => '/^(?:(?:"[^"]*?"\s*|[^,@]*)(<\s*)|(?:"[^"]*?"|[^,@]*)\s+|)'   // Display name
[357]116        . '((?:[^.<>\s@",\[\]]+[^<>\s@",\[\]])*[^.<>\s@",\[\]]+)'       // Local-part
[23]117        . '@'                                                           // @
[735]118        . '((?:(\[)|[a-z0-9]?)'                                         // Domain, first char
[23]119        . '(?(4)'                                                       // Domain conditional for if first domain char is [
120        . '(?:[0-9]{1,3}\.){3}[0-9]{1,3}\]'                             // TRUE, matches IP address
121        . '|'
[735]122        . '[.-]?(?:[a-z0-9]+[-.])*(?:[a-z0-9]+\.)+[a-z]{2,19}))'        // FALSE, matches domain name
[23]123        . '(?(1)'                                                       // Comment conditional for if initial < exists
[490]124        . '(?:\s*>\s*|>\s+\([^,@]+\)\s*)'                               // TRUE, ensure ending >
[23]125        . '|'
[724]126        . '(?:|\s*|\s+\([^,@]+\)\s*))$/i' . $app->getParam('preg_u'))); // FALSE ensure there is no ending >
[23]127
[25]128        if (isset($params)) {
129            $this->setParam($params);
130        }
[23]131    }
132
133    /**
134     * Set (or overwrite existing) parameters by passing an array of new parameters.
135     *
136     * @access public
137     * @param  array    $params     Array of parameters (key => val pairs).
138     */
[468]139    public function setParam($params)
[23]140    {
[479]141        $app =& App::getInstance();
[454]142
[23]143        if (isset($params) && is_array($params)) {
144            // Enforce valid email addresses.
145            if (isset($params['to']) && !$this->validEmail($params['to'])) {
146                $params['to'] = null;
147            }
148            if (isset($params['from']) && !$this->validEmail($params['from'])) {
149                $params['from'] = null;
150            }
[490]151            if (isset($params['envelope_sender_address']) && !$this->validEmail($params['envelope_sender_address'])) {
152                $params['envelope_sender_address'] = null;
153            }
[23]154
155            // Merge new parameters with old overriding only those passed.
156            $this->_params = array_merge($this->_params, $params);
157        } else {
[136]158            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
[23]159        }
160    }
161
162    /**
163     * Return the value of a parameter, if it exists.
164     *
165     * @access public
166     * @param string $param        Which parameter to return.
167     * @return mixed               Configured parameter value.
168     */
[468]169    public function getParam($param)
[23]170    {
[479]171        $app =& App::getInstance();
[454]172
[478]173        if (array_key_exists($param, $this->_params)) {
[23]174            return $this->_params[$param];
175        } else {
[146]176            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
[23]177            return null;
178        }
179    }
180
[774]181    /*
182    * Append headers to $this->_params['headers']
183    *
184    * @access   public
185    * @param    array   $headers    Array of key=>val pairs that will be appended.
186    * @return   void
187    * @author   Quinn Comendant <quinn@strangecode.com>
188    * @since    23 Jul 2022 13:36:21
189    */
190    public function appendHeaders($headers)
191    {
192        $app =& App::getInstance();
193        if (!isset($headers) || !is_array($headers)) {
194            $app->logMsg(sprintf('%s requires an array of header values', __METHOD__), LOG_NOTICE, __FILE__, __LINE__);
195        }
196
197        $this->_params['headers'] = array_merge($this->_params['headers'], $headers);
198    }
199
[23]200    /**
201     * Loads template from file to generate email body.
202     *
203     * @access  public
204     * @param   string  $template   Filename of email template.
205     * @author  Quinn Comendant <quinn@strangecode.com>
206     * @since   28 Nov 2005 12:56:23
207     */
[468]208    public function setTemplate($template)
[23]209    {
[479]210        $app =& App::getInstance();
[454]211
[23]212        // Load file, using include_path.
[774]213        $this->_template = file_get_contents($template, true);
214        if (!$this->_template || '' == trim($this->_template)) {
215            $app->logMsg(sprintf('Email template file does not exist or is empty: %s', $template), LOG_ERR, __FILE__, __LINE__);
[23]216            $this->_template = null;
217            $this->_template_replaced = null;
[774]218            $this->_raw_body = null;
[23]219            return false;
220        }
[578]221
222        // Ensure template is UTF-8.
223        $detected_encoding = mb_detect_encoding($this->_template, array('UTF-8', 'ISO-8859-1', 'WINDOWS-1252'), true);
224        if ('UTF-8' != strtoupper($detected_encoding)) {
225            $this->_template = mb_convert_encoding($this->_template, 'UTF-8', $detected_encoding);
226        }
227
[24]228        // This could be a new template, so reset the _template_replaced.
[23]229        $this->_template_replaced = null;
[774]230        $this->_raw_body = null;
[630]231
232        $this->_template_filename = $template;
233
[23]234        return true;
235    }
236
237    /**
238     * Loads template from string to generate email body.
239     *
240     * @access  public
[774]241     * @param   string  $string   Content of email template.
[23]242     * @author  Quinn Comendant <quinn@strangecode.com>
243     * @since   28 Nov 2005 12:56:23
244     */
[468]245    public function setString($string)
[23]246    {
[479]247        $app =& App::getInstance();
[454]248
[41]249        if ('' == trim($string)) {
[774]250            $app->logMsg(sprintf('Empty string provided to %s.', __METHOD__), LOG_ERR, __FILE__, __LINE__);
[23]251            $this->_template_replaced = null;
[774]252            $this->_raw_body = null;
[23]253            return false;
254        } else {
[41]255            $this->_template = $string;
[24]256            // This could be a new template, so reset the _template_replaced.
[23]257            $this->_template_replaced = null;
[774]258            $this->_raw_body = null;
[630]259
260            $this->_template_filename = '(using Email::setString)';
261
[23]262            return true;
263        }
264    }
[42]265
[23]266    /**
267     * Replace variables in template with argument data.
268     *
269     * @access  public
270     * @param   array   $replacements   Array keys are the values to search for, array vales are the replacement values.
271     * @author  Quinn Comendant <quinn@strangecode.com>
272     * @since   28 Nov 2005 13:08:51
273     */
[468]274    public function replace($replacements)
[23]275    {
[479]276        $app =& App::getInstance();
[454]277
[24]278        // Ensure template exists.
[23]279        if (!isset($this->_template)) {
[136]280            $app->logMsg(sprintf('Cannot replace variables, no template defined.', null), LOG_ERR, __FILE__, __LINE__);
[42]281            return false;
[23]282        }
[42]283
[24]284        // Ensure replacements argument is an array.
[23]285        if (!is_array($replacements)) {
[136]286            $app->logMsg(sprintf('Cannot replace variables, invalid replacements.', null), LOG_ERR, __FILE__, __LINE__);
[42]287            return false;
[23]288        }
[42]289
[23]290        // Apply regex pattern to search elements.
291        $search = array_keys($replacements);
[696]292        array_walk($search, function (&$v) {
[759]293            $v = sprintf('{%s}', mb_strtoupper($v));
[696]294        });
[23]295
[35]296        // Replacement values.
[23]297        $replace = array_values($replacements);
[42]298
[24]299        // Search and replace all values at once.
[216]300        $this->_template_replaced = str_replace($search, $replace, $this->_template);
[23]301    }
302
[774]303    /**
304     * Set the final email body to send. Using this disables any further post-processing, including encoding and scanning for mail header injection attacks.
305     *
306     * @access  public
307     * @param   string  $body   Final email body.
308     * @author  Quinn Comendant <quinn@strangecode.com>
309     * @since   23 Jul 2022 12:01:52
310     */
311    public function setRawBody($string)
312    {
313        $app =& App::getInstance();
314
315        if ('' == trim($string)) {
316            $app->logMsg(sprintf('Empty string provided to %s.', __METHOD__), LOG_ERR, __FILE__, __LINE__);
317            $this->_template = null;
318            $this->_template_replaced = null;
319            $this->_raw_body = null;
320            return false;
321        } else {
322            $this->_template = null;
323            $this->_template_replaced = null;
324            $this->_raw_body = $string;
325
326            $this->_template_filename = '(using Email::setRawBody)';
327
328            return true;
329        }
330    }
331
[502]332    /*
333    * Returns the body of the current email. This can be used to store the message that is being sent.
334    * It will use the original template, or the replaced template if it has been processed.
[618]335    * You can also use this function to do post-processing on the email body before sending it,
336    * like removing extraneous lines:
[696]337    * $email->setString(preg_replace('/(?:(?:\r\n|\r|\n)\s*){2}/su', "\n\n", $email->getBody()));
[502]338    *
339    * @access   public
340    * @return   string  Message body.
341    * @author   Quinn Comendant <quinn@strangecode.com>
342    * @version  1.0
343    * @since    18 Nov 2014 21:15:19
344    */
345    public function getBody()
346    {
[559]347        $app =& App::getInstance();
348
[774]349        if (isset($this->_raw_body)) {
350            return $this->_raw_body;
351        }
352
[502]353        $final_body = isset($this->_template_replaced) ? $this->_template_replaced : $this->_template;
354        // Ensure all placeholders have been replaced. Find anything with {...} characters.
355        if (preg_match('/({[^}]+})/', $final_body, $unreplaced_match)) {
356            unset($unreplaced_match[0]);
[743]357            $app->logMsg(sprintf('Cannot get email body. Unreplaced variable(s) "%s" in template "%s"', getDump($unreplaced_match), $this->_template_filename), LOG_ERR, __FILE__, __LINE__);
[502]358            return false;
359        }
360        return $final_body;
361    }
362
[23]363    /**
364     * Send email using PHP's mail() function.
365     *
366     * @access  public
367     * @param   string  $to
368     * @param   string  $from
369     * @param   string  $subject
370     * @author  Quinn Comendant <quinn@strangecode.com>
371     * @since   28 Nov 2005 12:56:09
372     */
[468]373    public function send($to=null, $from=null, $subject=null, $headers=null)
[23]374    {
[479]375        $app =& App::getInstance();
[282]376
[24]377        // Use arguments if provided.
[23]378        if (isset($to)) {
[490]379            $this->setParam(array('to' => $to));
[23]380        }
381        if (isset($from)) {
[490]382            $this->setParam(array('from' => $from));
[23]383        }
384        if (isset($subject)) {
[490]385            $this->setParam(array('subject' => $subject));
[23]386        }
[38]387        if (isset($headers)) {
[490]388            $this->setParam(array('headers' => $headers));
[38]389        }
[36]390
[24]391        // Ensure required values exist.
[43]392        if (!isset($this->_params['subject'])) {
[364]393            $app->logMsg('Cannot send email. SUBJECT not defined.', LOG_ERR, __FILE__, __LINE__);
[23]394            return false;
[774]395        } else if (!isset($this->_template) && !isset($this->_raw_body)) {
396            $app->logMsg(sprintf('Cannot send email: "%s". Need a template or raw body.', $this->_params['subject']), LOG_ERR, __FILE__, __LINE__);
[43]397            return false;
[23]398        } else if (!isset($this->_params['to'])) {
[136]399            $app->logMsg(sprintf('Cannot send email: "%s". TO not defined.', $this->_params['subject']), LOG_NOTICE, __FILE__, __LINE__);
[23]400            return false;
401        } else if (!isset($this->_params['from'])) {
[136]402            $app->logMsg(sprintf('Cannot send email: "%s". FROM not defined.', $this->_params['subject']), LOG_ERR, __FILE__, __LINE__);
[23]403            return false;
404        }
405
[774]406        // Final “to” header can have multiple addresses if in an array.
407        $final_to = is_array($this->_params['to']) ? join(', ', $this->_params['to']) : $this->_params['to'];
408        if (!preg_match('/(?:>$|>,)/', $final_to)) {
409            $app->logMsg(sprintf('Email addresses should be enclosed in <angle brackets>: %s', $final_to), LOG_NOTICE, __FILE__, __LINE__);
[212]410        }
[24]411
[774]412        // “From” headers are custom headers.
413        $this->appendHeaders(['From' => $this->_params['from']]);
[42]414
[38]415        // Process headers.
[670]416        $final_headers_arr = array();
417        $final_headers = '';
[774]418        foreach ($this->_params['headers'] as $key => $val) {
[490]419            // Validate key and values.
[770]420            if (!isset($val) || !strlen($val)) {
[684]421                $app->logMsg(sprintf('Empty email header provided: %s', $key), LOG_NOTICE, __FILE__, __LINE__);
[490]422                continue;
[424]423            }
[774]424            // Ensure headers meet RFC requirements.
425            // https://datatracker.ietf.org/doc/html/rfc5322#section-2.1.1
426            // https://datatracker.ietf.org/doc/html/rfc5322#section-2.2
427            if (!strlen($key) || strlen($key . $val) > 998 || preg_match('/[^\x21-\x39\x3B-\x7E]/', $key)) {
[500]428                $app->logMsg(sprintf('Broken email header provided: %s=%s', $key, $val), LOG_WARNING, __FILE__, __LINE__);
429                continue;
430            }
[490]431            // If the envelope_sender_address was given as a header, move it to the correct place.
[622]432            if ('envelope_sender_address' == strtolower($key)) {
[490]433                $this->_params['envelope_sender_address'] = isset($this->_params['envelope_sender_address']) ? $this->_params['envelope_sender_address'] : $val;
434                continue;
435            }
[622]436            // If we're sending in sandbox mode, remove any headers with recipient addresses.
437            if ($this->getParam('sandbox_mode') == self::SANDBOX_MODE_REDIRECT && in_array(strtolower($key), array('to', 'cc', 'bcc')) && mb_strpos($val, '@') !== false) {
438                // Don't carry this into the $final_headers.
439                $app->logMsg(sprintf('Skipping header in sandbox mode: %s=%s', $key, $val), LOG_DEBUG, __FILE__, __LINE__);
440                continue;
441            }
[670]442            $final_headers_arr[] = sprintf('%s: %s', $key, $val);
[38]443        }
[670]444        $final_headers = join($this->getParam('crlf'), $final_headers_arr);
[42]445
[24]446        // This is the address where delivery problems are sent to. We must strip off everything except the local@domain part.
[490]447        if (isset($this->_params['envelope_sender_address'])) {
448            $envelope_sender_address = sprintf('<%s>', trim($this->_params['envelope_sender_address'], '<>'));
449        } else {
[735]450            $envelope_sender_address = preg_replace('/^.*<?([^\s@\[\]<>()]+\@[A-Za-z0-9.-]{1,}\.[A-Za-z]{2,19})>?$/iU' . $app->getParam('preg_u'), '$1', $this->_params['from']);
[490]451        }
[212]452        if ('' != $envelope_sender_address && $this->validEmail($envelope_sender_address)) {
[490]453            $additional_parameter = sprintf('-f %s', $envelope_sender_address);
[212]454        } else {
[490]455            $additional_parameter = '';
[212]456        }
[42]457
[774]458        if (isset($this->_raw_body)) {
459            $mail_function = 'mail';
460            $final_body = $this->_raw_body;
461        } else {
462            $mail_function = 'mb_send_mail';
463
464            // Wrap email text body, using _template_replaced if replacements have been used, or just a fresh _template if not.
465            $final_body = isset($this->_template_replaced) ? $this->_template_replaced : $this->_template;
466            if (false !== $this->getParam('wrap')) {
467                $final_body = wordwrap($final_body, $this->getParam('line_length'), $this->getParam('crlf'));
468            }
469
470            // Ensure all placeholders have been replaced. Find anything with {...} characters.
471            if (preg_match('/({[^}]+})/', $final_body, $unreplaced_match)) {
472                unset($unreplaced_match[0]);
473                $app->logMsg(sprintf('Unreplaced variable(s) "%s" in template "%s"', getDump($unreplaced_match), $this->_template_filename), LOG_ERR, __FILE__, __LINE__);
474                return false;
475            }
476
477            // Check for mail header injection attacks.
478            $full_mail_content = join($this->getParam('crlf'), array($final_to, $this->_params['subject'], $final_body));
479            if (preg_match("/(^|[\n\r])(Content-Type|MIME-Version|Content-Transfer-Encoding|Bcc|Cc)\s*:/i", $full_mail_content)) {
480                $app->logMsg(sprintf('Mail header injection attack in content: %s', $full_mail_content), LOG_WARNING, __FILE__, __LINE__);
481                return false;
482            }
[36]483        }
[346]484
[622]485        // Enter sandbox mode, if specified.
486        switch ($this->getParam('sandbox_mode')) {
487        case self::SANDBOX_MODE_REDIRECT:
488            if (!$this->getParam('sandbox_to_addr')) {
489                $app->logMsg(sprintf('Email sandbox_mode is SANDBOX_MODE_REDIRECT but sandbox_to_addr is not set.', null), LOG_ERR, __FILE__, __LINE__);
490                break;
491            }
492            $final_to = $this->getParam('sandbox_to_addr');
493            break;
494
495        case self::SANDBOX_MODE_STDERR:
496            file_put_contents('php://stderr', sprintf("Subject: %s\nTo: %s\n%s\n\n%s", $this->getParam('subject'), $final_to, str_replace($this->getParam('crlf'), "\n", $final_headers), $final_body), FILE_APPEND);
497            return true;
[628]498
499        case self::SANDBOX_MODE_LOG:
[668]500            // Temporarily modify log settings to allow full multi-line emails to appear in logs.
[628]501            $log_serialize = $app->getParam('log_serialize');
[668]502            $log_message_max_length = $app->getParam('log_message_max_length');
503            $app->setParam(array('log_serialize' => false, 'log_message_max_length' => 65536));
504            $app->logMsg(sprintf("\nSubject: %s\nTo: %s\n%s\n\n%s", $this->getParam('subject'), $final_to, trim(str_replace($this->getParam('crlf'), "\n", $final_headers)), trim($final_body)), LOG_DEBUG, __FILE__, __LINE__);
505            $app->setParam(array('log_serialize' => $log_serialize, 'log_message_max_length' => $log_message_max_length));
[628]506            return true;
[622]507        }
508
[454]509        // Send email without 5th parameter if safemode is enabled.
[345]510        if (ini_get('safe_mode')) {
[774]511            $ret = $mail_function($final_to, $this->_params['subject'], $final_body, $final_headers);
[479]512        } else {
[774]513            $ret = $mail_function($final_to, $this->_params['subject'], $final_body, $final_headers, $additional_parameter);
[347]514        }
[454]515
[346]516        // Ensure message was successfully accepted for delivery.
517        if ($ret) {
[770]518            $app->logMsg(sprintf('Email successfully sent to %s: %s', $final_to, $this->_params['subject']), LOG_INFO, __FILE__, __LINE__);
[346]519            return true;
520        } else {
[490]521            $app->logMsg(sprintf('Email failure: %s, %s, %s, %s', $final_to, $this->_params['subject'], str_replace("\r\n", '\r\n', $final_headers), $additional_parameter), LOG_WARNING, __FILE__, __LINE__);
[346]522            return false;
[454]523        }
[23]524    }
[42]525
[23]526    /**
527     * Validates an email address based on the recommendations in RFC 3696.
[42]528     * Is more loose than restrictive, to allow the many valid variants of
[23]529     * email addresses while catching the most common mistakes. Checks an array too.
530     * http://www.faqs.org/rfcs/rfc822.html
531     * http://www.faqs.org/rfcs/rfc2822.html
532     * http://www.faqs.org/rfcs/rfc3696.html
533     * http://www.faqs.org/rfcs/rfc1035.html
534     *
535     * @access  public
536     * @param   mixed  $email  Address to check, string or array.
537     * @return  bool    Validity of address.
538     * @author  Quinn Comendant <quinn@strangecode.com>
539     * @since   30 Nov 2005 22:00:50
540     */
[468]541    public function validEmail($email)
[23]542    {
[479]543        $app =& App::getInstance();
[454]544
[24]545        // If an array, check values recursively.
[23]546        if (is_array($email)) {
547            foreach ($email as $e) {
548                if (!$this->validEmail($e)) {
549                    return false;
550                }
551            }
552            return true;
553        } else {
[334]554            // To be valid email address must match regex and fit within the length constraints.
[247]555            if (preg_match($this->getParam('regex'), $email, $e_parts) && mb_strlen($e_parts[2]) < 64 && mb_strlen($e_parts[3]) < 255) {
[24]556                return true;
557            } else {
[394]558                $app->logMsg(sprintf('Invalid email address: %s', $email), LOG_INFO, __FILE__, __LINE__);
[23]559                return false;
560            }
561        }
562    }
563}
564
Note: See TracBrowser for help on using the repository browser.