From 60aa38bd478d2fc6964ead6cbbcee41dc0bbb231 Mon Sep 17 00:00:00 2001 From: nuxsmin Date: Wed, 25 Jan 2017 18:03:46 +0100 Subject: [PATCH] * [FIX] Fixed broken component when showing complexity dialog * [MOD] Updated PHPMailer * [MOD] Improved LDAP username detection * [MOD] Improved email messages when generating temporary master pass --- inc/Exts/phpmailer/PHPMailer.php | 2446 ++++++++++------- inc/Exts/phpmailer/SMTP.php | 984 ++++--- inc/SP/Auth/Ldap/LdapBase.class.php | 13 +- .../ConfigActionController.class.php | 1 + inc/SP/Core/Messages/MessageBase.class.php | 8 +- inc/SP/Core/Messages/NoticeMessage.class.php | 8 +- inc/SP/Log/Email.class.php | 13 +- inc/SP/Mgmt/Users/UserUtil.class.php | 3 +- inc/SP/Util/Util.class.php | 1 + .../css/mdl-jquery-modal-dialog.css | 3 +- .../css/mdl-jquery-modal-dialog.css.map | 2 +- .../css/mdl-jquery-modal-dialog.min.css | 2 +- .../css/mdl-jquery-modal-dialog.scss | 1 + inc/themes/material-blue/css/styles.css | 5 + inc/themes/material-blue/css/styles.css.map | 2 +- inc/themes/material-blue/css/styles.min.css | 2 +- inc/themes/material-blue/css/styles.scss | 8 + inc/themes/material-blue/js/app-theme.js | 96 +- inc/themes/material-blue/js/app-theme.min.js | 32 +- .../js/mdl-jquery-modal-dialog.js | 11 +- .../js/mdl-jquery-modal-dialog.min.js | 6 +- 21 files changed, 2209 insertions(+), 1438 deletions(-) diff --git a/inc/Exts/phpmailer/PHPMailer.php b/inc/Exts/phpmailer/PHPMailer.php index 80b3e672..a69dec0b 100644 --- a/inc/Exts/phpmailer/PHPMailer.php +++ b/inc/Exts/phpmailer/PHPMailer.php @@ -1,131 +1,114 @@ - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @author Brent R. Matzelle (original founder) - * @copyright 2013 Marcus Bointon + * PHP Version 5 + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - * @note This program is distributed in the hope that it will be useful - WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. */ namespace phpmailer; -if (version_compare(PHP_VERSION, '5.0.0', '<')) { - exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n"); -} +use Exception; /** * PHPMailer - PHP email creation and transport class. - * PHP Version 5.0.0 - * - * @package PHPMailer - * @author Marcus Bointon (coolbru) - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @author Brent R. Matzelle (original founder) - * @copyright 2013 Marcus Bointon - * @copyright 2010 - 2012 Jim Jagielski - * @copyright 2004 - 2009 Andy Prevost + * @package PHPMailer + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) */ class PHPMailer { /** * The PHPMailer Version number. - * - * @type string + * @var string */ - public $Version = '5.2.7'; + public $Version = '5.2.22'; /** * Email priority. - * Options: 1 = High, 3 = Normal, 5 = low. - * - * @type int + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * @var integer */ - public $Priority = 3; + public $Priority = null; /** * The character set of the message. - * - * @type string + * @var string */ public $CharSet = 'iso-8859-1'; /** * The MIME Content-type of the message. - * - * @type string + * @var string */ public $ContentType = 'text/plain'; /** * The message encoding. * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". - * - * @type string + * @var string */ public $Encoding = '8bit'; /** * Holds the most recent mailer error message. - * - * @type string + * @var string */ public $ErrorInfo = ''; /** * The From email address for the message. - * - * @type string + * @var string */ public $From = 'root@localhost'; /** * The From name of the message. - * - * @type string + * @var string */ public $FromName = 'Root User'; /** * The Sender email (Return-Path) of the message. * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. - * - * @type string + * @var string */ public $Sender = ''; /** * The Return-Path of the message. * If empty, it will be set to either From or Sender. - * - * @type string + * @var string + * @deprecated Email senders should never set a return-path header; + * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. + * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference */ public $ReturnPath = ''; /** * The Subject of the message. - * - * @type string + * @var string */ public $Subject = ''; /** * An HTML or plain text message body. * If HTML then call isHTML(true). - * - * @type string + * @var string */ public $Body = ''; @@ -134,8 +117,7 @@ class PHPMailer * This body can be read by mail clients that do not have HTML email * capability such as mutt & Eudora. * Clients that can read HTML will view the normal Body. - * - * @type string + * @var string */ public $AltBody = ''; @@ -143,106 +125,97 @@ class PHPMailer * An iCal message part body. * Only supported in simple alt or alt_inline message types * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator - * * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ * @link http://kigkonsult.se/iCalcreator/ - * @type string + * @var string */ public $Ical = ''; /** * The complete compiled MIME message body. - * * @access protected - * @type string + * @var string */ protected $MIMEBody = ''; /** * The complete compiled MIME message headers. - * - * @type string + * @var string * @access protected */ protected $MIMEHeader = ''; /** * Extra headers that createHeader() doesn't fold in. - * - * @type string + * @var string * @access protected */ protected $mailHeader = ''; /** * Word-wrap the message body to this number of chars. - * - * @type int + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * @var integer */ public $WordWrap = 0; /** * Which method to use to send mail. * Options: "mail", "sendmail", or "smtp". - * - * @type string + * @var string */ public $Mailer = 'mail'; /** * The path to the sendmail program. - * - * @type string + * @var string */ public $Sendmail = '/usr/sbin/sendmail'; /** * Whether mail() uses a fully sendmail-compatible MTA. * One which supports sendmail's "-oi -f" options. - * - * @type bool + * @var boolean */ public $UseSendmailOptions = true; /** * Path to PHPMailer plugins. * Useful if the SMTP class is not in the PHP include path. - * - * @type string + * @var string * @deprecated Should not be needed now there is an autoloader. */ public $PluginDir = ''; /** - * The email address that a reading confirmation should be sent to. - * - * @type string + * The email address that a reading confirmation should be sent to, also known as read receipt. + * @var string */ public $ConfirmReadingTo = ''; /** - * The hostname to use in Message-Id and Received headers - * and as default HELO string. - * If empty, the value returned - * by SERVER_NAME is used or 'localhost.localdomain'. - * - * @type string + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * @var string */ public $Hostname = ''; /** - * An ID to be used in the Message-Id header. + * An ID to be used in the Message-ID header. * If empty, a unique id will be generated. - * - * @type string + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * @var string */ public $MessageID = ''; /** * The message Date to be used in the Date header. * If empty, the current date will be added. - * - * @type string + * @var string */ public $MessageDate = ''; @@ -252,198 +225,215 @@ class PHPMailer * You can also specify a different port * for each host by using this format: [hostname:port] * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). * Hosts will be tried in order. - * - * @type string + * @var string */ public $Host = 'localhost'; /** * The default SMTP server port. - * - * @type int - * @Todo Why is this needed when the SMTP class takes care of it? + * @var integer + * @TODO Why is this needed when the SMTP class takes care of it? */ public $Port = 25; /** * The SMTP HELO of the message. - * Default is $Hostname. - * - * @type string + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * @var string * @see PHPMailer::$Hostname */ public $Helo = ''; /** - * The secure connection prefix. - * Options: "", "ssl" or "tls" - * - * @type string + * What kind of encryption to use on the SMTP connection. + * Options: '', 'ssl' or 'tls' + * @var string */ public $SMTPSecure = ''; + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * @var boolean + */ + public $SMTPAutoTLS = true; + /** * Whether to use SMTP authentication. * Uses the Username and Password properties. - * - * @type bool + * @var boolean * @see PHPMailer::$Username * @see PHPMailer::$Password */ public $SMTPAuth = false; + /** + * Options array passed to stream_context_create when connecting via SMTP. + * @var array + */ + public $SMTPOptions = array(); + /** * SMTP username. - * - * @type string + * @var string */ public $Username = ''; /** * SMTP password. - * - * @type string + * @var string */ public $Password = ''; /** * SMTP auth type. - * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5 - * - * @type string + * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified + * @var string */ public $AuthType = ''; /** * SMTP realm. * Used for NTLM auth - * - * @type string + * @var string */ public $Realm = ''; /** * SMTP workstation. * Used for NTLM auth - * - * @type string + * @var string */ public $Workstation = ''; /** * The SMTP server timeout in seconds. - * - * @type int + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * @var integer */ - public $Timeout = 10; + public $Timeout = 300; /** * SMTP class debug output mode. - * Options: 0 = off, 1 = commands, 2 = commands and data - * - * @type int + * Debug output level. + * Options: + * * `0` No output + * * `1` Commands + * * `2` Data and commands + * * `3` As 2 plus connection status + * * `4` Low-level data output + * @var integer * @see SMTP::$do_debug */ public $SMTPDebug = 0; /** - * The function/method to use for debugging output. - * Options: "echo" or "error_log" + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini * - * @type string + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * + * @var string|callable * @see SMTP::$Debugoutput */ - public $Debugoutput = "echo"; + public $Debugoutput = 'echo'; /** * Whether to keep SMTP connection open after each message. * If this is set to true then to close the connection * requires an explicit call to smtpClose(). - * - * @type bool + * @var boolean */ public $SMTPKeepAlive = false; /** * Whether to split multiple to addresses into multiple messages * or send them all in one message. - * - * @type bool + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * @var boolean */ public $SingleTo = false; /** * Storage for addresses when SingleTo is enabled. - * - * @type array - * @todo This should really not be public + * @var array + * @TODO This should really not be public */ public $SingleToArray = array(); /** * Whether to generate VERP addresses on send. * Only applicable when sending via SMTP. - * - * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path - * @type bool + * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @link http://www.postfix.org/VERP_README.html Postfix VERP info + * @var boolean */ public $do_verp = false; /** * Whether to allow sending messages with an empty body. - * - * @type bool + * @var boolean */ public $AllowEmpty = false; /** * The default line ending. - * - * @note The default remains "\n". We force CRLF where we know + * @note The default remains "\n". We force CRLF where we know * it must be used via self::CRLF. - * @type string + * @var string */ public $LE = "\n"; /** * DKIM selector. - * - * @type string + * @var string */ public $DKIM_selector = ''; /** * DKIM Identity. - * Usually the email address used as the source of the email - * - * @type string + * Usually the email address used as the source of the email. + * @var string */ public $DKIM_identity = ''; /** * DKIM passphrase. * Used if your key is encrypted. - * - * @type string + * @var string */ public $DKIM_passphrase = ''; /** * DKIM signing domain name. - * * @example 'example.com' - * @type string + * @var string */ public $DKIM_domain = ''; /** * DKIM private key file path. - * - * @type string + * @var string */ public $DKIM_private = ''; + /** + * DKIM private key string. + * If set, takes precedence over `$DKIM_private`. + * @var string + */ + public $DKIM_private_string = ''; + /** * Callback Action function name. * @@ -453,199 +443,226 @@ class PHPMailer * Value can be any php callable: http://www.php.net/is_callable * * Parameters: - * bool $result result of the send action + * boolean $result result of the send action * string $to email address of the recipient * string $cc cc email addresses * string $bcc bcc email addresses * string $subject the subject * string $body the email body * string $from email address of sender - * - * @type string + * @var string */ public $action_function = ''; /** - * What to use in the X-Mailer header. - * Options: null for default, whitespace for none, or a string to use - * - * @type string + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace for none, or a string to use + * @var string */ public $XMailer = ''; + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * @see PHPMailer::validateAddress() + * @var string|callable + * @static + */ + public static $validator = 'auto'; + /** * An instance of the SMTP sender class. - * - * @type SMTP + * @var SMTP * @access protected */ protected $smtp = null; /** - * The array of 'to' addresses. - * - * @type array + * The array of 'to' names and addresses. + * @var array * @access protected */ protected $to = array(); /** - * The array of 'cc' addresses. - * - * @type array + * The array of 'cc' names and addresses. + * @var array * @access protected */ protected $cc = array(); /** - * The array of 'bcc' addresses. - * - * @type array + * The array of 'bcc' names and addresses. + * @var array * @access protected */ protected $bcc = array(); /** * The array of reply-to names and addresses. - * - * @type array + * @var array * @access protected */ protected $ReplyTo = array(); /** * An array of all kinds of addresses. - * Includes all of $to, $cc, $bcc, $replyto - * - * @type array + * Includes all of $to, $cc, $bcc + * @var array * @access protected + * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc */ protected $all_recipients = array(); + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * @var array + * @access protected + * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + */ + protected $RecipientsQueue = array(); + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * @var array + * @access protected + * @see PHPMailer::$ReplyTo + */ + protected $ReplyToQueue = array(); + /** * The array of attachments. - * - * @type array + * @var array * @access protected */ protected $attachment = array(); /** * The array of custom headers. - * - * @type array + * @var array * @access protected */ protected $CustomHeader = array(); /** * The most recent Message-ID (including angular brackets). - * - * @type string + * @var string * @access protected */ protected $lastMessageID = ''; /** * The message's MIME type. - * - * @type string + * @var string * @access protected */ protected $message_type = ''; /** * The array of MIME boundary strings. - * - * @type array + * @var array * @access protected */ protected $boundary = array(); /** * The array of available languages. - * - * @type array + * @var array * @access protected */ protected $language = array(); /** * The number of errors encountered. - * - * @type integer + * @var integer * @access protected */ protected $error_count = 0; /** * The S/MIME certificate file path. - * - * @type string + * @var string * @access protected */ protected $sign_cert_file = ''; /** * The S/MIME key file path. - * - * @type string + * @var string * @access protected */ protected $sign_key_file = ''; + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * @var string + * @access protected + */ + protected $sign_extracerts_file = ''; + /** * The S/MIME password for the key. * Used only if the key is encrypted. - * - * @type string + * @var string * @access protected */ protected $sign_key_pass = ''; /** * Whether to throw exceptions for errors. - * - * @type bool + * @var boolean * @access protected */ protected $exceptions = false; /** - * Error severity: message only, continue processing + * Unique ID used for message ID and boundaries. + * @var string + * @access protected + */ + protected $uniqueid = ''; + + /** + * Error severity: message only, continue processing. */ const STOP_MESSAGE = 0; /** - * Error severity: message, likely ok to continue processing + * Error severity: message, likely ok to continue processing. */ const STOP_CONTINUE = 1; /** - * Error severity: message, plus full stop, critical error reached + * Error severity: message, plus full stop, critical error reached. */ const STOP_CRITICAL = 2; /** - * SMTP RFC standard line ending + * SMTP RFC standard line ending. */ const CRLF = "\r\n"; /** - * Constructor - * - * @param bool $exceptions Should we throw external exceptions? + * The maximum line length allowed by RFC 2822 section 2.1.1 + * @var integer */ - public function __construct($exceptions = false) + const MAX_LINE_LENGTH = 998; + + /** + * Constructor. + * @param boolean $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) { - $this->exceptions = ($exceptions == true); - //Make sure our autoloader is loaded -// if (version_compare(PHP_VERSION, '5.1.2', '>=')) { -// $al = spl_autoload_functions(); -// if ($al === false or !in_array('PHPMailerAutoload', $al)) { -// require 'PHPMailerAutoload.php'; -// } -// } + if ($exceptions !== null) { + $this->exceptions = (boolean)$exceptions; + } } /** @@ -653,9 +670,8 @@ class PHPMailer */ public function __destruct() { - if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely - $this->smtpClose(); - } + //Close any open SMTP connection nicely + $this->smtpClose(); } /** @@ -663,14 +679,13 @@ class PHPMailer * Also, unless sendmail_path points to sendmail (or something that * claims to be sendmail), don't pass params (not a perfect fix, * but it will do) - * - * @param string $to To + * @param string $to To * @param string $subject Subject - * @param string $body Message Body - * @param string $header Additional Header(s) - * @param string $params Params + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string $params Params * @access private - * @return bool + * @return boolean */ private function mailPassthru($to, $subject, $body, $header, $params) { @@ -680,50 +695,67 @@ class PHPMailer } else { $subject = $this->encodeHeader($this->secureHeader($subject)); } - if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { - $rt = @mail($to, $subject, $body, $header); - } else { - $rt = @mail($to, $subject, $body, $header, $params); - } - return $rt; - } + //Can't use additional_parameters in safe_mode, calling mail() with null params breaks + //@link http://php.net/manual/en/function.mail.php + if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { + $result = @mail($to, $subject, $body, $header); + } else { + $result = @mail($to, $subject, $body, $header, $params); + } + return $result; + } /** * Output debugging info via user-defined method. - * Only if debug output is enabled. - * + * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). * @see PHPMailer::$Debugoutput * @see PHPMailer::$SMTPDebug * @param string $str */ protected function edebug($str) { - if (!$this->SMTPDebug) { + if ($this->SMTPDebug <= 0) { + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); return; } switch ($this->Debugoutput) { case 'error_log': + //Don't output, just log error_log($str); break; case 'html': - //Cleans up output a bit for a better looking display that's HTML-safe - echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "
\n"; + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "
\n"; break; case 'echo': default: - echo $str . "\n"; + //Normalize line breaks + $str = preg_replace('/\r\n?/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( + "\n", + "\n \t ", + trim($str) + ) . "\n"; } } /** * Sets message type to HTML or plain. - * - * @param bool $ishtml True for HTML mode. + * @param boolean $isHtml True for HTML mode. * @return void */ - public function isHTML($ishtml = true) + public function isHTML($isHtml = true) { - if ($ishtml) { + if ($isHtml) { $this->ContentType = 'text/html'; } else { $this->ContentType = 'text/plain'; @@ -732,7 +764,6 @@ class PHPMailer /** * Send messages using SMTP. - * * @return void */ public function isSMTP() @@ -742,7 +773,6 @@ class PHPMailer /** * Send messages using PHP's mail() function. - * * @return void */ public function isMail() @@ -752,113 +782,160 @@ class PHPMailer /** * Send messages using $Sendmail. - * * @return void */ public function isSendmail() { - if (!stristr(ini_get('sendmail_path'), 'sendmail')) { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (!stristr($ini_sendmail_path, 'sendmail')) { $this->Sendmail = '/usr/sbin/sendmail'; + } else { + $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'sendmail'; } /** * Send messages using qmail. - * * @return void */ public function isQmail() { - if (!stristr(ini_get('sendmail_path'), 'qmail')) { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (!stristr($ini_sendmail_path, 'qmail')) { $this->Sendmail = '/var/qmail/bin/qmail-inject'; + } else { + $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'qmail'; } /** * Add a "To" address. - * - * @param string $address + * @param string $address The email address to send to * @param string $name - * @return bool true on success, false if address already used + * @return boolean true on success, false if address already used or invalid in some way */ public function addAddress($address, $name = '') { - return $this->addAnAddress('to', $address, $name); + return $this->addOrEnqueueAnAddress('to', $address, $name); } /** * Add a "CC" address. - * * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address + * @param string $address The email address to send to * @param string $name - * @return bool true on success, false if address already used + * @return boolean true on success, false if address already used or invalid in some way */ public function addCC($address, $name = '') { - return $this->addAnAddress('cc', $address, $name); + return $this->addOrEnqueueAnAddress('cc', $address, $name); } /** * Add a "BCC" address. - * * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address + * @param string $address The email address to send to * @param string $name - * @return bool true on success, false if address already used + * @return boolean true on success, false if address already used or invalid in some way */ public function addBCC($address, $name = '') { - return $this->addAnAddress('bcc', $address, $name); + return $this->addOrEnqueueAnAddress('bcc', $address, $name); } /** - * Add a "Reply-to" address. - * - * @param string $address + * Add a "Reply-To" address. + * @param string $address The email address to reply to * @param string $name - * @return bool + * @return boolean true on success, false if address already used or invalid in some way */ public function addReplyTo($address, $name = '') { - return $this->addAnAddress('Reply-To', $address, $name); + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); } /** - * Add an address to one of the recipient arrays. - * Addresses that have been added already return false, but do not throw exceptions - * - * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' - * @param string $address The email address to send to + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to * @param string $name * @throws phpmailerException - * @return bool true on success, false if address already used or invalid in some way + * @return boolean true on success, false if address already used or invalid in some way + * @access protected + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (($pos = strrpos($address, '@')) === false) { + // At-sign is misssing. + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + $params = array($kind, $address, $name); + // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { + if ($kind != 'Reply-To') { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + return true; + } + } else { + if (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + return true; + } + } + return false; + } + // Immediately add standard addresses without IDN. + return call_user_func_array(array($this, 'addAnAddress'), $params); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * @throws phpmailerException + * @return boolean true on success, false if address already used or invalid in some way * @access protected */ protected function addAnAddress($kind, $address, $name = '') { - if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { - $this->setError($this->lang('Invalid recipient array') . ': ' . $kind); - $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind); + if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { + $error_message = $this->lang('Invalid recipient kind: ') . $kind; + $this->setError($error_message); + $this->edebug($error_message); if ($this->exceptions) { - throw new phpmailerException('Invalid recipient array: ' . $kind); + throw new phpmailerException($error_message); } return false; } - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim if (!$this->validateAddress($address)) { - $this->setError($this->lang('invalid_address') . ': ' . $address); - $this->edebug($this->lang('invalid_address') . ': ' . $address); + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; + $this->setError($error_message); + $this->edebug($error_message); if ($this->exceptions) { - throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); + throw new phpmailerException($error_message); } return false; } if ($kind != 'Reply-To') { - if (!isset($this->all_recipients[strtolower($address)])) { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { array_push($this->$kind, array($address, $name)); $this->all_recipients[strtolower($address)] = true; return true; @@ -872,24 +949,82 @@ class PHPMailer return false; } + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
" into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * @param string $addrstr The address list string + * @param bool $useimap Whether to use the IMAP extension to parse the list + * @return array + * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + */ + public function parseAddresses($addrstr, $useimap = true) + { + $addresses = array(); + if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + foreach ($list as $address) { + if ($address->host != '.SYNTAX-ERROR.') { + if ($this->validateAddress($address->mailbox . '@' . $address->host)) { + $addresses[] = array( + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host + ); + } + } + } + } else { + //Use this simpler parser + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if ($this->validateAddress($address)) { + $addresses[] = array( + 'name' => '', + 'address' => $address + ); + } + } else { + list($name, $email) = explode('<', $address); + $email = trim(str_replace('>', '', $email)); + if ($this->validateAddress($email)) { + $addresses[] = array( + 'name' => trim(str_replace(array('"', "'"), '', $name)), + 'address' => $email + ); + } + } + } + } + return $addresses; + } + /** * Set the From and FromName properties. - * * @param string $address * @param string $name - * @param bool $auto Whether to also set the Sender address, defaults to true + * @param boolean $auto Whether to also set the Sender address, defaults to true * @throws phpmailerException - * @return bool + * @return boolean */ public function setFrom($address, $name = '', $auto = true) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!$this->validateAddress($address)) { - $this->setError($this->lang('invalid_address') . ': ' . $address); - $this->edebug($this->lang('invalid_address') . ': ' . $address); + // Don't validate now addresses with IDN. Will be done in send(). + if (($pos = strrpos($address, '@')) === false or + (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and + !$this->validateAddress($address)) { + $error_message = $this->lang('invalid_address') . " (setFrom) $address"; + $this->setError($error_message); + $this->edebug($error_message); if ($this->exceptions) { - throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); + throw new phpmailerException($error_message); } return false; } @@ -908,7 +1043,6 @@ class PHPMailer * Technically this is the value from the last time the headers were created, * but it's also the message ID of the last sent message except in * pathological cases. - * * @return string */ public function getLastMessageID() @@ -918,30 +1052,48 @@ class PHPMailer /** * Check that a string looks like an email address. - * - * @param string $address The email address to check - * @param string $patternselect A selector for the validation pattern to use : - * 'auto' - pick best one automatically; - * 'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; - * 'pcre' - use old PCRE implementation; - * 'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough; - * 'noregex' - super fast, really dumb. - * @return bool + * @param string $address The email address to check + * @param string|callable $patternselect A selector for the validation pattern to use : + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * @return boolean * @static * @access public */ - public static function validateAddress($address, $patternselect = 'auto') + public static function validateAddress($address, $patternselect = null) { - if ($patternselect == 'auto') { - if (defined( - 'PCRE_VERSION' - ) - ) { //Check this instead of extension_loaded so it works when that function is disabled - if (version_compare(PCRE_VERSION, '8.0') >= 0) { + if (is_null($patternselect)) { + $patternselect = self::$validator; + } + if (is_callable($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { + return false; + } + if (!$patternselect or $patternselect == 'auto') { + //Check this constant first so it works when extension_loaded() is disabled by safe mode + //Constant was added in PHP 5.2.4 + if (defined('PCRE_VERSION')) { + //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 + if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { $patternselect = 'pcre8'; } else { $patternselect = 'pcre'; } + } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { + //Fall back to older PCRE + $patternselect = 'pcre'; } else { //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension if (version_compare(PHP_VERSION, '5.2.0') >= 0) { @@ -954,15 +1106,12 @@ class PHPMailer switch ($patternselect) { case 'pcre8': /** - * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is - * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to - * not allow a@b type valid addresses :( - * - * @link http://squiloople.com/2009/12/20/email-address-validation/ + * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. + * @link http://squiloople.com/2009/12/20/email-address-validation/ * @copyright 2009-2010 Michael Rushton - * Feel free to use and redistribute this code. But please keep this copyright notice. + * Feel free to use and redistribute this code. But please keep this copyright notice. */ - return (bool)preg_match( + return (boolean)preg_match( '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . @@ -974,10 +1123,9 @@ class PHPMailer '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address ); - break; case 'pcre': //An older regex that doesn't need a recent PCRE - return (bool)preg_match( + return (boolean)preg_match( '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . @@ -990,27 +1138,75 @@ class PHPMailer '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address ); - break; - case 'php': - default: - return (bool)filter_var($address, FILTER_VALIDATE_EMAIL); - break; + case 'html5': + /** + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) + */ + return (boolean)preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); case 'noregex': //No PCRE! Do something _very_ approximate! //Check the address is 3 chars or longer and contains an @ that's not the first or last char return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1); - break; + case 'php': + default: + return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); } } + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * "intl" and "mbstring" PHP extensions. + * @return bool "true" if required functions for IDN support are present + */ + public function idnSupported() + { + // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. + return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain has characters not allowed in an IDN) + * @see PHPMailer::$CharSet + * @param string $address The email address to convert + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + // Verify we have required functions, CharSet, and at-sign. + if ($this->idnSupported() and + !empty($this->CharSet) and + ($pos = strrpos($address, '@')) !== false) { + $domain = substr($address, ++$pos); + // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { + $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); + if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? + idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : + idn_to_ascii($domain)) !== false) { + return substr($address, 0, $pos) . $punycode; + } + } + } + return $address; + } + /** * Create a message and send it. * Uses the sending method specified by $Mailer. - * * @throws phpmailerException - * @return bool false on error - See the ErrorInfo property for details of the error. + * @return boolean false on error - See the ErrorInfo property for details of the error. */ public function send() { @@ -1019,11 +1215,11 @@ class PHPMailer return false; } return $this->postSend(); - } catch (phpmailerException $e) { + } catch (phpmailerException $exc) { $this->mailHeader = ''; - $this->setError($e->getMessage()); + $this->setError($exc->getMessage()); if ($this->exceptions) { - throw $e; + throw $exc; } return false; } @@ -1031,40 +1227,68 @@ class PHPMailer /** * Prepare a message for sending. - * * @throws phpmailerException - * @return bool + * @return boolean */ public function preSend() { try { - $this->mailHeader = ""; + $this->error_count = 0; // Reset errors + $this->mailHeader = ''; + + // Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + $params[1] = $this->punyencodeAddress($params[1]); + call_user_func_array(array($this, 'addAnAddress'), $params); + } if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); } + // Validate From, Sender, and ConfirmReadingTo addresses + foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { + $this->$address_kind = trim($this->$address_kind); + if (empty($this->$address_kind)) { + continue; + } + $this->$address_kind = $this->punyencodeAddress($this->$address_kind); + if (!$this->validateAddress($this->$address_kind)) { + $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + } + // Set whether the message is multipart/alternative - if (!empty($this->AltBody)) { + if ($this->alternativeExists()) { $this->ContentType = 'multipart/alternative'; } - $this->error_count = 0; // reset errors $this->setMessageType(); // Refuse to send an empty message unless we are specifically allowing it if (!$this->AllowEmpty and empty($this->Body)) { throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); } - $this->MIMEHeader = $this->createHeader(); + // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + $this->MIMEHeader = ''; $this->MIMEBody = $this->createBody(); + // createBody may have added some headers, so retain them + $tempheaders = $this->MIMEHeader; + $this->MIMEHeader = $this->createHeader(); + $this->MIMEHeader .= $tempheaders; // To capture the complete message when using mail(), create // an extra header list which createHeader() doesn't fold in if ($this->Mailer == 'mail') { if (count($this->to) > 0) { - $this->mailHeader .= $this->addrAppend("To", $this->to); + $this->mailHeader .= $this->addrAppend('To', $this->to); } else { - $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;"); + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); } $this->mailHeader .= $this->headerLine( 'Subject', @@ -1074,10 +1298,10 @@ class PHPMailer // Sign with DKIM if enabled if (!empty($this->DKIM_domain) - && !empty($this->DKIM_private) && !empty($this->DKIM_selector) - && !empty($this->DKIM_domain) - && file_exists($this->DKIM_private) + && (!empty($this->DKIM_private_string) + || (!empty($this->DKIM_private) && file_exists($this->DKIM_private)) + ) ) { $header_dkim = $this->DKIM_Add( $this->MIMEHeader . $this->mailHeader, @@ -1088,11 +1312,10 @@ class PHPMailer str_replace("\r\n", "\n", $header_dkim) . self::CRLF; } return true; - - } catch (phpmailerException $e) { - $this->setError($e->getMessage()); + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); if ($this->exceptions) { - throw $e; + throw $exc; } return false; } @@ -1101,9 +1324,8 @@ class PHPMailer /** * Actually send a message. * Send the email via the selected mechanism - * * @throws phpmailerException - * @return bool + * @return boolean */ public function postSend() { @@ -1118,18 +1340,18 @@ class PHPMailer case 'mail': return $this->mailSend($this->MIMEHeader, $this->MIMEBody); default: - if (method_exists($this, $this->Mailer . 'Send')) { - $sendMethod = $this->Mailer . 'Send'; + $sendMethod = $this->Mailer.'Send'; + if (method_exists($this, $sendMethod)) { return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); - } else { - return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } + + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } - } catch (phpmailerException $e) { - $this->setError($e->getMessage()); - $this->edebug($e->getMessage()); + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); if ($this->exceptions) { - throw $e; + throw $exc; } } return false; @@ -1137,41 +1359,51 @@ class PHPMailer /** * Send mail using the $Sendmail program. - * * @param string $header The message headers - * @param string $body The message body - * @see PHPMailer::$Sendmail + * @param string $body The message body + * @see PHPMailer::$Sendmail * @throws phpmailerException * @access protected - * @return bool + * @return boolean */ protected function sendmailSend($header, $body) { - if ($this->Sender != '') { + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) and self::isShellSafe($this->Sender)) { if ($this->Mailer == 'qmail') { - $sendmail = sprintf("%s -f%s", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + $sendmailFmt = '%s -f%s'; } else { - $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + $sendmailFmt = '%s -oi -f%s -t'; } } else { if ($this->Mailer == 'qmail') { - $sendmail = sprintf("%s", escapeshellcmd($this->Sendmail)); + $sendmailFmt = '%s'; } else { - $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); + $sendmailFmt = '%s -oi -t'; } } - if ($this->SingleTo === true) { - foreach ($this->SingleToArray as $val) { + + // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing. + $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { if (!@$mail = popen($sendmail, 'w')) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } - fputs($mail, "To: " . $val . "\n"); + fputs($mail, 'To: ' . $toAddr . "\n"); fputs($mail, $header); fputs($mail, $body); $result = pclose($mail); - // implement call back function if it exists - $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + $this->doCallback( + ($result == 0), + array($toAddr), + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From + ); if ($result != 0) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -1183,9 +1415,15 @@ class PHPMailer fputs($mail, $header); fputs($mail, $body); $result = pclose($mail); - // implement call back function if it exists - $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + $this->doCallback( + ($result == 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From + ); if ($result != 0) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -1194,50 +1432,82 @@ class PHPMailer } /** - * Send mail using the PHP mail() function. + * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. * + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * @param string $string The string to be validated + * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report + * @access protected + * @return boolean + */ + protected static function isShellSafe($string) + { + // Future-proof + if (escapeshellcmd($string) !== $string + or !in_array(escapeshellarg($string), array("'$string'", "\"$string\"")) + ) { + return false; + } + + $length = strlen($string); + + for ($i = 0; $i < $length; $i++) { + $c = $string[$i]; + + // All other characters have a special meaning in at least one common shell, including = and +. + // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + // Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Send mail using the PHP mail() function. * @param string $header The message headers - * @param string $body The message body - * @link http://www.php.net/manual/en/book.mail.php + * @param string $body The message body + * @link http://www.php.net/manual/en/book.mail.php * @throws phpmailerException * @access protected - * @return bool + * @return boolean */ protected function mailSend($header, $body) { $toArr = array(); - foreach ($this->to as $t) { - $toArr[] = $this->addrFormat($t); + foreach ($this->to as $toaddr) { + $toArr[] = $this->addrFormat($toaddr); } $to = implode(', ', $toArr); - if (empty($this->Sender)) { - $params = " "; - } else { - $params = sprintf("-f%s", $this->Sender); + $params = null; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (self::isShellSafe($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } } - if ($this->Sender != '' and !ini_get('safe_mode')) { + if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); } - $rt = false; - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $val) { - $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + $result = false; + if ($this->SingleTo and count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); } } else { - $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); } if (isset($old_from)) { ini_set('sendmail_from', $old_from); } - if (!$rt) { + if (!$result) { throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); } return true; @@ -1246,7 +1516,6 @@ class PHPMailer /** * Get an instance to use for SMTP operations. * Override this function to load your own SMTP implementation - * * @return SMTP */ public function getSMTPInstance() @@ -1261,70 +1530,62 @@ class PHPMailer * Send mail via SMTP. * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. * Uses the PHPMailerSMTP class by default. - * - * @see PHPMailer::getSMTPInstance() to use a different class. + * @see PHPMailer::getSMTPInstance() to use a different class. * @param string $header The message headers - * @param string $body The message body + * @param string $body The message body * @throws phpmailerException - * @uses SMTP + * @uses SMTP * @access protected - * @return bool + * @return boolean */ protected function smtpSend($header, $body) { $bad_rcpt = array(); - - if (!$this->smtpConnect()) { + if (!$this->smtpConnect($this->SMTPOptions)) { throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); } - $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; + if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { + $smtp_from = $this->Sender; + } else { + $smtp_from = $this->From; + } if (!$this->smtp->mail($smtp_from)) { $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); } // Attempt to send to all recipients - foreach ($this->to as $to) { - if (!$this->smtp->recipient($to[0])) { - $bad_rcpt[] = $to[0]; - $isSent = 0; - } else { - $isSent = 1; + foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0])) { + $error = $this->smtp->getError(); + $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); + $isSent = false; + } else { + $isSent = true; + } + $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); } - $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From); - } - foreach ($this->cc as $cc) { - if (!$this->smtp->recipient($cc[0])) { - $bad_rcpt[] = $cc[0]; - $isSent = 0; - } else { - $isSent = 1; - } - $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From); - } - foreach ($this->bcc as $bcc) { - if (!$this->smtp->recipient($bcc[0])) { - $bad_rcpt[] = $bcc[0]; - $isSent = 0; - } else { - $isSent = 1; - } - $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From); } - //Only send the DATA command if we have viable recipients + // Only send the DATA command if we have viable recipients if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); } - if ($this->SMTPKeepAlive == true) { + if ($this->SMTPKeepAlive) { $this->smtp->reset(); } else { $this->smtp->quit(); $this->smtp->close(); } - if (count($bad_rcpt) > 0) { //Create error message for any bad addresses + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } throw new phpmailerException( - $this->lang('recipients_failed') . implode(', ', $bad_rcpt), + $this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE ); } @@ -1334,20 +1595,24 @@ class PHPMailer /** * Initiate a connection to an SMTP server. * Returns false if the operation failed. - * * @param array $options An array of options compatible with stream_context_create() - * @uses SMTP + * @uses SMTP * @access public * @throws phpmailerException - * @return bool + * @return boolean */ - public function smtpConnect($options = array()) + public function smtpConnect($options = null) { if (is_null($this->smtp)) { $this->smtp = $this->getSMTPInstance(); } - //Already connected? + //If no options are provided, use whatever is set in the instance + if (is_null($options)) { + $options = $this->SMTPOptions; + } + + // Already connected? if ($this->smtp->connected()) { return true; } @@ -1362,22 +1627,33 @@ class PHPMailer foreach ($hosts as $hostentry) { $hostinfo = array(); if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { - //Not a valid host entry + // Not a valid host entry continue; } - //$hostinfo[2]: optional ssl or tls prefix - //$hostinfo[3]: the hostname - //$hostinfo[4]: optional port number - //The host string prefix can temporarily override the current setting for SMTPSecure - //If it's not specified, the default value is used + // $hostinfo[2]: optional ssl or tls prefix + // $hostinfo[3]: the hostname + // $hostinfo[4]: optional port number + // The host string prefix can temporarily override the current setting for SMTPSecure + // If it's not specified, the default value is used $prefix = ''; + $secure = $this->SMTPSecure; $tls = ($this->SMTPSecure == 'tls'); - if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) { + if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { $prefix = 'ssl://'; - $tls = false; //Can't have SSL and TLS at once + $tls = false; // Can't have SSL and TLS at the same time + $secure = 'ssl'; } elseif ($hostinfo[2] == 'tls') { $tls = true; - //tls doesn't use a prefix + // tls doesn't use a prefix + $secure = 'tls'; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA1'); + if ('tls' === $secure or 'ssl' === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); + } } $host = $hostinfo[3]; $port = $this->Port; @@ -1393,12 +1669,19 @@ class PHPMailer $hello = $this->serverHostname(); } $this->smtp->hello($hello); - + //Automatically enable TLS encryption if: + // * it's not disabled + // * we have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } if ($tls) { if (!$this->smtp->startTLS()) { throw new phpmailerException($this->lang('connect_host')); } - //We must resend HELO after tls negotiation + // We must resend EHLO after TLS negotiation $this->smtp->hello($hello); } if ($this->SMTPAuth) { @@ -1414,16 +1697,17 @@ class PHPMailer } } return true; - } catch (phpmailerException $e) { - $lastexception = $e; - //We must have connected, but then failed TLS or Auth, so close connection nicely + } catch (phpmailerException $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + // We must have connected, but then failed TLS or Auth, so close connection nicely $this->smtp->quit(); } } } - //If we get here, all connection attempts have failed, so close connection hard + // If we get here, all connection attempts have failed, so close connection hard $this->smtp->close(); - //As we've caught all exceptions, just report whatever the last one was + // As we've caught all exceptions, just report whatever the last one was if ($this->exceptions and !is_null($lastexception)) { throw $lastexception; } @@ -1432,12 +1716,11 @@ class PHPMailer /** * Close the active SMTP session if one exists. - * * @return void */ public function smtpClose() { - if ($this->smtp !== null) { + if (is_a($this->smtp, 'SMTP')) { if ($this->smtp->connected()) { $this->smtp->quit(); $this->smtp->close(); @@ -1449,15 +1732,27 @@ class PHPMailer * Set the language for error messages. * Returns false if it cannot load the language file. * The default language is English. - * - * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") * @param string $lang_path Path to the language file directory, with trailing separator (slash) - * @return bool + * @return boolean * @access public */ - public function setLanguage($langcode = 'en', $lang_path = 'language/') + public function setLanguage($langcode = 'en', $lang_path = '') { - //Define full set of translatable strings + // Backwards compatibility for renamed language codes + $renamed_langcodes = array( + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + ); + + if (isset($renamed_langcodes[$langcode])) { + $langcode = $renamed_langcodes[$langcode]; + } + + // Define full set of translatable strings in English $PHPMAILER_LANG = array( 'authenticate' => 'SMTP Error: Could not authenticate.', 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', @@ -1469,34 +1764,43 @@ class PHPMailer 'file_open' => 'File Error: Could not open file: ', 'from_failed' => 'The following From address failed: ', 'instantiate' => 'Could not instantiate mail function.', - 'invalid_address' => 'Invalid address', + 'invalid_address' => 'Invalid address: ', 'mailer_not_supported' => ' mailer is not supported.', 'provide_address' => 'You must provide at least one recipient email address.', 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 'signing' => 'Signing Error: ', 'smtp_connect_failed' => 'SMTP connect() failed.', 'smtp_error' => 'SMTP server error: ', - 'variable_set' => 'Cannot set or reset variable: ' + 'variable_set' => 'Cannot set or reset variable: ', + 'extension_missing' => 'Extension missing: ' ); - //Overwrite language-specific strings. - //This way we'll never have missing translations - no more "language string failed to load"! - $l = true; + if (empty($lang_path)) { + // Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; + } + //Validate $langcode + if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { + $langcode = 'en'; + } + $foundlang = true; $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; - if ($langcode != 'en') { //There is no English translation file - //Make sure language file path is readable + // There is no English translation file + if ($langcode != 'en') { + // Make sure language file path is readable if (!is_readable($lang_file)) { - $l = false; + $foundlang = false; } else { - $l = include $lang_file; + // Overwrite language-specific strings. + // This way we'll never have missing translation keys. + $foundlang = include $lang_file; } } $this->language = $PHPMAILER_LANG; - return ($l == true); //Returns false if language not found + return (boolean)$foundlang; // Returns false if language not found } /** * Get the array of strings for the current language. - * * @return array */ public function getTranslations() @@ -1506,30 +1810,28 @@ class PHPMailer /** * Create recipient headers. - * * @access public * @param string $type * @param array $addr An array of recipient, - * where each recipient is a 2-element indexed array with element 0 containing an address - * and element 1 containing a name, like: - * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) + * where each recipient is a 2-element indexed array with element 0 containing an address + * and element 1 containing a name, like: + * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) * @return string */ public function addrAppend($type, $addr) { $addresses = array(); - foreach ($addr as $a) { - $addresses[] = $this->addrFormat($a); + foreach ($addr as $address) { + $addresses[] = $this->addrFormat($address); } return $type . ': ' . implode(', ', $addresses) . $this->LE; } /** * Format an address for use in a message header. - * * @access public * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name - * like array('joe@example.com', 'Joe User') + * like array('joe@example.com', 'Joe User') * @return string */ public function addrFormat($addr) @@ -1537,9 +1839,9 @@ class PHPMailer if (empty($addr[1])) { // No name provided return $this->secureHeader($addr[0]); } else { - return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader( + return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( $addr[0] - ) . ">"; + ) . '>'; } } @@ -1548,50 +1850,56 @@ class PHPMailer * For use with mailers that do not automatically perform wrapping * and for quoted-printable encoded messages. * Original written by philippe. - * * @param string $message The message to wrap * @param integer $length The line length to wrap to - * @param bool $qp_mode Whether to run in Quoted-Printable mode + * @param boolean $qp_mode Whether to run in Quoted-Printable mode * @access public * @return string */ public function wrapText($message, $length, $qp_mode = false) { - $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; + if ($qp_mode) { + $soft_break = sprintf(' =%s', $this->LE); + } else { + $soft_break = $this->LE; + } // If utf-8 encoding is used, we will need to make sure we don't // split multibyte characters when we wrap - $is_utf8 = (strtolower($this->CharSet) == "utf-8"); + $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); $lelen = strlen($this->LE); $crlflen = strlen(self::CRLF); $message = $this->fixEOL($message); + //Remove a trailing line break if (substr($message, -$lelen) == $this->LE) { $message = substr($message, 0, -$lelen); } - $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE + //Split message into lines + $lines = explode($this->LE, $message); + //Message will be rebuilt in here $message = ''; - for ($i = 0; $i < count($line); $i++) { - $line_part = explode(' ', $line[$i]); + foreach ($lines as $line) { + $words = explode(' ', $line); $buf = ''; - for ($e = 0; $e < count($line_part); $e++) { - $word = $line_part[$e]; + $firstword = true; + foreach ($words as $word) { if ($qp_mode and (strlen($word) > $length)) { $space_left = $length - strlen($buf) - $crlflen; - if ($e != 0) { + if (!$firstword) { if ($space_left > 20) { $len = $space_left; if ($is_utf8) { $len = $this->utf8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == "=") { + } elseif (substr($word, $len - 1, 1) == '=') { $len--; - } elseif (substr($word, $len - 2, 1) == "=") { + } elseif (substr($word, $len - 2, 1) == '=') { $len -= 2; } $part = substr($word, 0, $len); $word = substr($word, $len); $buf .= ' ' . $part; - $message .= $buf . sprintf("=%s", self::CRLF); + $message .= $buf . sprintf('=%s', self::CRLF); } else { $message .= $buf . $soft_break; } @@ -1604,29 +1912,33 @@ class PHPMailer $len = $length; if ($is_utf8) { $len = $this->utf8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == "=") { + } elseif (substr($word, $len - 1, 1) == '=') { $len--; - } elseif (substr($word, $len - 2, 1) == "=") { + } elseif (substr($word, $len - 2, 1) == '=') { $len -= 2; } $part = substr($word, 0, $len); $word = substr($word, $len); if (strlen($word) > 0) { - $message .= $part . sprintf("=%s", self::CRLF); + $message .= $part . sprintf('=%s', self::CRLF); } else { $buf = $part; } } } else { $buf_o = $buf; - $buf .= ($e == 0) ? $word : (' ' . $word); + if (!$firstword) { + $buf .= ' '; + } + $buf .= $word; if (strlen($buf) > $length and $buf_o != '') { $message .= $buf_o . $soft_break; $buf = $word; } } + $firstword = false; } $message .= $buf . self::CRLF; } @@ -1636,13 +1948,12 @@ class PHPMailer /** * Find the last character boundary prior to $maxLength in a utf-8 - * quoted (printable) encoded string. + * quoted-printable encoded string. * Original written by Colin Brown. - * * @access public * @param string $encodedText utf-8 QP text - * @param int $maxLength find last character boundary prior to this length - * @return int + * @param integer $maxLength Find the last character boundary prior to this length + * @return integer */ public function utf8CharBoundary($encodedText, $maxLength) { @@ -1650,23 +1961,27 @@ class PHPMailer $lookBack = 3; while (!$foundSplitPos) { $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); - $encodedCharPos = strpos($lastChunk, "="); - if ($encodedCharPos !== false) { + $encodedCharPos = strpos($lastChunk, '='); + if (false !== $encodedCharPos) { // Found start of encoded character byte within $lookBack block. // Check the encoded byte value (the 2 chars after the '=') $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); $dec = hexdec($hex); - if ($dec < 128) { // Single byte character. + if ($dec < 128) { + // Single byte character. // If the encoded char was found at pos 0, it will fit // otherwise reduce maxLength to start of the encoded char - $maxLength = ($encodedCharPos == 0) ? $maxLength : - $maxLength - ($lookBack - $encodedCharPos); + if ($encodedCharPos > 0) { + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + } $foundSplitPos = true; - } elseif ($dec >= 192) { // First byte of a multi byte character + } elseif ($dec >= 192) { + // First byte of a multi byte character // Reduce maxLength to split at start of character $maxLength = $maxLength - ($lookBack - $encodedCharPos); $foundSplitPos = true; - } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back + } elseif ($dec < 192) { + // Middle byte of a multi byte character, look further back $lookBack += 3; } } else { @@ -1677,10 +1992,11 @@ class PHPMailer return $maxLength; } - /** - * Set the body wrapping. - * + * Apply word wrapping to the message body. + * Wraps the message body to the number of chars set in the WordWrap property. + * You should only do this to plain-text bodies as wrapping HTML tags may break them. + * This is called automatically by createBody(), so you don't need to call it yourself. * @access public * @return void */ @@ -1705,7 +2021,6 @@ class PHPMailer /** * Assemble message headers. - * * @access public * @return string The assembled headers */ @@ -1713,38 +2028,25 @@ class PHPMailer { $result = ''; - // Set the boundaries - $uniq_id = md5(uniqid(time())); - $this->boundary[1] = 'b1_' . $uniq_id; - $this->boundary[2] = 'b2_' . $uniq_id; - $this->boundary[3] = 'b3_' . $uniq_id; - if ($this->MessageDate == '') { - $result .= $this->headerLine('Date', self::rfcDate()); - } else { - $result .= $this->headerLine('Date', $this->MessageDate); - } - - if ($this->ReturnPath) { - $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>'); - } elseif ($this->Sender == '') { - $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>'); - } else { - $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>'); + $this->MessageDate = self::rfcDate(); } + $result .= $this->headerLine('Date', $this->MessageDate); // To be created automatically by mail() - if ($this->Mailer != 'mail') { - if ($this->SingleTo === true) { - foreach ($this->to as $t) { - $this->SingleToArray[] = $this->addrFormat($t); + if ($this->SingleTo) { + if ($this->Mailer != 'mail') { + foreach ($this->to as $toaddr) { + $this->SingleToArray[] = $this->addrFormat($toaddr); } - } else { - if (count($this->to) > 0) { + } + } else { + if (count($this->to) > 0) { + if ($this->Mailer != 'mail') { $result .= $this->addrAppend('To', $this->to); - } elseif (count($this->cc) == 0) { - $result .= $this->headerLine('To', 'undisclosed-recipients:;'); } + } elseif (count($this->cc) == 0) { + $result .= $this->headerLine('To', 'undisclosed-recipients:;'); } } @@ -1773,17 +2075,21 @@ class PHPMailer $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } - if ($this->MessageID != '') { + // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 + // https://tools.ietf.org/html/rfc5322#section-3.6.4 + if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { $this->lastMessageID = $this->MessageID; } else { - $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname()); + $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); + } + $result .= $this->headerLine('Message-ID', $this->lastMessageID); + if (!is_null($this->Priority)) { + $result .= $this->headerLine('X-Priority', $this->Priority); } - $result .= $this->HeaderLine('Message-ID', $this->lastMessageID); - $result .= $this->headerLine('X-Priority', $this->Priority); if ($this->XMailer == '') { $result .= $this->headerLine( 'X-Mailer', - 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)' + 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' ); } else { $myXmailer = trim($this->XMailer); @@ -1793,14 +2099,14 @@ class PHPMailer } if ($this->ConfirmReadingTo != '') { - $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); + $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); } // Add custom headers - for ($index = 0; $index < count($this->CustomHeader); $index++) { + foreach ($this->CustomHeader as $header) { $result .= $this->headerLine( - trim($this->CustomHeader[$index][0]), - $this->encodeHeader(trim($this->CustomHeader[$index][1])) + trim($header[0]), + $this->encodeHeader(trim($header[1])) ); } if (!$this->sign_key_file) { @@ -1813,13 +2119,13 @@ class PHPMailer /** * Get the message MIME type headers. - * * @access public * @return string */ public function getMailMIME() { $result = ''; + $ismultipart = true; switch ($this->message_type) { case 'inline': $result .= $this->headerLine('Content-Type', 'multipart/related;'); @@ -1840,11 +2146,20 @@ class PHPMailer default: // Catches case 'plain': and case '': $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); + $ismultipart = false; break; } - //RFC1341 part 5 says 7bit is assumed if not specified + // RFC1341 part 5 says 7bit is assumed if not specified if ($this->Encoding != '7bit') { - $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE + if ($ismultipart) { + if ($this->Encoding == '8bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); + } + // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible + } else { + $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + } } if ($this->Mailer != 'mail') { @@ -1857,22 +2172,27 @@ class PHPMailer /** * Returns the whole MIME message. * Includes complete headers and body. - * Only valid post PreSend(). - * - * @see PHPMailer::PreSend() + * Only valid post preSend(). + * @see PHPMailer::preSend() * @access public * @return string */ public function getSentMIMEMessage() { - return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; + return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; } + /** + * Create unique ID + * @return string + */ + protected function generateId() { + return md5(uniqid(time())); + } /** * Assemble the message body. * Returns an empty string on failure. - * * @access public * @throws phpmailerException * @return string The assembled message body @@ -1880,6 +2200,11 @@ class PHPMailer public function createBody() { $body = ''; + //Create unique IDs and preset boundaries + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1_' . $this->uniqueid; + $this->boundary[2] = 'b2_' . $this->uniqueid; + $this->boundary[3] = 'b3_' . $this->uniqueid; if ($this->sign_key_file) { $body .= $this->getMailMIME() . $this->LE; @@ -1887,37 +2212,70 @@ class PHPMailer $this->setWordWrap(); + $bodyEncoding = $this->Encoding; + $bodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { + $bodyEncoding = '7bit'; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $bodyCharSet = 'us-ascii'; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the body part only + if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { + $bodyEncoding = 'quoted-printable'; + } + + $altBodyEncoding = $this->Encoding; + $altBodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { + $altBodyEncoding = '7bit'; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $altBodyCharSet = 'us-ascii'; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the alt body part only + if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { + $altBodyEncoding = 'quoted-printable'; + } + //Use this as a preamble in all multipart message types + $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; switch ($this->message_type) { case 'inline': - $body .= $this->getBoundary($this->boundary[1], '', '', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[1]); break; case 'attach': - $body .= $this->getBoundary($this->boundary[1], '', '', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; case 'inline_attach': + $body .= $mimepre; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/related;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], '', '', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[2]); $body .= $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; case 'alt': - $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); - $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; - $body .= $this->getBoundary($this->boundary[1], '', 'text/html', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; if (!empty($this->Ical)) { $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); @@ -1927,49 +2285,52 @@ class PHPMailer $body .= $this->endBoundary($this->boundary[1]); break; case 'alt_inline': - $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); - $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/related;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], '', 'text/html', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[2]); $body .= $this->LE; $body .= $this->endBoundary($this->boundary[1]); break; case 'alt_attach': + $body .= $mimepre; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', ''); - $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; - $body .= $this->getBoundary($this->boundary[2], '', 'text/html', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->endBoundary($this->boundary[2]); $body .= $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; case 'alt_inline_attach': + $body .= $mimepre; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', ''); - $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->textLine('--' . $this->boundary[2]); $body .= $this->headerLine('Content-Type', 'multipart/related;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[3], '', 'text/html', ''); - $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[3]); $body .= $this->LE; @@ -1978,7 +2339,9 @@ class PHPMailer $body .= $this->attachAll('attachment', $this->boundary[1]); break; default: - // catch case 'plain' and case '' + // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types + //Reset the `Encoding` property in case we changed it for line length reasons + $this->Encoding = $bodyEncoding; $body .= $this->encodeString($this->Body, $this->Encoding); break; } @@ -1988,32 +2351,51 @@ class PHPMailer } elseif ($this->sign_key_file) { try { if (!defined('PKCS7_TEXT')) { - throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.'); + throw new phpmailerException($this->lang('extension_missing') . 'openssl'); } - //TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 + // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 $file = tempnam(sys_get_temp_dir(), 'mail'); - file_put_contents($file, $body); //TODO check this worked + if (false === file_put_contents($file, $body)) { + throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); + } $signed = tempnam(sys_get_temp_dir(), 'signed'); - if (@openssl_pkcs7_sign( - $file, - $signed, - 'file://' . realpath($this->sign_cert_file), - array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), - null - ) - ) { + //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 + if (empty($this->sign_extracerts_file)) { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null + ); + } else { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null, + PKCS7_DETACHED, + $this->sign_extracerts_file + ); + } + if ($sign) { @unlink($file); $body = file_get_contents($signed); @unlink($signed); + //The message returned by openssl contains both headers and body, so need to split them up + $parts = explode("\n\n", $body, 2); + $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; + $body = $parts[1]; } else { @unlink($file); @unlink($signed); throw new phpmailerException($this->lang('signing') . openssl_error_string()); } - } catch (phpmailerException $e) { + } catch (phpmailerException $exc) { $body = ''; if ($this->exceptions) { - throw $e; + throw $exc; } } } @@ -2022,7 +2404,6 @@ class PHPMailer /** * Return the start of a message boundary. - * * @access protected * @param string $boundary * @param string $charSet @@ -2043,9 +2424,12 @@ class PHPMailer $encoding = $this->Encoding; } $result .= $this->textLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); + $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); $result .= $this->LE; - $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + // RFC1341 part 5 says 7bit is assumed if not specified + if ($encoding != '7bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + } $result .= $this->LE; return $result; @@ -2053,7 +2437,6 @@ class PHPMailer /** * Return the end of a message boundary. - * * @access protected * @param string $boundary * @return string @@ -2065,33 +2448,31 @@ class PHPMailer /** * Set the message type. - * PHPMailer only supports some preset message types, - * not arbitrary MIME structures. - * + * PHPMailer only supports some preset message types, not arbitrary MIME structures. * @access protected * @return void */ protected function setMessageType() { - $this->message_type = array(); + $type = array(); if ($this->alternativeExists()) { - $this->message_type[] = "alt"; + $type[] = 'alt'; } if ($this->inlineImageExists()) { - $this->message_type[] = "inline"; + $type[] = 'inline'; } if ($this->attachmentExists()) { - $this->message_type[] = "attach"; + $type[] = 'attach'; } - $this->message_type = implode("_", $this->message_type); - if ($this->message_type == "") { - $this->message_type = "plain"; + $this->message_type = implode('_', $type); + if ($this->message_type == '') { + //The 'plain' message_type refers to the message having a single body element, not that it is plain-text + $this->message_type = 'plain'; } } /** * Format a header line. - * * @access public * @param string $name * @param string $value @@ -2104,7 +2485,6 @@ class PHPMailer /** * Return a formatted mail line. - * * @access public * @param string $value * @return string @@ -2116,15 +2496,15 @@ class PHPMailer /** * Add an attachment from a path on the filesystem. + * Never use a user-supplied path to a file! * Returns false if the file could not be found or read. - * - * @param string $path Path to the attachment. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. * @param string $disposition Disposition to use * @throws phpmailerException - * @return bool + * @return boolean */ public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') { @@ -2133,7 +2513,7 @@ class PHPMailer throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); } - //If a MIME type is not specified, try to work it out from the file name + // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($path); } @@ -2154,11 +2534,11 @@ class PHPMailer 7 => 0 ); - } catch (phpmailerException $e) { - $this->setError($e->getMessage()); - $this->edebug($e->getMessage()); + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); if ($this->exceptions) { - throw $e; + throw $exc; } return false; } @@ -2167,7 +2547,6 @@ class PHPMailer /** * Return the array of attachments. - * * @return array */ public function getAttachments() @@ -2178,7 +2557,6 @@ class PHPMailer /** * Attach all file, string, and binary attachments to the message. * Returns an empty string on failure. - * * @access protected * @param string $disposition_type * @param string $boundary @@ -2215,22 +2593,34 @@ class PHPMailer $type = $attachment[4]; $disposition = $attachment[6]; $cid = $attachment[7]; - if ($disposition == 'inline' && isset($cidUniq[$cid])) { + if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { continue; } $cidUniq[$cid] = true; - $mime[] = sprintf("--%s%s", $boundary, $this->LE); - $mime[] = sprintf( - "Content-Type: %s; name=\"%s\"%s", - $type, - $this->encodeHeader($this->secureHeader($name)), - $this->LE - ); - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + $mime[] = sprintf('--%s%s', $boundary, $this->LE); + //Only include a filename property if we have one + if (!empty($name)) { + $mime[] = sprintf( + 'Content-Type: %s; name="%s"%s', + $type, + $this->encodeHeader($this->secureHeader($name)), + $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Type: %s%s', + $type, + $this->LE + ); + } + // RFC1341 part 5 says 7bit is assumed if not specified + if ($encoding != '7bit') { + $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); + } if ($disposition == 'inline') { - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); } // If a filename contains any of these chars, it should be quoted, @@ -2238,20 +2628,29 @@ class PHPMailer // Fixes a warning in IETF's msglint MIME checker // Allow for bypassing the Content-Disposition header totally if (!(empty($disposition))) { - if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { + $encoded_name = $this->encodeHeader($this->secureHeader($name)); + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { $mime[] = sprintf( - "Content-Disposition: %s; filename=\"%s\"%s", + 'Content-Disposition: %s; filename="%s"%s', $disposition, - $this->encodeHeader($this->secureHeader($name)), + $encoded_name, $this->LE . $this->LE ); } else { - $mime[] = sprintf( - "Content-Disposition: %s; filename=%s%s", - $disposition, - $this->encodeHeader($this->secureHeader($name)), - $this->LE . $this->LE - ); + if (!empty($encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename=%s%s', + $disposition, + $encoded_name, + $this->LE . $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Disposition: %s%s', + $disposition, + $this->LE . $this->LE + ); + } } } else { $mime[] = $this->LE; @@ -2274,19 +2673,17 @@ class PHPMailer } } - $mime[] = sprintf("--%s--%s", $boundary, $this->LE); + $mime[] = sprintf('--%s--%s', $boundary, $this->LE); - return implode("", $mime); + return implode('', $mime); } /** * Encode a file attachment in requested format. * Returns an empty string on failure. - * - * @param string $path The full path to the file + * @param string $path The full path to the file * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' * @throws phpmailerException - * @see EncodeFile(encodeFile * @access protected * @return string */ @@ -2299,9 +2696,12 @@ class PHPMailer $magic_quotes = get_magic_quotes_runtime(); if ($magic_quotes) { if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime(0); + set_magic_quotes_runtime(false); } else { - ini_set('magic_quotes_runtime', 0); + //Doesn't exist in PHP 5.4, but we don't need to check because + //get_magic_quotes_runtime always returns false in 5.4+ + //so it will never get here + ini_set('magic_quotes_runtime', false); } } $file_buffer = file_get_contents($path); @@ -2314,8 +2714,8 @@ class PHPMailer } } return $file_buffer; - } catch (Exception $e) { - $this->setError($e->getMessage()); + } catch (Exception $exc) { + $this->setError($exc->getMessage()); return ''; } } @@ -2323,8 +2723,7 @@ class PHPMailer /** * Encode a string in requested format. * Returns an empty string on failure. - * - * @param string $str The text to encode + * @param string $str The text to encode * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' * @access public * @return string @@ -2339,7 +2738,7 @@ class PHPMailer case '7bit': case '8bit': $encoded = $this->fixEOL($str); - //Make sure it ends with a line break + // Make sure it ends with a line break if (substr($encoded, -(strlen($this->LE))) != $this->LE) { $encoded .= $this->LE; } @@ -2360,7 +2759,6 @@ class PHPMailer /** * Encode a header string optimally. * Picks shortest of Q, B, quoted-printable or none. - * * @access public * @param string $str * @param string $position @@ -2368,11 +2766,11 @@ class PHPMailer */ public function encodeHeader($str, $position = 'text') { - $x = 0; + $matchcount = 0; switch (strtolower($position)) { case 'phrase': if (!preg_match('/[\200-\377]/', $str)) { - // Can't use addslashes as we don't know what value has magic_quotes_sybase + // Can't use addslashes as we don't know the value of magic_quotes_sybase $encoded = addcslashes($str, "\0..\37\177\\\""); if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { return ($encoded); @@ -2380,26 +2778,27 @@ class PHPMailer return ("\"$encoded\""); } } - $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); break; /** @noinspection PhpMissingBreakStatementInspection */ case 'comment': - $x = preg_match_all('/[()"]/', $str, $matches); - // Intentional fall-through + $matchcount = preg_match_all('/[()"]/', $str, $matches); + // Intentional fall-through case 'text': default: - $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); break; } - if ($x == 0) { //There are no chars that need encoding + //There are no chars that need encoding + if ($matchcount == 0) { return ($str); } $maxlen = 75 - 7 - strlen($this->CharSet); // Try to select the encoding which should produce the shortest output - if ($x > strlen($str) / 3) { - //More than a third of the content will need encoding, so B encoding will be most efficient + if ($matchcount > strlen($str) / 3) { + // More than a third of the content will need encoding, so B encoding will be most efficient $encoding = 'B'; if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { // Use a custom function which correctly encodes and wraps long @@ -2417,7 +2816,7 @@ class PHPMailer $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); } - $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded); + $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); $encoded = trim(str_replace("\n", $this->LE, $encoded)); return $encoded; @@ -2425,10 +2824,9 @@ class PHPMailer /** * Check if a string contains multi-byte characters. - * * @access public * @param string $str multi-byte text to wrap encode - * @return bool + * @return boolean */ public function hasMultiBytes($str) { @@ -2439,23 +2837,33 @@ class PHPMailer } } + /** + * Does a string contain any 8-bit chars (in any charset)? + * @param string $text + * @return boolean + */ + public function has8bitChars($text) + { + return (boolean)preg_match('/[\x80-\xFF]/', $text); + } + /** * Encode and wrap long multibyte strings for mail headers * without breaking lines within a character. - * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php - * + * Adapted from a function by paravoid + * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 * @access public * @param string $str multi-byte text to wrap encode - * @param string $lf string to use as linefeed/end-of-line + * @param string $linebreak string to use as linefeed/end-of-line * @return string */ - public function base64EncodeWrapMB($str, $lf = null) + public function base64EncodeWrapMB($str, $linebreak = null) { - $start = "=?" . $this->CharSet . "?B?"; - $end = "?="; - $encoded = ""; - if ($lf === null) { - $lf = $this->LE; + $start = '=?' . $this->CharSet . '?B?'; + $end = '?='; + $encoded = ''; + if ($linebreak === null) { + $linebreak = $this->LE; } $mb_length = mb_strlen($str, $this->CharSet); @@ -2474,119 +2882,112 @@ class PHPMailer $chunk = base64_encode($chunk); $lookBack++; } while (strlen($chunk) > $length); - $encoded .= $chunk . $lf; + $encoded .= $chunk . $linebreak; } // Chomp the last linefeed - $encoded = substr($encoded, 0, -strlen($lf)); + $encoded = substr($encoded, 0, -strlen($linebreak)); return $encoded; } /** * Encode a string in quoted-printable format. * According to RFC2045 section 6.7. - * * @access public - * @param string $string The text to encode + * @param string $string The text to encode * @param integer $line_max Number of chars allowed on a line before wrapping * @return string - * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 + * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment */ public function encodeQP($string, $line_max = 76) { - if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) - return $this->fixEOL(quoted_printable_encode($string)); + // Use native function if it's available (>= PHP5.3) + if (function_exists('quoted_printable_encode')) { + return quoted_printable_encode($string); } - //Fall back to a pure PHP implementation + // Fall back to a pure PHP implementation $string = str_replace( array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string) ); - $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); - return $this->fixEOL($string); + return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); } /** * Backward compatibility wrapper for an old QP encoding function that was removed. - * - * @see PHPMailer::encodeQP() - * @access public + * @see PHPMailer::encodeQP() + * @access public * @param string $string * @param integer $line_max - * @param bool $space_conv + * @param boolean $space_conv * @return string * @deprecated Use encodeQP instead. */ public function encodeQPphp( $string, $line_max = 76, - /** @noinspection PhpUnusedParameterInspection */ - $space_conv = false - ) - { + /** @noinspection PhpUnusedParameterInspection */ $space_conv = false + ) { return $this->encodeQP($string, $line_max); } /** * Encode a string using Q encoding. - * - * @link http://tools.ietf.org/html/rfc2047 - * @param string $str the text to encode + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode * @param string $position Where the text is going to be used, see the RFC for what that means * @access public * @return string */ public function encodeQ($str, $position = 'text') { - //There should not be any EOL in the string + // There should not be any EOL in the string $pattern = ''; $encoded = str_replace(array("\r", "\n"), '', $str); switch (strtolower($position)) { case 'phrase': - //RFC 2047 section 5.3 + // RFC 2047 section 5.3 $pattern = '^A-Za-z0-9!*+\/ -'; break; /** @noinspection PhpMissingBreakStatementInspection */ case 'comment': - //RFC 2047 section 5.2 + // RFC 2047 section 5.2 $pattern = '\(\)"'; - //intentional fall-through - //for this reason we build the $pattern without including delimiters and [] + // intentional fall-through + // for this reason we build the $pattern without including delimiters and [] case 'text': default: - //RFC 2047 section 5.1 - //Replace every high ascii, control, =, ? and _ characters + // RFC 2047 section 5.1 + // Replace every high ascii, control, =, ? and _ characters $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; break; } $matches = array(); if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { - //If the string contains an '=', make sure it's the first thing we replace - //so as to avoid double-encoding - $s = array_search('=', $matches[0]); - if ($s !== false) { - unset($matches[0][$s]); + // If the string contains an '=', make sure it's the first thing we replace + // so as to avoid double-encoding + $eqkey = array_search('=', $matches[0]); + if (false !== $eqkey) { + unset($matches[0][$eqkey]); array_unshift($matches[0], '='); } foreach (array_unique($matches[0]) as $char) { $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); } } - //Replace every spaces to _ (more readable than =20) + // Replace every spaces to _ (more readable than =20) return str_replace(' ', '_', $encoded); } - /** * Add a string or binary attachment (non-filesystem). * This method can be used to attach ascii or binary data, * such as a BLOB record from a database. - * - * @param string $string String attachment data. - * @param string $filename Name of the attachment. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. * @param string $disposition Disposition to use * @return void */ @@ -2596,9 +2997,8 @@ class PHPMailer $encoding = 'base64', $type = '', $disposition = 'attachment' - ) - { - //If a MIME type is not specified, try to work it out from the file name + ) { + // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($filename); } @@ -2618,19 +3018,19 @@ class PHPMailer /** * Add an embedded (inline) attachment from a file. * This can include images, sounds, and just about any other document type. - * These differ from 'regular' attachmants in that they are intended to be + * These differ from 'regular' attachments in that they are intended to be * displayed inline with the message, not just attached for download. * This is used in HTML messages that embed the images * the HTML refers to using the $cid value. - * - * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment; Use this to reference - * the content when using an embedded image in HTML. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File MIME type. + * Never use a user-supplied path to a file! + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File MIME type. * @param string $disposition Disposition to use - * @return bool True on successfully adding an attachment + * @return boolean True on successfully adding an attachment */ public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') { @@ -2639,7 +3039,7 @@ class PHPMailer return false; } - //If a MIME type is not specified, try to work it out from the file name + // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($path); } @@ -2668,15 +3068,14 @@ class PHPMailer * This can include images, sounds, and just about any other document type. * Be sure to set the $type to an image type for images: * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. - * - * @param string $string The attachment binary data. - * @param string $cid Content ID of the attachment; Use this to reference - * the content when using an embedded image in HTML. + * @param string $string The attachment binary data. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. * @param string $name - * @param string $encoding File encoding (see $Encoding). - * @param string $type MIME type. + * @param string $encoding File encoding (see $Encoding). + * @param string $type MIME type. * @param string $disposition Disposition to use - * @return bool True on successfully adding an attachment + * @return boolean True on successfully adding an attachment */ public function addStringEmbeddedImage( $string, @@ -2685,10 +3084,9 @@ class PHPMailer $encoding = 'base64', $type = '', $disposition = 'inline' - ) - { - //If a MIME type is not specified, try to work it out from the name - if ($type == '') { + ) { + // If a MIME type is not specified, try to work it out from the name + if ($type == '' and !empty($name)) { $type = self::filenameToType($name); } @@ -2708,9 +3106,8 @@ class PHPMailer /** * Check if an inline attachment is present. - * * @access public - * @return bool + * @return boolean */ public function inlineImageExists() { @@ -2724,8 +3121,7 @@ class PHPMailer /** * Check if an attachment (non-inline) is present. - * - * @return bool + * @return boolean */ public function attachmentExists() { @@ -2739,17 +3135,31 @@ class PHPMailer /** * Check if this message has an alternative body set. - * - * @return bool + * @return boolean */ public function alternativeExists() { return !empty($this->AltBody); } + /** + * Clear queued addresses of given kind. + * @access protected + * @param string $kind 'to', 'cc', or 'bcc' + * @return void + */ + public function clearQueuedAddresses($kind) + { + $RecipientsQueue = $this->RecipientsQueue; + foreach ($RecipientsQueue as $address => $params) { + if ($params[0] == $kind) { + unset($this->RecipientsQueue[$address]); + } + } + } + /** * Clear all To recipients. - * * @return void */ public function clearAddresses() @@ -2758,11 +3168,11 @@ class PHPMailer unset($this->all_recipients[strtolower($to[0])]); } $this->to = array(); + $this->clearQueuedAddresses('to'); } /** * Clear all CC recipients. - * * @return void */ public function clearCCs() @@ -2771,11 +3181,11 @@ class PHPMailer unset($this->all_recipients[strtolower($cc[0])]); } $this->cc = array(); + $this->clearQueuedAddresses('cc'); } /** * Clear all BCC recipients. - * * @return void */ public function clearBCCs() @@ -2784,21 +3194,21 @@ class PHPMailer unset($this->all_recipients[strtolower($bcc[0])]); } $this->bcc = array(); + $this->clearQueuedAddresses('bcc'); } /** * Clear all ReplyTo recipients. - * * @return void */ public function clearReplyTos() { $this->ReplyTo = array(); + $this->ReplyToQueue = array(); } /** * Clear all recipient types. - * * @return void */ public function clearAllRecipients() @@ -2807,11 +3217,11 @@ class PHPMailer $this->cc = array(); $this->bcc = array(); $this->all_recipients = array(); + $this->RecipientsQueue = array(); } /** * Clear all filesystem, string, and binary attachments. - * * @return void */ public function clearAttachments() @@ -2821,7 +3231,6 @@ class PHPMailer /** * Clear all custom headers. - * * @return void */ public function clearCustomHeaders() @@ -2831,7 +3240,6 @@ class PHPMailer /** * Add an error message to the error container. - * * @access protected * @param string $msg * @return void @@ -2841,8 +3249,17 @@ class PHPMailer $this->error_count++; if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { $lasterror = $this->smtp->getError(); - if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { - $msg .= '

' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "

\n"; + if (!empty($lasterror['error'])) { + $msg .= $this->lang('smtp_error') . $lasterror['error']; + if (!empty($lasterror['detail'])) { + $msg .= ' Detail: '. $lasterror['detail']; + } + if (!empty($lasterror['smtp_code'])) { + $msg .= ' SMTP code: ' . $lasterror['smtp_code']; + } + if (!empty($lasterror['smtp_code_ex'])) { + $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; + } } } $this->ErrorInfo = $msg; @@ -2850,15 +3267,14 @@ class PHPMailer /** * Return an RFC 822 formatted date. - * * @access public * @return string * @static */ public static function rfcDate() { - //Set the time zone to whatever the default is to avoid 500 errors - //Will default to UTC if it's not set properly in php.ini + // Set the time zone to whatever the default is to avoid 500 errors + // Will default to UTC if it's not set properly in php.ini date_default_timezone_set(@date_default_timezone_get()); return date('D, j M Y H:i:s O'); } @@ -2866,7 +3282,6 @@ class PHPMailer /** * Get the server hostname. * Returns 'localhost.localdomain' if unknown. - * * @access protected * @return string */ @@ -2887,7 +3302,6 @@ class PHPMailer /** * Get an error message in the current language. - * * @access protected * @param string $key * @return string @@ -2898,18 +3312,24 @@ class PHPMailer $this->setLanguage('en'); // set the default language } - if (isset($this->language[$key])) { + if (array_key_exists($key, $this->language)) { + if ($key == 'smtp_connect_failed') { + //Include a link to troubleshooting docs on SMTP connection failure + //this is by far the biggest cause of support questions + //but it's usually not PHPMailer's fault. + return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + } return $this->language[$key]; } else { - return 'Language string failed to load: ' . $key; + //Return the key as a fallback + return $key; } } /** * Check if an error occurred. - * * @access public - * @return bool True if an error did occur. + * @return boolean True if an error did occur. */ public function isError() { @@ -2919,7 +3339,6 @@ class PHPMailer /** * Ensure consistent line endings in a string. * Changes every end of line from CRLF, CR or LF to $this->LE. - * * @access public * @param string $str String to fixEOL * @return string @@ -2939,9 +3358,8 @@ class PHPMailer * Add a custom header. * $name value can be overloaded to contain * both header name and value (name:value) - * * @access public - * @param string $name Custom header name + * @param string $name Custom header name * @param string $value Header value * @return void */ @@ -2956,33 +3374,73 @@ class PHPMailer } /** - * Create a message from an HTML string. - * Automatically makes modifications for inline images and backgrounds - * and creates a plain-text version by converting the HTML. - * Overwrites any existing values in $this->Body and $this->AltBody - * + * Returns all custom headers. + * @return array + */ + public function getCustomHeaders() + { + return $this->CustomHeader; + } + + /** + * Create a message body from an HTML string. + * Automatically inlines images and creates a plain-text version by converting the HTML, + * overwriting any existing values in Body and AltBody. + * Do not source $message content from user input! + * $basedir is prepended when handling relative URLs, e.g. and must not be empty + * will look for an image file in $basedir/images/a.png and convert it to inline. + * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email) + * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly. * @access public * @param string $message HTML message string - * @param string $basedir baseline directory for path - * @param bool $advanced Whether to use the advanced HTML to text converter - * @return string $message + * @param string $basedir Absolute path to a base directory to prepend to relative paths to images + * @param boolean|callable $advanced Whether to use the internal HTML to text converter + * or your own custom converter @see PHPMailer::html2text() + * @return string $message The transformed message Body */ public function msgHTML($message, $basedir = '', $advanced = false) { - preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); - if (isset($images[2])) { - foreach ($images[2] as $i => $url) { - // do not change urls for absolute images (thanks to corvuscorax) - if (!preg_match('#^[A-z]+://#', $url)) { + preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); + if (array_key_exists(2, $images)) { + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + // Ensure $basedir has a trailing / + $basedir .= '/'; + } + foreach ($images[2] as $imgindex => $url) { + // Convert data URIs into embedded images + if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { + $data = substr($url, strpos($url, ',')); + if ($match[2]) { + $data = base64_decode($data); + } else { + $data = rawurldecode($data); + } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 + if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { + $message = str_replace( + $images[0][$imgindex], + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + continue; + } + if ( + // Only process relative URLs if a basedir is provided (i.e. no absolute local paths) + !empty($basedir) + // Ignore URLs containing parent dir traversal (..) + && (strpos($url, '..') === false) + // Do not change urls that are already inline images + && substr($url, 0, 4) !== 'cid:' + // Do not change absolute URLs, including anonymous protocol + && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url) + ) { $filename = basename($url); $directory = dirname($url); if ($directory == '.') { $directory = ''; } - $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2 - if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { - $basedir .= '/'; - } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 if (strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } @@ -2991,12 +3449,12 @@ class PHPMailer $cid, $filename, 'base64', - self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)) + self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) ) ) { $message = preg_replace( - "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui", - $images[1][$i] . "=\"cid:" . $cid . "\"", + '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + $images[1][$imgindex] . '="cid:' . $cid . '"', $message ); } @@ -3004,10 +3462,10 @@ class PHPMailer } } $this->isHTML(true); - //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better + // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better $this->Body = $this->normalizeBreaks($message); $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); - if (empty($this->AltBody)) { + if (!$this->alternativeExists()) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . self::CRLF . self::CRLF; } @@ -3016,17 +3474,28 @@ class PHPMailer /** * Convert an HTML string into plain text. - * - * @param string $html The HTML text to convert - * @param bool $advanced Should this use the more complex html2text converter or just a simple one? + * This is used by msgHTML(). + * Note - older versions of this function used a bundled advanced converter + * which was been removed for license reasons in #232. + * Example usage: + * + * // Use default conversion + * $plain = $mail->html2text($html); + * // Use your own custom converter + * $plain = $mail->html2text($html, function($html) { + * $converter = new MyHtml2text($html); + * return $converter->get_text(); + * }); + * + * @param string $html The HTML text to convert + * @param boolean|callable $advanced Any boolean value to use the internal converter, + * or provide your own callable for custom conversion. * @return string */ public function html2text($html, $advanced = false) { - if ($advanced) { - require_once 'extras/class.html2text.php'; - $h = new html2text($html); - return $h->get_text(); + if (is_callable($advanced)) { + return call_user_func($advanced, $html); } return html_entity_decode( trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), @@ -3037,7 +3506,6 @@ class PHPMailer /** * Get the MIME type for a file extension. - * * @param string $ext File extension * @access public * @return string MIME type of file. @@ -3046,109 +3514,123 @@ class PHPMailer public static function _mime_types($ext = '') { $mimes = array( - 'xl' => 'application/excel', - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'bin' => 'application/macbinary', - 'doc' => 'application/msword', - 'word' => 'application/msword', + 'xl' => 'application/excel', + 'js' => 'application/javascript', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'class' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'psd' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'so' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'php3' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php' => 'application/x-httpd-php', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => 'application/x-javascript', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => 'application/x-tar', - 'xht' => 'application/xhtml+xml', + 'phps' => 'application/x-httpd-php-source', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xht' => 'application/xhtml+xml', 'xhtml' => 'application/xhtml+xml', - 'zip' => 'application/zip', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mpga' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'png' => 'image/png', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'eml' => 'message/rfc822', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'eml' => 'message/rfc822', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', 'shtml' => 'text/html', - 'log' => 'text/plain', - 'text' => 'text/plain', - 'txt' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mov' => 'video/quicktime', - 'qt' => 'video/quicktime', - 'rv' => 'video/vnd.rn-realvideo', - 'avi' => 'video/x-msvideo', + 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', + 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie' ); - return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)] : 'application/octet-stream'); + if (array_key_exists(strtolower($ext), $mimes)) { + return $mimes[strtolower($ext)]; + } + return 'application/octet-stream'; } /** * Map a file name to a MIME type. * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. - * * @param string $filename A file name or full path, does not need to exist as a file * @return string * @static */ public static function filenameToType($filename) { - //In case the path is a URL, strip any query string before getting extension + // In case the path is a URL, strip any query string before getting extension $qpos = strpos($filename, '?'); - if ($qpos !== false) { + if (false !== $qpos) { $filename = substr($filename, 0, $qpos); } $pathinfo = self::mb_pathinfo($filename); @@ -3159,48 +3641,44 @@ class PHPMailer * Multi-byte-safe pathinfo replacement. * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. * Works similarly to the one in PHP >= 5.2.0 - * * @link http://www.php.net/manual/en/function.pathinfo.php#107461 - * @param string $path A filename or path, does not need to exist as a file + * @param string $path A filename or path, does not need to exist as a file * @param integer|string $options Either a PATHINFO_* constant, - * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 + * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 * @return string|array * @static */ public static function mb_pathinfo($path, $options = null) { $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); - $m = array(); - preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); - if (array_key_exists(1, $m)) { - $ret['dirname'] = $m[1]; - } - if (array_key_exists(2, $m)) { - $ret['basename'] = $m[2]; - } - if (array_key_exists(5, $m)) { - $ret['extension'] = $m[5]; - } - if (array_key_exists(3, $m)) { - $ret['filename'] = $m[3]; + $pathinfo = array(); + if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { + if (array_key_exists(1, $pathinfo)) { + $ret['dirname'] = $pathinfo[1]; + } + if (array_key_exists(2, $pathinfo)) { + $ret['basename'] = $pathinfo[2]; + } + if (array_key_exists(5, $pathinfo)) { + $ret['extension'] = $pathinfo[5]; + } + if (array_key_exists(3, $pathinfo)) { + $ret['filename'] = $pathinfo[3]; + } } switch ($options) { case PATHINFO_DIRNAME: case 'dirname': return $ret['dirname']; - break; case PATHINFO_BASENAME: case 'basename': return $ret['basename']; - break; case PATHINFO_EXTENSION: case 'extension': return $ret['extension']; - break; case PATHINFO_FILENAME: case 'filename': return $ret['filename']; - break; default: return $ret; } @@ -3208,38 +3686,31 @@ class PHPMailer /** * Set or reset instance properties. - * + * You should avoid this function - it's more verbose, less efficient, more error-prone and + * harder to debug than setting properties directly. * Usage Example: - * $page->set('X-Priority', '3'); - * + * `$mail->set('SMTPSecure', 'tls');` + * is the same as: + * `$mail->SMTPSecure = 'tls';` * @access public - * @param string $name - * @param mixed $value - * NOTE: will not work with arrays, there are no arrays to set/reset - * @throws phpmailerException - * @return bool - * @todo Should this not be using __set() magic function? + * @param string $name The property name to set + * @param mixed $value The value to set the property to + * @return boolean + * @TODO Should this not be using the __set() magic function? */ public function set($name, $value = '') { - try { - if (isset($this->$name)) { - $this->$name = $value; - } else { - throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL); - } - } catch (Exception $e) { - $this->setError($e->getMessage()); - if ($e->getCode() == self::STOP_CRITICAL) { - return false; - } + if (property_exists($this, $name)) { + $this->$name = $value; + return true; + } else { + $this->setError($this->lang('variable_set') . $name); + return false; } - return true; } /** * Strip newlines to prevent header injection. - * * @access public * @param string $str * @return string @@ -3253,7 +3724,6 @@ class PHPMailer * Normalize line breaks in a string. * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. * Defaults to CRLF (for message bodies) and preserves consecutive breaks. - * * @param string $text * @param string $breaktype What kind of line break to use, defaults to CRLF * @return string @@ -3265,25 +3735,24 @@ class PHPMailer return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); } - /** * Set the public and private key files and password for S/MIME signing. - * * @access public * @param string $cert_filename * @param string $key_filename * @param string $key_pass Password for private key + * @param string $extracerts_filename Optional path to chain certificate */ - public function sign($cert_filename, $key_filename, $key_pass) + public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') { $this->sign_cert_file = $cert_filename; $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; + $this->sign_extracerts_file = $extracerts_filename; } /** * Quoted-Printable-encode a DKIM header. - * * @access public * @param string $txt * @return string @@ -3296,7 +3765,7 @@ class PHPMailer if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { $line .= $txt[$i]; } else { - $line .= "=" . sprintf("%02X", $ord); + $line .= '=' . sprintf('%02X', $ord); } } return $line; @@ -3304,56 +3773,73 @@ class PHPMailer /** * Generate a DKIM signature. - * * @access public - * @param string $s Header + * @param string $signHeader * @throws phpmailerException - * @return string + * @return string The DKIM signature value */ - public function DKIM_Sign($s) + public function DKIM_Sign($signHeader) { if (!defined('PKCS7_TEXT')) { if ($this->exceptions) { - throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.'); + throw new phpmailerException($this->lang('extension_missing') . 'openssl'); } return ''; } - $privKeyStr = file_get_contents($this->DKIM_private); - if ($this->DKIM_passphrase != '') { + $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); + if ('' != $this->DKIM_passphrase) { $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); } else { - $privKey = $privKeyStr; + $privKey = openssl_pkey_get_private($privKeyStr); } - if (openssl_sign($s, $signature, $privKey)) { - return base64_encode($signature); + //Workaround for missing digest algorithms in old PHP & OpenSSL versions + //@link http://stackoverflow.com/a/11117338/333340 + if (version_compare(PHP_VERSION, '5.3.0') >= 0 and + in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { + openssl_pkey_free($privKey); + return base64_encode($signature); + } + } else { + $pinfo = openssl_pkey_get_details($privKey); + $hash = hash('sha256', $signHeader); + //'Magic' constant for SHA256 from RFC3447 + //@link https://tools.ietf.org/html/rfc3447#page-43 + $t = '3031300d060960864801650304020105000420' . $hash; + $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); + $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); + + if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { + openssl_pkey_free($privKey); + return base64_encode($signature); + } } + openssl_pkey_free($privKey); return ''; } /** * Generate a DKIM canonicalization header. - * * @access public - * @param string $s Header + * @param string $signHeader Header * @return string */ - public function DKIM_HeaderC($s) + public function DKIM_HeaderC($signHeader) { - $s = preg_replace("/\r\n\s+/", " ", $s); - $lines = explode("\r\n", $s); + $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); + $lines = explode("\r\n", $signHeader); foreach ($lines as $key => $line) { - list($heading, $value) = explode(":", $line, 2); + list($heading, $value) = explode(':', $line, 2); $heading = strtolower($heading); - $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces - $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value + $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces + $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value } - $s = implode("\r\n", $lines); - return $s; + $signHeader = implode("\r\n", $lines); + return $signHeader; } /** * Generate a DKIM canonicalization body. - * * @access public * @param string $body Message Body * @return string @@ -3375,16 +3861,15 @@ class PHPMailer /** * Create the DKIM header and body in a new message header. - * * @access public * @param string $headers_line Header lines - * @param string $subject Subject - * @param string $body Body + * @param string $subject Subject + * @param string $body Body * @return string */ public function DKIM_Add($headers_line, $subject, $body) { - $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMquery = 'dns/txt'; // Query method $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) @@ -3392,6 +3877,7 @@ class PHPMailer $headers = explode($this->LE, $headers_line); $from_header = ''; $to_header = ''; + $date_header = ''; $current = ''; foreach ($headers as $header) { if (strpos($header, 'From:') === 0) { @@ -3400,9 +3886,12 @@ class PHPMailer } elseif (strpos($header, 'To:') === 0) { $to_header = $header; $current = 'to_header'; + } elseif (strpos($header, 'Date:') === 0) { + $date_header = $header; + $current = 'date_header'; } else { - if ($current && strpos($header, ' =?') === 0) { - $current .= $header; + if (!empty($$current) && strpos($header, ' =?') === 0) { + $$current .= $header; } else { $current = ''; } @@ -3410,6 +3899,7 @@ class PHPMailer } $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); $subject = str_replace( '|', '=7C', @@ -3417,32 +3907,53 @@ class PHPMailer ); // Copied header fields (dkim-quoted-printable) $body = $this->DKIM_BodyC($body); $DKIMlen = strlen($body); // Length of body - $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body - $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";"; - $dkimhdrs = "DKIM-Signature: v=1; a=" . - $DKIMsignatureType . "; q=" . - $DKIMquery . "; l=" . - $DKIMlen . "; s=" . + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body + if ('' == $this->DKIM_identity) { + $ident = ''; + } else { + $ident = ' i=' . $this->DKIM_identity . ';'; + } + $dkimhdrs = 'DKIM-Signature: v=1; a=' . + $DKIMsignatureType . '; q=' . + $DKIMquery . '; l=' . + $DKIMlen . '; s=' . $this->DKIM_selector . ";\r\n" . - "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" . - "\th=From:To:Subject;\r\n" . - "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" . + "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . + "\th=From:To:Date:Subject;\r\n" . + "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . "\tz=$from\r\n" . "\t|$to\r\n" . + "\t|$date\r\n" . "\t|$subject;\r\n" . "\tbh=" . $DKIMb64 . ";\r\n" . "\tb="; $toSign = $this->DKIM_HeaderC( - $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs + $from_header . "\r\n" . + $to_header . "\r\n" . + $date_header . "\r\n" . + $subject_header . "\r\n" . + $dkimhdrs ); $signed = $this->DKIM_Sign($toSign); return $dkimhdrs . $signed . "\r\n"; } + /** + * Detect if a string contains a line longer than the maximum line length allowed. + * @param string $str + * @return boolean + * @static + */ + public static function hasLineLongerThanMax($str) + { + //+2 to include CRLF line break for a 1000 total + return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); + } + /** * Allows for public read access to 'to' property. - * + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ @@ -3453,7 +3964,7 @@ class PHPMailer /** * Allows for public read access to 'cc' property. - * + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ @@ -3464,7 +3975,7 @@ class PHPMailer /** * Allows for public read access to 'bcc' property. - * + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ @@ -3475,7 +3986,7 @@ class PHPMailer /** * Allows for public read access to 'ReplyTo' property. - * + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ @@ -3486,7 +3997,7 @@ class PHPMailer /** * Allows for public read access to 'all_recipients' property. - * + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ @@ -3497,16 +4008,15 @@ class PHPMailer /** * Perform a callback. - * - * @param bool $isSent - * @param string $to - * @param string $cc - * @param string $bcc + * @param boolean $isSent + * @param array $to + * @param array $cc + * @param array $bcc * @param string $subject * @param string $body * @param string $from */ - protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) { if (!empty($this->action_function) && is_callable($this->action_function)) { $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); @@ -3517,14 +4027,12 @@ class PHPMailer /** * PHPMailer exception handler - * * @package PHPMailer */ -class phpmailerException extends \Exception +class phpmailerException extends Exception { /** * Prettify error message output - * * @return string */ public function errorMessage() diff --git a/inc/Exts/phpmailer/SMTP.php b/inc/Exts/phpmailer/SMTP.php index dff10030..e8d9f2d0 100644 --- a/inc/Exts/phpmailer/SMTP.php +++ b/inc/Exts/phpmailer/SMTP.php @@ -1,175 +1,227 @@ - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @copyright 2013 Marcus Bointon - * @copyright 2004 - 2008 Andy Prevost + * PHP Version 5 + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski - * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. */ namespace phpmailer; /** * PHPMailer RFC821 SMTP email transport class. - * - * Implements RFC 821 SMTP commands - * and provides some utility methods for sending mail to an SMTP server. - * - * PHP Version 5.0.0 - * - * @category PHP - * @package PHPMailer - * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php - * @author Chris Ryan - * @author Marcus Bointon - * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) + * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. + * @package PHPMailer + * @author Chris Ryan + * @author Marcus Bointon */ class SMTP { /** - * The PHPMailer SMTP Version number. + * The PHPMailer SMTP version number. + * @var string */ - const VERSION = '5.2.7'; + const VERSION = '5.2.22'; /** * SMTP line break constant. + * @var string */ const CRLF = "\r\n"; /** * The SMTP port to use if one is not specified. + * @var integer */ const DEFAULT_SMTP_PORT = 25; /** - * The PHPMailer SMTP Version number. - * - * @type string - * @deprecated This should be a constant - * @see SMTP::VERSION + * The maximum line length allowed by RFC 2822 section 2.1.1 + * @var integer */ - public $Version = '5.2.7'; + const MAX_LINE_LENGTH = 998; + + /** + * Debug level for no output + */ + const DEBUG_OFF = 0; + + /** + * Debug level to show client -> server messages + */ + const DEBUG_CLIENT = 1; + + /** + * Debug level to show client -> server and server -> client messages + */ + const DEBUG_SERVER = 2; + + /** + * Debug level to show connection status, client -> server and server -> client messages + */ + const DEBUG_CONNECTION = 3; + + /** + * Debug level to show all messages + */ + const DEBUG_LOWLEVEL = 4; + + /** + * The PHPMailer SMTP Version number. + * @var string + * @deprecated Use the `VERSION` constant instead + * @see SMTP::VERSION + */ + public $Version = '5.2.22'; /** * SMTP server port number. - * - * @type int - * @deprecated This is only ever ued as default value, so should be a constant - * @see SMTP::DEFAULT_SMTP_PORT + * @var integer + * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead + * @see SMTP::DEFAULT_SMTP_PORT */ public $SMTP_PORT = 25; /** - * SMTP reply line ending - * - * @type string - * @deprecated Use the class constant instead - * @see SMTP::CRLF + * SMTP reply line ending. + * @var string + * @deprecated Use the `CRLF` constant instead + * @see SMTP::CRLF */ public $CRLF = "\r\n"; /** * Debug output level. * Options: - * 0: no output - * 1: commands - * 2: data and commands - * 3: as 2 plus connection status - * 4: low level data output - * - * @type int + * * self::DEBUG_OFF (`0`) No debug output, default + * * self::DEBUG_CLIENT (`1`) Client commands + * * self::DEBUG_SERVER (`2`) Client commands and server responses + * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status + * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages + * @var integer */ - public $do_debug = 0; + public $do_debug = self::DEBUG_OFF; /** - * The function/method to use for debugging output. - * Options: 'echo', 'html' or 'error_log' + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini * - * @type string + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * + * @var string|callable */ public $Debugoutput = 'echo'; /** * Whether to use VERP. - * - * @type bool + * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @link http://www.postfix.org/VERP_README.html Info on VERP + * @var boolean */ public $do_verp = false; /** * The timeout value for connection, in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 - * - * @type int + * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. + * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 + * @var integer */ public $Timeout = 300; /** - * The SMTP timelimit value for reads, in seconds. - * - * @type int + * How long to wait for commands to complete, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * @var integer */ - public $Timelimit = 30; + public $Timelimit = 300; + + /** + * @var array patterns to extract smtp transaction id from smtp reply + * Only first capture group will be use, use non-capturing group to deal with it + * Extend this class to override this property to fulfil your needs. + */ + protected $smtp_transaction_id_patterns = array( + 'exim' => '/[0-9]{3} OK id=(.*)/', + 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', + 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' + ); /** * The socket for the server connection. - * - * @type resource + * @var resource */ protected $smtp_conn; /** - * Error message, if any, for the last call. - * - * @type string + * Error information, if any, for the last SMTP command. + * @var array */ - protected $error = ''; + protected $error = array( + 'error' => '', + 'detail' => '', + 'smtp_code' => '', + 'smtp_code_ex' => '' + ); /** * The reply the server sent to us for HELO. - * - * @type string + * If null, no HELO string has yet been received. + * @var string|null */ - protected $helo_rply = ''; + protected $helo_rply = null; + + /** + * The set of SMTP extensions sent in reply to EHLO command. + * Indexes of the array are extension names. + * Value at index 'HELO' or 'EHLO' (according to command that was sent) + * represents the server name. In case of HELO it is the only element of the array. + * Other values can be boolean TRUE or an array containing extension options. + * If null, no HELO/EHLO string has yet been received. + * @var array|null + */ + protected $server_caps = null; /** * The most recent reply received from the server. - * - * @type string + * @var string */ protected $last_reply = ''; - /** - * Constructor. - * - * @access public - */ - public function __construct() - { - $this->smtp_conn = 0; - $this->error = null; - $this->helo_rply = null; - - $this->do_debug = 0; - } - /** * Output debugging info via a user-selected method. - * + * @see SMTP::$Debugoutput + * @see SMTP::$do_debug * @param string $str Debug string to output + * @param integer $level The debug level of this message; see DEBUG_* constants * @return void */ - protected function edebug($str) + protected function edebug($str, $level = 0) { + if ($level > $this->do_debug) { + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $level); + return; + } switch ($this->Debugoutput) { case 'error_log': //Don't output, just log @@ -178,119 +230,145 @@ class SMTP case 'html': //Cleans up output a bit for a better looking, HTML-safe output echo htmlentities( - preg_replace('/[\r\n]+/', '', $str), - ENT_QUOTES, - 'UTF-8' - ) - . "
\n"; + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "
\n"; break; case 'echo': default: - echo gmdate('Y-m-d H:i:s') . "\t" . trim($str) . "\n"; + //Normalize line breaks + $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( + "\n", + "\n \t ", + trim($str) + )."\n"; } } /** * Connect to an SMTP server. - * - * @param string $host SMTP server IP or host name - * @param int $port The port number to connect to - * @param int $timeout How long to wait for the connection to open + * @param string $host SMTP server IP or host name + * @param integer $port The port number to connect to + * @param integer $timeout How long to wait for the connection to open * @param array $options An array of options for stream_context_create() * @access public - * @return bool + * @return boolean */ public function connect($host, $port = null, $timeout = 30, $options = array()) { + static $streamok; + //This is enabled by default since 5.0.0 but some providers disable it + //Check this once and cache the result + if (is_null($streamok)) { + $streamok = function_exists('stream_socket_client'); + } // Clear errors to avoid confusion - $this->error = null; - + $this->setError(''); // Make sure we are __not__ connected if ($this->connected()) { // Already connected, generate error - $this->error = array('error' => 'Already connected to a server'); + $this->setError('Already connected to a server'); return false; } - if (empty($port)) { $port = self::DEFAULT_SMTP_PORT; } - // Connect to the SMTP server - if ($this->do_debug >= 3) { - $this->edebug('Connection: opening'); - } - + $this->edebug( + "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), + self::DEBUG_CONNECTION + ); $errno = 0; $errstr = ''; - $socket_context = stream_context_create($options); - //Suppress errors; connection failures are handled at a higher level - $this->smtp_conn = @stream_socket_client( - $host . ":" . $port, - $errno, - $errstr, - $timeout, - STREAM_CLIENT_CONNECT, - $socket_context - ); - - // Verify we connected properly - if (empty($this->smtp_conn)) { - $this->error = array( - 'error' => 'Failed to connect to server', - 'errno' => $errno, - 'errstr' => $errstr + if ($streamok) { + $socket_context = stream_context_create($options); + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = stream_socket_client( + $host . ":" . $port, + $errno, + $errstr, + $timeout, + STREAM_CLIENT_CONNECT, + $socket_context + ); + restore_error_handler(); + } else { + //Fall back to fsockopen which should work in more places, but is missing some features + $this->edebug( + "Connection: stream_socket_client not available, falling back to fsockopen", + self::DEBUG_CONNECTION + ); + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = fsockopen( + $host, + $port, + $errno, + $errstr, + $timeout + ); + restore_error_handler(); + } + // Verify we connected properly + if (!is_resource($this->smtp_conn)) { + $this->setError( + 'Failed to connect to server', + $errno, + $errstr + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] + . ": $errstr ($errno)", + self::DEBUG_CLIENT ); - if ($this->do_debug >= 1) { - $this->edebug( - 'SMTP ERROR: ' . $this->error['error'] - . ": $errstr ($errno)" - ); - } return false; } - if ($this->do_debug >= 3) { - $this->edebug('Connection: opened'); - } - + $this->edebug('Connection: opened', self::DEBUG_CONNECTION); // SMTP server can take longer to respond, give longer timeout for first read // Windows does not have support for this timeout function if (substr(PHP_OS, 0, 3) != 'WIN') { $max = ini_get('max_execution_time'); - if ($max != 0 && $timeout > $max) { // Don't bother if unlimited + // Don't bother if unlimited + if ($max != 0 && $timeout > $max) { @set_time_limit($timeout); } stream_set_timeout($this->smtp_conn, $timeout, 0); } - // Get any announcement $announce = $this->get_lines(); - - if ($this->do_debug >= 2) { - $this->edebug('SERVER -> CLIENT: ' . $announce); - } - + $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); return true; } /** * Initiate a TLS (encrypted) session. - * * @access public - * @return bool + * @return boolean */ public function startTLS() { - if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) { + if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + // Begin encrypted connection if (!stream_socket_enable_crypto( $this->smtp_conn, true, - STREAM_CRYPTO_METHOD_TLS_CLIENT - ) - ) { + $crypto_method + )) { return false; } return true; @@ -299,28 +377,65 @@ class SMTP /** * Perform SMTP authentication. * Must be run after hello(). - * - * @see hello() - * @param string $username The user name - * @param string $password The password - * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5) - * @param string $realm The auth realm for NTLM + * @see hello() + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2) + * @param string $realm The auth realm for NTLM * @param string $workstation The auth workstation for NTLM - * @access public - * @return bool True if successfully authenticated. + * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) + * @return bool True if successfully authenticated.* @access public */ public function authenticate( $username, $password, - $authtype = 'LOGIN', + $authtype = null, $realm = '', - $workstation = '' - ) - { - if (empty($authtype)) { - $authtype = 'LOGIN'; + $workstation = '', + $OAuth = null + ) { + if (!$this->server_caps) { + $this->setError('Authentication is not allowed before HELO/EHLO'); + return false; } + if (array_key_exists('EHLO', $this->server_caps)) { + // SMTP extensions are available. Let's try to find a proper authentication method + + if (!array_key_exists('AUTH', $this->server_caps)) { + $this->setError('Authentication is not allowed at this stage'); + // 'at this stage' means that auth may be allowed after the stage changes + // e.g. after STARTTLS + return false; + } + + self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); + self::edebug( + 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), + self::DEBUG_LOWLEVEL + ); + + if (empty($authtype)) { + foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { + if (in_array($method, $this->server_caps['AUTH'])) { + $authtype = $method; + break; + } + } + if (empty($authtype)) { + $this->setError('No supported authentication methods found'); + return false; + } + self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); + } + + if (!in_array($authtype, $this->server_caps['AUTH'])) { + $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); + return false; + } + } elseif (empty($authtype)) { + $authtype = 'LOGIN'; + } switch ($authtype) { case 'PLAIN': // Start authentication @@ -349,6 +464,19 @@ class SMTP return false; } break; + case 'XOAUTH2': + //If the OAuth Instance is not set. Can be a case when PHPMailer is used + //instead of PHPMailerOAuth + if (is_null($OAuth)) { + return false; + } + $oauth = $OAuth->getOauth64(); + + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { + return false; + } + break; case 'NTLM': /* * ntlm_sasl_client.php @@ -359,21 +487,20 @@ class SMTP * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication */ require_once 'extras/ntlm_sasl_client.php'; - $temp = new stdClass(); + $temp = new stdClass; $ntlm_client = new ntlm_sasl_client_class; //Check that functions are available - if (!$ntlm_client->Initialize($temp)) { - $this->error = array('error' => $temp->error); - if ($this->do_debug >= 1) { - $this->edebug( - 'You need to enable some modules in your php.ini file: ' - . $this->error['error'] - ); - } + if (!$ntlm_client->initialize($temp)) { + $this->setError($temp->error); + $this->edebug( + 'You need to enable some modules in your php.ini file: ' + . $this->error['error'], + self::DEBUG_CLIENT + ); return false; } //msg1 - $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1 + $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 if (!$this->sendCommand( 'AUTH NTLM', @@ -383,7 +510,6 @@ class SMTP ) { return false; } - //Though 0 based, there is a white space after the 3 digit number //msg2 $challenge = substr($this->last_reply, 3); @@ -393,7 +519,7 @@ class SMTP $password ); //msg3 - $msg3 = $ntlm_client->TypeMsg3( + $msg3 = $ntlm_client->typeMsg3( $ntlm_res, $username, $realm, @@ -401,7 +527,6 @@ class SMTP ); // send encoded username return $this->sendCommand('Username', base64_encode($msg3), 235); - break; case 'CRAM-MD5': // Start authentication if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { @@ -415,7 +540,9 @@ class SMTP // send encoded credentials return $this->sendCommand('Username', base64_encode($response), 235); - break; + default: + $this->setError("Authentication method \"$authtype\" is not supported"); + return false; } return true; } @@ -424,7 +551,6 @@ class SMTP * Calculate an MD5 HMAC hash. * Works like hash_hmac('md5', $data, $key) * in case that function is not available - * * @param string $data The data to hash * @param string $key The key to hash with * @access protected @@ -442,15 +568,15 @@ class SMTP // RFC 2104 HMAC implementation for php. // Creates an md5 HMAC. // Eliminates the need to install mhash to compute a HMAC - // Hacked by Lance Rushing + // by Lance Rushing - $b = 64; // byte length for md5 - if (strlen($key) > $b) { + $bytelen = 64; // byte length for md5 + if (strlen($key) > $bytelen) { $key = pack('H*', md5($key)); } - $key = str_pad($key, $b, chr(0x00)); - $ipad = str_pad('', $b, chr(0x36)); - $opad = str_pad('', $b, chr(0x5c)); + $key = str_pad($key, $bytelen, chr(0x00)); + $ipad = str_pad('', $bytelen, chr(0x36)); + $opad = str_pad('', $bytelen, chr(0x5c)); $k_ipad = $key ^ $ipad; $k_opad = $key ^ $opad; @@ -459,21 +585,19 @@ class SMTP /** * Check connection state. - * * @access public - * @return bool True if connected. + * @return boolean True if connected. */ public function connected() { - if (!empty($this->smtp_conn)) { + if (is_resource($this->smtp_conn)) { $sock_status = stream_get_meta_data($this->smtp_conn); if ($sock_status['eof']) { - // the socket is valid but we are not connected - if ($this->do_debug >= 1) { - $this->edebug( - 'SMTP NOTICE: EOF caught while checking if connected' - ); - } + // The socket is valid but we are not connected + $this->edebug( + 'SMTP NOTICE: EOF caught while checking if connected', + self::DEBUG_CLIENT + ); $this->close(); return false; } @@ -485,22 +609,20 @@ class SMTP /** * Close the socket and clean up the state of the class. * Don't use this function without first trying to use QUIT. - * - * @see quit() + * @see quit() * @access public * @return void */ public function close() { - $this->error = null; // so there is no confusion + $this->setError(''); + $this->server_caps = null; $this->helo_rply = null; - if (!empty($this->smtp_conn)) { + if (is_resource($this->smtp_conn)) { // close the connection and cleanup fclose($this->smtp_conn); - if ($this->do_debug >= 3) { - $this->edebug('Connection: closed'); - } - $this->smtp_conn = 0; + $this->smtp_conn = null; //Makes for cleaner serialization + $this->edebug('Connection: closed', self::DEBUG_CONNECTION); } } @@ -512,134 +634,168 @@ class SMTP * on a single line followed by a with the message headers * and the message body being separated by and additional . * Implements rfc 821: DATA - * * @param string $msg_data Message data to send * @access public - * @return bool + * @return boolean */ public function data($msg_data) { + //This will use the standard timelimit if (!$this->sendCommand('DATA', 'DATA', 354)) { return false; } /* The server is ready to accept data! - * according to rfc821 we should not send more than 1000 - * including the CRLF - * characters on a single line so we will break the data up - * into lines by \r and/or \n then if needed we will break - * each of those into smaller lines to fit within the limit. - * in addition we will be looking for lines that start with - * a period '.' and append and additional period '.' to that - * line. NOTE: this does not count towards limit. + * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) + * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into + * smaller lines to fit within the limit. + * We will also look for lines that start with a '.' and prepend an additional '.'. + * NOTE: this does not count towards line-length limit. */ - // Normalize the line breaks before exploding - $msg_data = str_replace("\r\n", "\n", $msg_data); - $msg_data = str_replace("\r", "\n", $msg_data); - $lines = explode("\n", $msg_data); + // Normalize line breaks before exploding + $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); - /* We need to find a good way to determine if headers are - * in the msg_data or if it is a straight msg body - * currently I am assuming rfc822 definitions of msg headers - * and if the first field of the first line (':' separated) - * does not contain a space then it _should_ be a header - * and we can process all lines before a blank "" line as - * headers. + /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field + * of the first line (':' separated) does not contain a space then it _should_ be a header and we will + * process all lines before a blank line as headers. */ $field = substr($lines[0], 0, strpos($lines[0], ':')); $in_headers = false; - if (!empty($field) && !strstr($field, ' ')) { + if (!empty($field) && strpos($field, ' ') === false) { $in_headers = true; } - //RFC 2822 section 2.1.1 limit - $max_line_length = 998; - foreach ($lines as $line) { - $lines_out = null; - if ($line == '' && $in_headers) { + $lines_out = array(); + if ($in_headers and $line == '') { $in_headers = false; } - // ok we need to break this line up into several smaller lines - while (strlen($line) > $max_line_length) { - $pos = strrpos(substr($line, 0, $max_line_length), ' '); - - // Patch to fix DOS attack + //Break this line up into several smaller lines if it's too long + //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), + while (isset($line[self::MAX_LINE_LENGTH])) { + //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on + //so as to avoid breaking in the middle of a word + $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); + //Deliberately matches both false and 0 if (!$pos) { - $pos = $max_line_length - 1; + //No nice break found, add a hard break + $pos = self::MAX_LINE_LENGTH - 1; $lines_out[] = substr($line, 0, $pos); $line = substr($line, $pos); } else { + //Break at the found point $lines_out[] = substr($line, 0, $pos); + //Move along by the amount we dealt with $line = substr($line, $pos + 1); } - - /* If processing headers add a LWSP-char to the front of new line - * rfc822 on long msg headers - */ + //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 if ($in_headers) { $line = "\t" . $line; } } $lines_out[] = $line; - // send the lines to the server - while (list(, $line_out) = @each($lines_out)) { - if (strlen($line_out) > 0) { - if (substr($line_out, 0, 1) == '.') { - $line_out = '.' . $line_out; - } + //Send the lines to the server + foreach ($lines_out as $line_out) { + //RFC2821 section 4.5.2 + if (!empty($line_out) and $line_out[0] == '.') { + $line_out = '.' . $line_out; } $this->client_send($line_out . self::CRLF); } } - // Message data has been sent, complete the command - return $this->sendCommand('DATA END', '.', 250); + //Message data has been sent, complete the command + //Increase timelimit for end of DATA command + $savetimelimit = $this->Timelimit; + $this->Timelimit = $this->Timelimit * 2; + $result = $this->sendCommand('DATA END', '.', 250); + //Restore timelimit + $this->Timelimit = $savetimelimit; + return $result; } /** * Send an SMTP HELO or EHLO command. * Used to identify the sending server to the receiving server. * This makes sure that client and server are in a known state. - * Implements from RFC 821: HELO + * Implements RFC 821: HELO * and RFC 2821 EHLO. - * * @param string $host The host name or IP to connect to * @access public - * @return bool + * @return boolean */ public function hello($host = '') { - // Try extended hello first (RFC 2821) - if (!$this->sendHello('EHLO', $host)) { - if (!$this->sendHello('HELO', $host)) { - return false; - } - } - - return true; + //Try extended hello first (RFC 2821) + return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); } /** * Send an SMTP HELO or EHLO command. * Low-level implementation used by hello() - * - * @see hello() + * @see hello() * @param string $hello The HELO string - * @param string $host The hostname to say we are + * @param string $host The hostname to say we are * @access protected - * @return bool + * @return boolean */ protected function sendHello($hello, $host) { $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); $this->helo_rply = $this->last_reply; + if ($noerror) { + $this->parseHelloFields($hello); + } else { + $this->server_caps = null; + } return $noerror; } + /** + * Parse a reply to HELO/EHLO command to discover server extensions. + * In case of HELO, the only parameter that can be discovered is a server name. + * @access protected + * @param string $type - 'HELO' or 'EHLO' + */ + protected function parseHelloFields($type) + { + $this->server_caps = array(); + $lines = explode("\n", $this->helo_rply); + + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space + $s = trim(substr($s, 4)); + if (empty($s)) { + continue; + } + $fields = explode(' ', $s); + if (!empty($fields)) { + if (!$n) { + $name = $type; + $fields = $fields[0]; + } else { + $name = array_shift($fields); + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = array(); + } + break; + default: + $fields = true; + } + } + $this->server_caps[$name] = $fields; + } + } + } + /** * Send an SMTP MAIL command. * Starts a mail transaction from the email address specified in @@ -647,10 +803,9 @@ class SMTP * the mail transaction is started and then one or more recipient * commands may be called followed by a data command. * Implements rfc 821: MAIL FROM: - * * @param string $from Source address of this message * @access public - * @return bool + * @return boolean */ public function mail($from) { @@ -666,37 +821,35 @@ class SMTP * Send an SMTP QUIT command. * Closes the socket if there is no error or the $close_on_error argument is true. * Implements from rfc 821: QUIT - * - * @param bool $close_on_error Should the connection close if an error occurs? + * @param boolean $close_on_error Should the connection close if an error occurs? * @access public - * @return bool + * @return boolean */ public function quit($close_on_error = true) { $noerror = $this->sendCommand('QUIT', 'QUIT', 221); - $e = $this->error; //Save any error + $err = $this->error; //Save any error if ($noerror or $close_on_error) { $this->close(); - $this->error = $e; //Restore any error from the quit command + $this->error = $err; //Restore any error from the quit command } return $noerror; } /** * Send an SMTP RCPT command. - * Sets the TO argument to $to. + * Sets the TO argument to $toaddr. * Returns true if the recipient was accepted false if it was rejected. * Implements from rfc 821: RCPT TO: - * - * @param string $to The address the message is being sent to + * @param string $address The address the message is being sent to * @access public - * @return bool + * @return boolean */ - public function recipient($to) + public function recipient($address) { return $this->sendCommand( - 'RCPT TO ', - 'RCPT TO:<' . $to . '>', + 'RCPT TO', + 'RCPT TO:<' . $address . '>', array(250, 251) ); } @@ -705,9 +858,8 @@ class SMTP * Send an SMTP RSET command. * Abort any transaction that is currently in progress. * Implements rfc 821: RSET - * * @access public - * @return bool True on success. + * @return boolean True on success. */ public function reset() { @@ -716,47 +868,61 @@ class SMTP /** * Send a command to an SMTP server and check its return code. - * - * @param string $command The command name - not sent to the server + * @param string $command The command name - not sent to the server * @param string $commandstring The actual command to send - * @param int|array $expect One or more expected integer success codes + * @param integer|array $expect One or more expected integer success codes * @access protected - * @return bool True on success. + * @return boolean True on success. */ protected function sendCommand($command, $commandstring, $expect) { if (!$this->connected()) { - $this->error = array( - "error" => "Called $command without being connected" - ); + $this->setError("Called $command without being connected"); + return false; + } + //Reject line breaks in all commands + if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { + $this->setError("Command '$command' contained line breaks"); return false; } $this->client_send($commandstring . self::CRLF); - $reply = $this->get_lines(); - $code = substr($reply, 0, 3); - - if ($this->do_debug >= 2) { - $this->edebug('SERVER -> CLIENT: ' . $reply); + $this->last_reply = $this->get_lines(); + // Fetch SMTP code and possible error code explanation + $matches = array(); + if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { + $code = $matches[1]; + $code_ex = (count($matches) > 2 ? $matches[2] : null); + // Cut off error code from each response line + $detail = preg_replace( + "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", + '', + $this->last_reply + ); + } else { + // Fall back to simple parsing if regex fails + $code = substr($this->last_reply, 0, 3); + $code_ex = null; + $detail = substr($this->last_reply, 4); } + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); + if (!in_array($code, (array)$expect)) { - $this->last_reply = null; - $this->error = array( - "error" => "$command command failed", - "smtp_code" => $code, - "detail" => substr($reply, 4) + $this->setError( + "$command command failed", + $detail, + $code, + $code_ex + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, + self::DEBUG_CLIENT ); - if ($this->do_debug >= 1) { - $this->edebug( - 'SMTP ERROR: ' . $this->error['error'] . ': ' . $reply - ); - } return false; } - $this->last_reply = $reply; - $this->error = null; + $this->setError(''); return true; } @@ -769,79 +935,67 @@ class SMTP * will send the message to the users terminal if they are logged * in and send them an email. * Implements rfc 821: SAML FROM: - * * @param string $from The address the message is from * @access public - * @return bool + * @return boolean */ public function sendAndMail($from) { - return $this->sendCommand("SAML", "SAML FROM:$from", 250); + return $this->sendCommand('SAML', "SAML FROM:$from", 250); } /** * Send an SMTP VRFY command. - * * @param string $name The name to verify * @access public - * @return bool + * @return boolean */ public function verify($name) { - return $this->sendCommand("VRFY", "VRFY $name", array(250, 251)); + return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); } /** * Send an SMTP NOOP command. * Used to keep keep-alives alive, doesn't actually do anything - * * @access public - * @return bool + * @return boolean */ public function noop() { - return $this->sendCommand("NOOP", "NOOP", 250); + return $this->sendCommand('NOOP', 'NOOP', 250); } /** * Send an SMTP TURN command. * This is an optional command for SMTP that this class does not support. - * This method is here to make the RFC821 Definition - * complete for this class and __may__ be implemented in future + * This method is here to make the RFC821 Definition complete for this class + * and _may_ be implemented in future * Implements from rfc 821: TURN - * * @access public - * @return bool + * @return boolean */ public function turn() { - $this->error = array( - 'error' => 'The SMTP TURN command is not implemented' - ); - if ($this->do_debug >= 1) { - $this->edebug('SMTP NOTICE: ' . $this->error['error']); - } + $this->setError('The SMTP TURN command is not implemented'); + $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); return false; } /** * Send raw data to the server. - * * @param string $data The data to send * @access public - * @return int|bool The number of bytes sent to the server or false on error + * @return integer|boolean The number of bytes sent to the server or false on error */ public function client_send($data) { - if ($this->do_debug >= 1) { - $this->edebug("CLIENT -> SERVER: $data"); - } + $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); return fwrite($this->smtp_conn, $data); } /** * Get the latest error. - * * @access public * @return array */ @@ -850,9 +1004,59 @@ class SMTP return $this->error; } + /** + * Get SMTP extensions available on the server + * @access public + * @return array|null + */ + public function getServerExtList() + { + return $this->server_caps; + } + + /** + * A multipurpose method + * The method works in three ways, dependent on argument value and current state + * 1. HELO/EHLO was not sent - returns null and set up $this->error + * 2. HELO was sent + * $name = 'HELO': returns server name + * $name = 'EHLO': returns boolean false + * $name = any string: returns null and set up $this->error + * 3. EHLO was sent + * $name = 'HELO'|'EHLO': returns server name + * $name = any string: if extension $name exists, returns boolean True + * or its options. Otherwise returns boolean False + * In other words, one can use this method to detect 3 conditions: + * - null returned: handshake was not or we don't know about ext (refer to $this->error) + * - false returned: the requested feature exactly not exists + * - positive value returned: the requested feature exists + * @param string $name Name of SMTP extension or 'HELO'|'EHLO' + * @return mixed + */ + public function getServerExt($name) + { + if (!$this->server_caps) { + $this->setError('No HELO/EHLO was sent'); + return null; + } + + // the tight logic knot ;) + if (!array_key_exists($name, $this->server_caps)) { + if ($name == 'HELO') { + return $this->server_caps['EHLO']; + } + if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { + return false; + } + $this->setError('HELO handshake was used. Client knows nothing about server extensions'); + return null; + } + + return $this->server_caps[$name]; + } + /** * Get the last reply from the server. - * * @access public * @return string */ @@ -867,57 +1071,47 @@ class SMTP * With SMTP we can tell if we have more lines to read if the * 4th character is '-' symbol. If it is a space then we don't * need to read anything else. - * * @access protected * @return string */ protected function get_lines() { + // If the connection is bad, give up straight away + if (!is_resource($this->smtp_conn)) { + return ''; + } $data = ''; $endtime = 0; - // If the connection is bad, give up now - if (!is_resource($this->smtp_conn)) { - return $data; - } stream_set_timeout($this->smtp_conn, $this->Timeout); if ($this->Timelimit > 0) { $endtime = time() + $this->Timelimit; } while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { $str = @fgets($this->smtp_conn, 515); - if ($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); - $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); - } + $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); $data .= $str; - if ($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); - } - // if 4th character is a space, we are done reading, break the loop - if (substr($str, 3, 1) == ' ') { + // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen + if ((isset($str[3]) and $str[3] == ' ')) { break; } // Timed-out? Log and break $info = stream_get_meta_data($this->smtp_conn); if ($info['timed_out']) { - if ($this->do_debug >= 4) { - $this->edebug( - 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' - ); - } + $this->edebug( + 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', + self::DEBUG_LOWLEVEL + ); break; } // Now check if reads took too long - if ($endtime) { - if (time() > $endtime) { - if ($this->do_debug >= 4) { - $this->edebug( - 'SMTP -> get_lines(): timelimit reached (' - . $this->Timelimit . ' sec)' - ); - } - break; - } + if ($endtime and time() > $endtime) { + $this->edebug( + 'SMTP -> get_lines(): timelimit reached ('. + $this->Timelimit . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; } } return $data; @@ -925,8 +1119,7 @@ class SMTP /** * Enable or disable VERP address generation. - * - * @param bool $enabled + * @param boolean $enabled */ public function setVerp($enabled = false) { @@ -935,18 +1128,33 @@ class SMTP /** * Get VERP address generation mode. - * - * @return bool + * @return boolean */ public function getVerp() { return $this->do_verp; } + /** + * Set error messages and codes. + * @param string $message The error message + * @param string $detail Further detail on the error + * @param string $smtp_code An associated SMTP error code + * @param string $smtp_code_ex Extended SMTP code + */ + protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') + { + $this->error = array( + 'error' => $message, + 'detail' => $detail, + 'smtp_code' => $smtp_code, + 'smtp_code_ex' => $smtp_code_ex + ); + } + /** * Set debug output method. - * - * @param string $method The function/method to use for debugging output. + * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. */ public function setDebugOutput($method = 'echo') { @@ -955,7 +1163,6 @@ class SMTP /** * Get debug output method. - * * @return string */ public function getDebugOutput() @@ -965,8 +1172,7 @@ class SMTP /** * Set debug output level. - * - * @param int $level + * @param integer $level */ public function setDebugLevel($level = 0) { @@ -975,8 +1181,7 @@ class SMTP /** * Get debug output level. - * - * @return int + * @return integer */ public function getDebugLevel() { @@ -985,8 +1190,7 @@ class SMTP /** * Set SMTP timeout. - * - * @param int $timeout + * @param integer $timeout */ public function setTimeout($timeout = 0) { @@ -995,11 +1199,53 @@ class SMTP /** * Get SMTP timeout. - * - * @return int + * @return integer */ public function getTimeout() { return $this->Timeout; } + + /** + * Reports an error number and string. + * @param integer $errno The error number returned by PHP. + * @param string $errmsg The error message returned by PHP. + */ + protected function errorHandler($errno, $errmsg) + { + $notice = 'Connection: Failed to connect to server.'; + $this->setError( + $notice, + $errno, + $errmsg + ); + $this->edebug( + $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, + self::DEBUG_CONNECTION + ); + } + + /** + * Will return the ID of the last smtp transaction based on a list of patterns provided + * in SMTP::$smtp_transaction_id_patterns. + * If no reply has been received yet, it will return null. + * If no pattern has been matched, it will return false. + * @return bool|null|string + */ + public function getLastTransactionID() + { + $reply = $this->getLastReply(); + + if (empty($reply)) { + return null; + } + + foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { + if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { + return $matches[1]; + } + } + + return false; + } } diff --git a/inc/SP/Auth/Ldap/LdapBase.class.php b/inc/SP/Auth/Ldap/LdapBase.class.php index bb208182..3e12f7db 100644 --- a/inc/SP/Auth/Ldap/LdapBase.class.php +++ b/inc/SP/Auth/Ldap/LdapBase.class.php @@ -199,7 +199,7 @@ abstract class LdapBase implements LdapInterface, AuthInterface /** * Realizar la autentificación con el servidor de LDAP. * - * @param string $bindDn con el DN del usuario + * @param string $bindDn con el DN del usuario * @param string $bindPass con la clave del usuario * @throws SPException * @return bool @@ -463,7 +463,7 @@ abstract class LdapBase implements LdapInterface, AuthInterface 'groupmembership' => 'group', 'memberof' => 'group', 'displayname' => 'name', - 'fullname' => 'name', + 'fullname' => 'fullname', 'givenname' => 'name', 'sn' => 'sn', 'mail' => 'mail', @@ -492,15 +492,20 @@ abstract class LdapBase implements LdapInterface, AuthInterface $res[$validAttributes[$normalizedAttribute]] = $values; } else { // Almacenamos 1 solo valor - $res[$validAttributes[$normalizedAttribute]] = $values[0]; + $res[$validAttributes[$normalizedAttribute]] = trim($values[0]); } } } } } + if (!empty($res["fullname"])) { + $this->LdapAuthData->setName($res['fullname']); + } else { + $this->LdapAuthData->setName($res['name'] . ' ' . $res['sn']); + } + $this->LdapAuthData->setDn($searchResults[0]['dn']); - $this->LdapAuthData->setName(trim($res['name'] . ' ' . $res['sn'])); $this->LdapAuthData->setEmail($res['mail']); $this->LdapAuthData->setExpire($res['expire']); $this->LdapAuthData->setGroups($res['group']); diff --git a/inc/SP/Controller/ConfigActionController.class.php b/inc/SP/Controller/ConfigActionController.class.php index b7672c9e..472cc581 100644 --- a/inc/SP/Controller/ConfigActionController.class.php +++ b/inc/SP/Controller/ConfigActionController.class.php @@ -38,6 +38,7 @@ use SP\Core\Messages\LogMessage; use SP\Core\Messages\NoticeMessage; use SP\Core\Session; use SP\Core\XmlExport; +use SP\Html\Html; use SP\Http\Request; use SP\Import\Import; use SP\Import\ImportParams; diff --git a/inc/SP/Core/Messages/MessageBase.class.php b/inc/SP/Core/Messages/MessageBase.class.php index 3b5af1f1..1ad43ed4 100644 --- a/inc/SP/Core/Messages/MessageBase.class.php +++ b/inc/SP/Core/Messages/MessageBase.class.php @@ -36,9 +36,9 @@ abstract class MessageBase implements MessageInterface */ protected $title; /** - * @var string + * @var array */ - protected $footer; + protected $footer = []; /** * @var array */ @@ -93,9 +93,9 @@ abstract class MessageBase implements MessageInterface } /** - * @param string $footer + * @param array $footer */ - public function setFooter($footer) + public function setFooter(array $footer) { $this->footer = $footer; } diff --git a/inc/SP/Core/Messages/NoticeMessage.class.php b/inc/SP/Core/Messages/NoticeMessage.class.php index 8cd7330e..95fc3fc5 100644 --- a/inc/SP/Core/Messages/NoticeMessage.class.php +++ b/inc/SP/Core/Messages/NoticeMessage.class.php @@ -41,7 +41,7 @@ class NoticeMessage extends MessageBase $message[] = '
'; $message[] = '

' . $this->title . '

'; $message[] = '
' . implode('
', $this->description) . '
'; - $message[] = '
' . $this->footer . '
'; + $message[] = '
' . implode('
', $this->footer) . '
'; $message[] = '
'; return implode('', $message); @@ -54,10 +54,6 @@ class NoticeMessage extends MessageBase */ public function composeText() { - $message[] = $this->title; - $message[] = implode(PHP_EOL, $this->description); - $message[] = $this->footer; - - return implode(PHP_EOL, $message); + return $this->title . PHP_EOL . implode(PHP_EOL, $this->description) . PHP_EOL . implode(PHP_EOL, $this->footer); } } \ No newline at end of file diff --git a/inc/SP/Log/Email.class.php b/inc/SP/Log/Email.class.php index c715f9ab..ea975a2e 100644 --- a/inc/SP/Log/Email.class.php +++ b/inc/SP/Log/Email.class.php @@ -118,6 +118,7 @@ class Email $Mail = new PHPMailer(); + $Mail->SMTPAutoTLS = false; $Mail->isSMTP(); $Mail->CharSet = 'utf-8'; $Mail->Host = $mailServer; @@ -172,15 +173,19 @@ class Email return false; } - $Mail = self::getMailer($mailTo, $Message->getTitle()); - $Mail->isHTML(true); + $Mail = self::getMailer(Config::getConfig()->getMailFrom(), $Message->getTitle()); + $Mail->isHTML(); foreach ($mailTo as $recipient) { $Mail->addBCC($recipient->user_email, $recipient->user_name); } - $Mail->Body = $Message->composeHtml() . implode(Log::NEWLINE_HTML, self::getEmailFooter()); - $Mail->AltBody = $Message->composeText() . implode(Log::NEWLINE_TXT, self::getEmailFooter()); + if (empty($Message->getFooter())) { + $Message->setFooter(self::getEmailFooter()); + } + + $Mail->Body = $Message->composeHtml(); + $Mail->AltBody = $Message->composeText(); $LogMessage = new LogMessage(); $LogMessage->setAction(__('Enviar Email', false)); diff --git a/inc/SP/Mgmt/Users/UserUtil.class.php b/inc/SP/Mgmt/Users/UserUtil.class.php index bfed6ceb..6c06f834 100644 --- a/inc/SP/Mgmt/Users/UserUtil.class.php +++ b/inc/SP/Mgmt/Users/UserUtil.class.php @@ -162,6 +162,7 @@ class UserUtil LEFT JOIN usrToGroups ON usertogroup_userId = user_id WHERE user_email IS NOT NULL AND user_groupId = ? OR usertogroup_groupId = ? + AND user_isDisabled = 0 ORDER BY user_login'; $Data = new QueryData(); @@ -182,7 +183,7 @@ class UserUtil $query = /** @lang SQL */ 'SELECT user_id, user_login, user_name, user_email FROM usrData - WHERE user_email IS NOT NULL + WHERE user_email IS NOT NULL AND user_isDisabled = 0 ORDER BY user_login'; $Data = new QueryData(); diff --git a/inc/SP/Util/Util.class.php b/inc/SP/Util/Util.class.php index 5a3aff84..f9b7068b 100644 --- a/inc/SP/Util/Util.class.php +++ b/inc/SP/Util/Util.class.php @@ -344,6 +344,7 @@ class Util $appinfo = [ 'appname' => 'sysPass', 'appdesc' => 'Systems Password Manager', + 'appalias' => 'SGC', 'appwebsite' => 'http://www.syspass.org', 'appblog' => 'http://www.cygnux.org', 'appdoc' => 'http://wiki.syspass.org', diff --git a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css index a3acfa2c..1cb30710 100644 --- a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css +++ b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css @@ -32,7 +32,8 @@ line-height: 1; background-color: #607d8b; color: #eceff1; - padding: .5em; } + padding: .5em; + text-align: center; } .dialog-button-bar { text-align: right; diff --git a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css.map b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css.map index dfd8e64c..b99a2dde 100644 --- a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css.map +++ b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.css.map @@ -1,6 +1,6 @@ { "version": 3, -"mappings": "AAEA;kBACmB;EACjB,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,CAAC;EACN,KAAK,EAAE,CAAC;EACR,MAAM,EAAE,CAAC;EACT,IAAI,EAAE,CAAC;EACP,QAAQ,EAAE,MAAM;EAChB,UAAU,EAAE,kBAAkB;EAC9B,OAAO,EAAE,IAAI;EACb,OAAO,EAAE,CAAC;EACV,kBAAkB,EAAE,qBAAqB;EACzC,eAAe,EAAE,qBAAqB;EACtC,UAAU,EAAE,qBAAqB;;AAGnC,uBAAwB;EACtB,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,GAAG;EACV,SAAS,EAAE,KAAK;EAChB,UAAU,EAAE,IAAI;EAChB,MAAM,EAAE,QAAQ;EAChB,OAAO,EAAE,KAAK;EACd,cAAc,EAAE,GAAG;;AAGrB,2BAA4B;EAC1B,OAAO,EAAE,KAAK;;AAGhB,wBAAyB;EACvB,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,GAAG;EAChB,cAAc,EAAE,MAAM;EACtB,WAAW,EAAE,CAAC;EACd,gBAAgB,ECzBE,OAAO;ED0BzB,KAAK,EC3Ba,OAAO;ED4BzB,OAAO,EAAE,IAAI;;AAGf,kBAAmB;EACjB,UAAU,EAAE,KAAK;EACjB,UAAU,EAAE,GAAG;;AAGjB,wBAAyB;EACvB,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,QAAQ;EAChB,OAAO,EAAE,KAAK;;AAGhB,8BAA+B;EAC7B,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI", +"mappings": "AAEA;kBACmB;EACjB,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,CAAC;EACN,KAAK,EAAE,CAAC;EACR,MAAM,EAAE,CAAC;EACT,IAAI,EAAE,CAAC;EACP,QAAQ,EAAE,MAAM;EAChB,UAAU,EAAE,kBAAkB;EAC9B,OAAO,EAAE,IAAI;EACb,OAAO,EAAE,CAAC;EACV,kBAAkB,EAAE,qBAAqB;EACzC,eAAe,EAAE,qBAAqB;EACtC,UAAU,EAAE,qBAAqB;;AAGnC,uBAAwB;EACtB,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,GAAG;EACV,SAAS,EAAE,KAAK;EAChB,UAAU,EAAE,IAAI;EAChB,MAAM,EAAE,QAAQ;EAChB,OAAO,EAAE,KAAK;EACd,cAAc,EAAE,GAAG;;AAGrB,2BAA4B;EAC1B,OAAO,EAAE,KAAK;;AAGhB,wBAAyB;EACvB,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,GAAG;EAChB,cAAc,EAAE,MAAM;EACtB,WAAW,EAAE,CAAC;EACd,gBAAgB,ECzBE,OAAO;ED0BzB,KAAK,EC3Ba,OAAO;ED4BzB,OAAO,EAAE,IAAI;EACb,UAAU,EAAE,MAAM;;AAGpB,kBAAmB;EACjB,UAAU,EAAE,KAAK;EACjB,UAAU,EAAE,GAAG;;AAGjB,wBAAyB;EACvB,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,QAAQ;EAChB,OAAO,EAAE,KAAK;;AAGhB,8BAA+B;EAC7B,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI", "sources": ["mdl-jquery-modal-dialog.scss","_base.scss"], "names": [], "file": "mdl-jquery-modal-dialog.css" diff --git a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.min.css b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.min.css index 5b2a061c..d1a923b8 100644 --- a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.min.css +++ b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.min.css @@ -1 +1 @@ -.dialog-container,.loading-container{position:absolute;top:0;right:0;bottom:0;left:0;overflow:scroll;background:rgba(0,0,0,0.4);z-index:9999;opacity:0;-webkit-transition:opacity 400ms ease-in;-moz-transition:opacity 400ms ease-in;transition:opacity 400ms ease-in}.dialog-container>div{position:relative;width:90%;max-width:500px;min-height:25px;margin:10% auto;z-index:99999;padding-bottom:1em}.dialog-container>div>*{padding:0 1em}.dialog-container header{font-size:20px;font-weight:500;letter-spacing:.02em;line-height:1;background-color:#607d8b;color:#eceff1;padding:.5em}.dialog-button-bar{text-align:right;margin-top:8px}.loading-container>div{position:relative;width:50px;height:50px;margin:10% auto;z-index:99999}.loading-container>div>div{width:100%;height:100%} \ No newline at end of file +.dialog-container,.loading-container{position:absolute;top:0;right:0;bottom:0;left:0;overflow:scroll;background:rgba(0,0,0,0.4);z-index:9999;opacity:0;-webkit-transition:opacity 400ms ease-in;-moz-transition:opacity 400ms ease-in;transition:opacity 400ms ease-in}.dialog-container>div{position:relative;width:90%;max-width:500px;min-height:25px;margin:10% auto;z-index:99999;padding-bottom:1em}.dialog-container>div>*{padding:0 1em}.dialog-container header{font-size:20px;font-weight:500;letter-spacing:.02em;line-height:1;background-color:#607d8b;color:#eceff1;padding:.5em;text-align:center}.dialog-button-bar{text-align:right;margin-top:8px}.loading-container>div{position:relative;width:50px;height:50px;margin:10% auto;z-index:99999}.loading-container>div>div{width:100%;height:100%} \ No newline at end of file diff --git a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.scss b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.scss index 37717d79..4e71db42 100644 --- a/inc/themes/material-blue/css/mdl-jquery-modal-dialog.scss +++ b/inc/themes/material-blue/css/mdl-jquery-modal-dialog.scss @@ -38,6 +38,7 @@ background-color: $color-bluegrey-fg; color: $color-bluegrey-bg; padding: .5em; + text-align: center; } .dialog-button-bar { diff --git a/inc/themes/material-blue/css/styles.css b/inc/themes/material-blue/css/styles.css index b9b87583..2e3c936e 100644 --- a/inc/themes/material-blue/css/styles.css +++ b/inc/themes/material-blue/css/styles.css @@ -572,6 +572,11 @@ pre, code, samp, kbd { text-align: justify; line-height: 2em; } +#box-complexity > div { + text-align: justify; + line-height: 1.5em; + margin-top: 1em; } + #debug { float: left; text-align: left; } diff --git a/inc/themes/material-blue/css/styles.css.map b/inc/themes/material-blue/css/styles.css.map index 35646939..dde8cdf8 100644 --- a/inc/themes/material-blue/css/styles.css.map +++ b/inc/themes/material-blue/css/styles.css.map @@ -1,6 +1,6 @@ { "version": 3, -"mappings": "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", +"mappings": "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", "sources": ["_elements.scss","_base.scss","styles.scss","_login.scss"], "names": [], "file": "styles.css" diff --git a/inc/themes/material-blue/css/styles.min.css b/inc/themes/material-blue/css/styles.min.css index b584947b..83763f19 100644 --- a/inc/themes/material-blue/css/styles.min.css +++ b/inc/themes/material-blue/css/styles.min.css @@ -1 +1 @@ -html,body{margin:0;padding:0;text-align:left;background-color:#f5f5f5;color:#555;font-size:12px;font-weight:normal;box-sizing:border-box}*{font-family:"Roboto Regular",Verdana,Tahoma,sans-serif;box-sizing:inherit}*:before,*:after{box-sizing:inherit}table{font-size:11px;border-spacing:0}table th{border-bottom:2px solid transparent;vertical-align:middle}table th .icon{width:24px;height:24px}table tr{height:20px}table tr.odd{background-color:#f9f9f9}table tr.even>td,table tr.odd>td{border-bottom:1px solid #d9d9d9!important}table tr.even:hover,table tr.odd:hover{background-color:#e8ff99}table td{padding:3px}table td.txtCliente{font-weight:bold;text-align:center}form{font-size:11px;margin:0}input.inputImg,img.inputImg{background-color:transparent!important;width:24px!important;height:24px!important;border:0;vertical-align:middle;margin:0 .5em}input.txtFile{width:200px}input.txtLong{width:300px}textarea{width:350px;resize:none}select.files{width:250px}input.spinner{width:5em}img{margin:0;padding:0;border:0;cursor:pointer}img.inputImgMini{background-color:transparent!important;width:16px!important;height:16px!important;margin:0 5px 0 5px;border:0;vertical-align:middle}i{cursor:pointer}form .form-field{display:flex;justify-content:space-between}form .form-field>label{min-width:12em;padding:.5em 0;font-size:16px;align-self:center}form .form-field>div{width:100%;align-self:center}a{text-decoration:none;color:#536dfe}a:visited{text-decoration:none;color:#536dfe}a:hover,a:active,a:focus{text-decoration:none;cursor:pointer}pre,code,samp,kbd{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;font-size:1em;direction:ltr;text-align:left;background-color:#fbfaf9;color:#333;box-shadow:inset 0 0 .3em #ccc;border-radius:2px}#nojs{width:80%;text-align:center;vertical-align:middle;margin:10px auto;padding:3px;background-color:#ef5350;color:white;font-weight:bold;font-size:14px}#wrap{height:auto!important;min-height:100%;width:100%;background-color:#f5f5f5}#wrap-loading{position:fixed;z-index:9999;top:50%;left:50%;padding:1em;background-color:rgba(255,255,255,0.8);border-radius:5px;display:none}#wrap-loading.overlay-full{top:0;left:0;width:100%;height:100%;background-color:rgba(255,255,255,0.5)}#wrap-loading.overlay-full #loading{position:absolute;top:50%;left:50%}#container{margin:auto;width:100%}#container.login{padding-top:5%}#container.error,#container.install,#container.passreset{width:100%}#container .logo{height:64px}#container #actions-bar{z-index:100;display:flex;justify-content:space-between;position:fixed;border:0 none;top:0;left:0;width:100%;padding:1em 0;background-color:transparent}#container #actions-bar-icons{flex-grow:1;text-align:center}#container #actions-bar-logo{display:none;padding:0 .5em}#container #actions-bar-logo img{display:inline-block;width:50px;opacity:.75}#container #content{width:95%;margin:2em auto 8em auto}#container #content.public-link{width:70%;min-height:0;margin:5em auto}#content td.descField,#box-popup td.descField{text-align:right;padding-right:20px;width:25%;font-weight:bold;border-right:1px solid #d9d9d9;color:#555}#content td.valField,#box-popup td.valField{padding-left:1em;width:100%}#content .pager{width:100%;margin-top:15px;padding:.5em;vertical-align:middle;font-size:11px;color:#999;background-color:#fcfcfc}#content .pager img{margin-left:5px;vertical-align:middle}#content .pager a{margin-left:5px;font-size:12px;color:#999}#content .pager>div{display:inline-block;width:49%}#content .pager .pager-left{text-align:left}#content .pager .pager-right{text-align:right}#content #title{width:50%;padding:7px;margin:auto;background-color:#d9d9d9;color:#fff;font-size:17px;letter-spacing:.3em;text-align:center}#content #title.titleNormal{background-color:#607d8b;color:#fff}#content .data-container{width:75%;margin:0 auto}#content .data{width:100%;padding:10px;border:1px solid #c9c9c9;margin:0 auto;background-color:#f9f9f9}#content fieldset.data{margin:2em auto}#content fieldset.data>legend{color:#607d8b;padding:0 .5em;font-size:1.5em}#content fieldset.data>div{display:none}#content fieldset.data>div table{width:100%}#content .data #history-icon{position:relative;top:5em;right:2em}#content .data td{text-align:left}#content .data td.descField{text-align:right;font-size:12px;font-weight:bold;color:#999}#content .data select{min-width:210px}#content .data .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#box-popup .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#content .data .list-wrap ul,#box-popup .list-wrap ul{list-style-type:none;margin:0;padding:0}#content .data .list-wrap li,#box-popup .list-wrap li{display:flex;background:#f2f2f2;padding:.5em;font-size:1em;margin-bottom:.5em}#content .data .list-wrap li:hover,#box-popup .list-wrap li:hover{background:#e8eaf6;color:#000}#content .data .list-wrap div.files-item-info,#box-popup .list-wrap div.files-item-info{flex-grow:2}#content .data .list-wrap div.files-item-info img,#box-popup .list-wrap div.files-item-info img{margin:0 .5em}#content .data .list-wrap div.files-item-actions,#box-popup .list-wrap div.files-item-actions{padding:.3em 0}#content .data .dropzone{width:30em;padding:1em;border:2px dashed #26a69a;text-align:center}#content .data .dropzone img{vertical-align:middle}#content .data .file-upload{display:none}#content .data .account-permissions{width:100%}#content .data .account-permissions fieldset{border:1px solid #c9c9c9;padding:1em}#content .data .account-permissions legend{font-weight:bold;color:#999;padding:.2em 0}#content .data .account-permissions fieldset>span{font-weight:bold;color:#999;padding:.2em 0;display:inline-block;width:100px;text-align:right}#content span.tag{margin:0 3px 3px 0;padding:.2em;background:#5c6bc0;color:#fff;border:0 solid transparent;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}#content .extra-info{margin-top:20px}#content #tabs fieldset{border:1px solid #c9c9c9}#content #tabs #frmConfig label{float:left}#content .tblConfig{margin-bottom:2em}#content .tblConfig td.descField{width:35%;font-size:11px;font-weight:bold}#content .tblConfig td.rowHeader{padding:5px 0 5px 0;background-color:#f5f5f5;text-align:center;font-weight:bold;border-top:15px solid #f9f9f9;border-bottom:3px solid #a9c1d7;letter-spacing:.5em;color:#696969}#content .tblConfig input.checkbox{width:15px;text-align:left;padding:0}#content .tblConfig .option-disabled{text-align:center;background-color:#fff8e1;color:#ffca28;font-weight:bold}#content h2{width:100%;height:1.5em;font-size:18px;color:white;background-color:#a9c1d7;margin:0;padding-top:.1em}#content .section{margin-top:2.5em;border-bottom:1px solid #d9d9d9;text-align:left;font-size:14px;font-weight:bold;color:#5c6bc0}#content .row_even>td{background-color:#f5f5f5}#content .row_odd>td{background-color:white}#content .data-header ul{list-style:none;width:100%;margin:0 0 10px 0;padding:0}#content .data-header li{display:inline-block;padding:.2em .5em;font-weight:bold;letter-spacing:.2em;color:#fff;text-align:center}#content .data-header li a{color:#777}#content .data-header li img{float:right;width:24px;height:24px;vertical-align:middle}#content .data-header-minimal{border-bottom:1px solid #dfdfdf}#content .data-header-minimal ul{display:flex;flex-wrap:wrap;justify-content:flex-start;margin:0}#content .data-header-minimal li{display:inline-flex;min-width:10em;font-weight:normal;letter-spacing:normal}#content .data-header-minimal li a{color:#b9b9b9;padding:.3em .8em}#content .data-table{width:100%}#content .data-table td:first-of-type,#content .data-table th:first-of-type{width:5em}#content .data-table thead th{background-color:#607d8b;color:#fff}#content .data-table tbody td.cell-data{text-align:left}#content .data-table tbody td.cell-nodata{padding:0 .5em;text-align:left}#content .data-table tbody td.cell-actions{text-align:right}#content .data-table tbody td.cell-actions i{opacity:.5}#content .data-table tbody td.cell-actions i:hover{opacity:1}#content .data-rows ul{display:table;list-style:none;width:100%;margin:0 0 10px 0;padding:0;background-color:#fcfcfc}#content .data-rows li{float:left;display:block;padding:1em;color:#696969;text-align:center;min-height:2em}#content .data-rows li.cell-nodata{padding:1em 0;min-height:2em;text-align:left}#content .data-rows li.cell-actions{float:right;min-height:2em;padding:1em 0;text-align:left;background-color:#fcfcfc;width:15em}#content .data-rows li.cell-nodata img,#content .data-rows li.cell-actions img{width:24px;height:24px;margin:0 .5em}#content #resEventLog .data{width:100%}#content #resEventLog thead{text-align:center}#content #resEventLog tbody{width:100%;height:500px;overflow:auto}#content #resEventLog td{border-bottom:1px solid #d9d9d9}#content #resEventLog .cell{text-align:center}#content #resEventLog .cell-description{width:60%}#content #searchbox{background-color:#fcfcfc;vertical-align:middle;position:relative;height:auto;padding:.5em 1em;margin-bottom:2em}#content #searchbox form{display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;text-align:left}#content #searchbox .search-filters>*{margin:0 1em}#content #searchbox .search-filters .filter-buttons{display:inline-block}#content #searchbox .search-filters .filter-slider{width:10em}#content #searchbox .search-filters-tags{display:none;flex-grow:2}#content .btn-clear{opacity:.35;filter:alpha(opacity=35)}#content .btn-clear:hover{opacity:1;filter:alpha(opacity=100)}#content .actions-optional{display:none}#box-popup{min-width:25em;max-width:50em;margin:5em auto;padding:0;background-color:#fff}#box-popup.box-password-view{min-width:30em;max-width:35em}#box-popup>h2{width:100%;font-size:18px;color:#fff;background-color:#607d8b;margin:0 0 1em 0;padding:.5em 0;line-height:1em}#box-popup>table{width:100%;padding-bottom:1em}#box-popup select{width:220px}#box-popup #resFancyAccion{display:none}#box-popup #resCheck{display:inline-block;width:80%;height:4em;padding:1em 0}#box-popup.image{background-color:transparent;max-width:100%;margin:0 auto;border-radius:0}#box-popup.image img{width:auto;margin:0 auto}#box-popup.image>div.title{background-color:#607d8b;color:#fff;padding:.5em}#box-popup.help{min-height:100px;background-color:#f5f5f5}#box-popup.help p{font-size:14px;text-align:justify;line-height:2em}#debug{float:left;text-align:left}#debuginfo{width:100%;min-height:10em;padding:1em;background-color:#fff8e1;text-align:left;line-height:1.5em}#debuginfo H3{text-align:center}.popup-data{min-width:400px;border:0;text-align:left;margin:0 .5em}.popup-data .descField{min-width:100px;font-weight:bold}footer{display:flex;justify-content:space-between;position:fixed;bottom:0;z-index:100;width:100%;padding:.5em 0;background-color:#f5f5f5;color:#b9b9b9;font-size:1em;box-shadow:0 -8px 6px -6px #c9c9c9;-webkit-box-shadow:0 -8px 6px -6px #c9c9c9;-moz-box-shadow:0 -8px 6px -6px #c9c9c9;display:flex;justify-content:space-between}footer .footer-parts{display:flex;justify-content:space-between}footer #footer-left{width:50%;margin:0 1em}footer #footer-right{width:50%;margin:0 1em;justify-content:flex-end;text-align:right}footer #updates{min-width:10em;text-align:center;cursor:pointer}footer #status{margin:0 1em}footer #status>div{display:inline-block}footer #status .status-info{padding:.5em}footer #session{text-align:left;color:#999;font-size:.8em}footer a{color:#b9b9b9}footer a:visited{color:#b9b9b9}footer #project a:hover{color:#a9c1d7;border-bottom:1px solid #a9c1d7}footer #updates a:hover{color:#a9c1d7}footer img{border:0;width:16px;height:16px;vertical-align:middle}.round,.round5{border-radius:5px!important;-moz-border-radius:5px!important;-webkit-border-radius:5px!important}.midround{border-radius:0 0 10px 10px!important;-moz-border-radius:0 0 10px 10px!important;-webkit-border-radius:0 0 10px 10px!important}.midroundup{border-radius:10px 10px 0 0!important;-moz-border-radius:10px 10px 0 0!important;-webkit-border-radius:10px 10px 0 0!important}.fullround{border-radius:50%!important;-moz-border-radius:50%!important;-webkit-border-radius:50%!important}.iconMini{width:16px!important;height:16px!important;vertical-align:middle}#content .error{width:350px;padding:15px;margin:0 auto;text-align:center;font-size:16px;line-height:1.5em;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}.hide{display:none!important}.btn-checks{padding:5px;margin:.2em 0;width:30em;border-bottom:1px solid #c9c9c9}.shadow{-webkit-box-shadow:2px 2px 3px -3px #a9a9a9;-moz-box-shadow:2px 2px 3px -3px #a9a9a9;box-shadow:1px 1px 2px #d9d9d9}.noRes{width:60%;padding:15px;background-color:#f9f9f9;color:#a9a9a9;border:#c9c9c9 1px solid;margin:20px auto;text-align:center;font-size:16px}.header-grey{background-color:#607d8b;color:#fff;min-height:2em}.no-background{background:none!important}.action-in-box{padding:1em;text-align:right}.action-in-box ul{list-style:none;margin:0;padding:0}.tab-data{margin:2em auto 0;width:75%}.item-actions{margin:1em auto}.tab-actions{margin:2em 0}.item-actions>ul,.tab-actions>ul{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;list-style:none;margin:0;padding:0}.item-actions>ul>li,.tab-actions>ul>li{margin-left:.5em}.fullWidth{max-width:100%!important}.filter-on{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;padding:.3em 1em}.global-on{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;padding:.3em 1em}.opacity50{filter:alpha(opacity=50);opacity:.5}.custom-combobox{position:relative;display:inline-block}.custom-combobox input{width:80%}.custom-combobox-toggle{position:absolute;top:0;bottom:0;margin-left:-1px;padding:0;*height:1.7em;*top:.1em}.custom-combobox-input{margin:0;padding:.3em}.passLevel{width:20px;height:20px;display:inline-block;position:relative;top:2px}.passLevel.strongest{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strongest:hover{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strong{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.strong:hover{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.good{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.good:hover{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.weak{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}.passLevel.weak:hover{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}#alert #alert-text{margin:15px auto;font-size:14px;font-weight:bold}#alert #alert-pass{width:50%;padding:10px;margin:15px auto;border:1px solid #c9c9c9;color:#555;font-weight:bold}.dialog-text,.dialog-user-text,.dialog-pass-text{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;padding:.5em;text-align:center;min-width:200px}.dialog-user-text{border-bottom:#d9d9d9 1px solid;color:#a9a9a9}.dialog-pass-text{border:transparent 1px solid;letter-spacing:.2em}.dialog-buttons{text-align:center;padding:.5em;border-top:1px solid #c9c9c9;line-height:2.5em}.dialog-clip-copy{color:#26a69a;background-color:#e0f2f1}.help-box{background-color:#fff!important;color:#607d8b}.help-box>*{font-weight:bold}.help-text{text-align:justify;line-height:1.5em;margin-top:1em}.tooltip{width:300px;max-width:300px;background-color:#777;color:#fff;z-index:101}.cursor-pointer{cursor:pointer}.password-actions{display:inline-block;width:12em}.password-actions>span,.password-actions i{margin-right:.6em}.custom-input-color{width:3em;height:1em;display:inline-block}.account-pass-image{height:32px;width:auto}.select-box{min-width:20em}fieldset.warning{padding:8px;border-radius:5px;color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}fieldset.warning legend{color:#ef5350!important}fieldset.warning a{color:#ef5350!important;font-weight:bold}#actions{width:100%;line-height:2em;margin-bottom:5em}#actions #logo{display:flex;width:100%;margin-bottom:30px;color:#607d8b;align-items:center;background:url("../imgs/logo_full_bg.png") left no-repeat;background-size:auto 150px;height:150px}#actions #page-title{width:100%;color:#607d8b;text-align:center}#actions #page-title h1{font-weight:bold;font-size:24px;letter-spacing:3px}#actions ul.errors{max-width:40vw;margin:0 auto;list-style:none;font-size:14px;text-align:left}#actions ul.errors>li{margin:1.5em auto;border-radius:3px;padding:1em .5em}#actions ul.errors>li.msg-critical{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}#actions ul.errors>li.msg-warning{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;color:#555}#actions ul.errors>li.msg-ok{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#actions ul.errors>li>p.hint{color:#555;font-size:12px}#actions ul.errors>li>p.hint i{margin-right:.5em}#actions form{width:450px;margin:0 auto;text-align:left}#actions form fieldset{margin-bottom:2em}#actions form fieldset legend{width:100%;color:#fff;font-size:14px;font-weight:bold;text-align:center;background-color:#607d8b;margin:1em 0;letter-spacing:.2em;padding:.2em 0}#actions div.buttons{margin-top:2em;text-align:center}#whatsNewIcon{text-align:center}#whatsNewIcon img{width:64px;height:64px}#whatsNewIcon h2{display:inline-block;color:#555;font-size:16px}#whatsNew{width:500px;background-color:#fffde1;padding:2em;line-height:1.5em;font-size:16px;color:#555;border:1px solid #d9d9d9;margin:0 auto 3em;display:none}#whatsNew ul{padding:0;border:0}#whatsNew li{padding-left:37px;line-height:32px;list-style:none}.help-box{display:none}.center{text-align:center!important}.right{text-align:right!important}.left{text-align:left!important}#login-container{width:40em;margin:0 auto;background:transparent url("../imgs/logo_full_bg.png") no-repeat top left;background-size:auto 10em}#login-container #boxSpacer{height:11em;background-color:transparent}#login-container #boxLogin{position:relative;margin:0 auto;width:100%;min-height:14em;padding:1em;background-color:#fff}#login-container #boxLogin #boxData{height:100%;min-height:14em;text-align:left;background-color:transparent}#login-container #boxLogin #boxData i{margin-right:.5em;opacity:.5}#login-container #boxLogin #boxData .extra-hidden{display:none}#login-container #boxLogin #boxButton{position:absolute;top:2em;right:2em}#login-container #boxLogin #boxActions{width:100%;text-align:right}#login-container #boxLogin #boxActions a{color:#c9c9c9}#login-container #boxLogout{margin-top:4em;width:100%}#login-container #boxLogout>div{margin:0 auto;width:250px;font-size:14px;padding:.5em;text-align:center;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}#login-container #boxUpdated{width:350px;margin:3em auto;font-size:14px;text-align:center;padding:.5em;color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#login-container #demo-info{margin:3em auto;color:#c9c9c9;border-top:1px solid #d9d9d9;border-bottom:1px solid #d9d9d9;padding:.5em}#login-container #demo-info ul{display:flex;justify-content:space-around;list-style:none}#login-container #demo-info ul li span{margin:0 2em}@media screen and (max-width:1000px){#content #searchbox .search-text{margin:0 auto}#content #searchbox .search-filters>*{margin:.5em 0}#content #searchbox .search-filters .selectize-control{width:100%}#content .data-container{width:100%}#content .data-container #title{width:90%}#content .data-container .selectize-control{width:100%}footer{display:none;justify-content:space-between;flex-wrap:wrap}footer .footer-parts{justify-content:space-between;flex-wrap:wrap}footer #footer-left,footer #footer-right{width:100%}footer .footer-parts>div{width:100%;padding:.5em 0}} \ No newline at end of file +html,body{margin:0;padding:0;text-align:left;background-color:#f5f5f5;color:#555;font-size:12px;font-weight:normal;box-sizing:border-box}*{font-family:"Roboto Regular",Verdana,Tahoma,sans-serif;box-sizing:inherit}*:before,*:after{box-sizing:inherit}table{font-size:11px;border-spacing:0}table th{border-bottom:2px solid transparent;vertical-align:middle}table th .icon{width:24px;height:24px}table tr{height:20px}table tr.odd{background-color:#f9f9f9}table tr.even>td,table tr.odd>td{border-bottom:1px solid #d9d9d9!important}table tr.even:hover,table tr.odd:hover{background-color:#e8ff99}table td{padding:3px}table td.txtCliente{font-weight:bold;text-align:center}form{font-size:11px;margin:0}input.inputImg,img.inputImg{background-color:transparent!important;width:24px!important;height:24px!important;border:0;vertical-align:middle;margin:0 .5em}input.txtFile{width:200px}input.txtLong{width:300px}textarea{width:350px;resize:none}select.files{width:250px}input.spinner{width:5em}img{margin:0;padding:0;border:0;cursor:pointer}img.inputImgMini{background-color:transparent!important;width:16px!important;height:16px!important;margin:0 5px 0 5px;border:0;vertical-align:middle}i{cursor:pointer}form .form-field{display:flex;justify-content:space-between}form .form-field>label{min-width:12em;padding:.5em 0;font-size:16px;align-self:center}form .form-field>div{width:100%;align-self:center}a{text-decoration:none;color:#536dfe}a:visited{text-decoration:none;color:#536dfe}a:hover,a:active,a:focus{text-decoration:none;cursor:pointer}pre,code,samp,kbd{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;font-size:1em;direction:ltr;text-align:left;background-color:#fbfaf9;color:#333;box-shadow:inset 0 0 .3em #ccc;border-radius:2px}#nojs{width:80%;text-align:center;vertical-align:middle;margin:10px auto;padding:3px;background-color:#ef5350;color:white;font-weight:bold;font-size:14px}#wrap{height:auto!important;min-height:100%;width:100%;background-color:#f5f5f5}#wrap-loading{position:fixed;z-index:9999;top:50%;left:50%;padding:1em;background-color:rgba(255,255,255,0.8);border-radius:5px;display:none}#wrap-loading.overlay-full{top:0;left:0;width:100%;height:100%;background-color:rgba(255,255,255,0.5)}#wrap-loading.overlay-full #loading{position:absolute;top:50%;left:50%}#container{margin:auto;width:100%}#container.login{padding-top:5%}#container.error,#container.install,#container.passreset{width:100%}#container .logo{height:64px}#container #actions-bar{z-index:100;display:flex;justify-content:space-between;position:fixed;border:0 none;top:0;left:0;width:100%;padding:1em 0;background-color:transparent}#container #actions-bar-icons{flex-grow:1;text-align:center}#container #actions-bar-logo{display:none;padding:0 .5em}#container #actions-bar-logo img{display:inline-block;width:50px;opacity:.75}#container #content{width:95%;margin:2em auto 8em auto}#container #content.public-link{width:70%;min-height:0;margin:5em auto}#content td.descField,#box-popup td.descField{text-align:right;padding-right:20px;width:25%;font-weight:bold;border-right:1px solid #d9d9d9;color:#555}#content td.valField,#box-popup td.valField{padding-left:1em;width:100%}#content .pager{width:100%;margin-top:15px;padding:.5em;vertical-align:middle;font-size:11px;color:#999;background-color:#fcfcfc}#content .pager img{margin-left:5px;vertical-align:middle}#content .pager a{margin-left:5px;font-size:12px;color:#999}#content .pager>div{display:inline-block;width:49%}#content .pager .pager-left{text-align:left}#content .pager .pager-right{text-align:right}#content #title{width:50%;padding:7px;margin:auto;background-color:#d9d9d9;color:#fff;font-size:17px;letter-spacing:.3em;text-align:center}#content #title.titleNormal{background-color:#607d8b;color:#fff}#content .data-container{width:75%;margin:0 auto}#content .data{width:100%;padding:10px;border:1px solid #c9c9c9;margin:0 auto;background-color:#f9f9f9}#content fieldset.data{margin:2em auto}#content fieldset.data>legend{color:#607d8b;padding:0 .5em;font-size:1.5em}#content fieldset.data>div{display:none}#content fieldset.data>div table{width:100%}#content .data #history-icon{position:relative;top:5em;right:2em}#content .data td{text-align:left}#content .data td.descField{text-align:right;font-size:12px;font-weight:bold;color:#999}#content .data select{min-width:210px}#content .data .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#box-popup .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#content .data .list-wrap ul,#box-popup .list-wrap ul{list-style-type:none;margin:0;padding:0}#content .data .list-wrap li,#box-popup .list-wrap li{display:flex;background:#f2f2f2;padding:.5em;font-size:1em;margin-bottom:.5em}#content .data .list-wrap li:hover,#box-popup .list-wrap li:hover{background:#e8eaf6;color:#000}#content .data .list-wrap div.files-item-info,#box-popup .list-wrap div.files-item-info{flex-grow:2}#content .data .list-wrap div.files-item-info img,#box-popup .list-wrap div.files-item-info img{margin:0 .5em}#content .data .list-wrap div.files-item-actions,#box-popup .list-wrap div.files-item-actions{padding:.3em 0}#content .data .dropzone{width:30em;padding:1em;border:2px dashed #26a69a;text-align:center}#content .data .dropzone img{vertical-align:middle}#content .data .file-upload{display:none}#content .data .account-permissions{width:100%}#content .data .account-permissions fieldset{border:1px solid #c9c9c9;padding:1em}#content .data .account-permissions legend{font-weight:bold;color:#999;padding:.2em 0}#content .data .account-permissions fieldset>span{font-weight:bold;color:#999;padding:.2em 0;display:inline-block;width:100px;text-align:right}#content span.tag{margin:0 3px 3px 0;padding:.2em;background:#5c6bc0;color:#fff;border:0 solid transparent;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}#content .extra-info{margin-top:20px}#content #tabs fieldset{border:1px solid #c9c9c9}#content #tabs #frmConfig label{float:left}#content .tblConfig{margin-bottom:2em}#content .tblConfig td.descField{width:35%;font-size:11px;font-weight:bold}#content .tblConfig td.rowHeader{padding:5px 0 5px 0;background-color:#f5f5f5;text-align:center;font-weight:bold;border-top:15px solid #f9f9f9;border-bottom:3px solid #a9c1d7;letter-spacing:.5em;color:#696969}#content .tblConfig input.checkbox{width:15px;text-align:left;padding:0}#content .tblConfig .option-disabled{text-align:center;background-color:#fff8e1;color:#ffca28;font-weight:bold}#content h2{width:100%;height:1.5em;font-size:18px;color:white;background-color:#a9c1d7;margin:0;padding-top:.1em}#content .section{margin-top:2.5em;border-bottom:1px solid #d9d9d9;text-align:left;font-size:14px;font-weight:bold;color:#5c6bc0}#content .row_even>td{background-color:#f5f5f5}#content .row_odd>td{background-color:white}#content .data-header ul{list-style:none;width:100%;margin:0 0 10px 0;padding:0}#content .data-header li{display:inline-block;padding:.2em .5em;font-weight:bold;letter-spacing:.2em;color:#fff;text-align:center}#content .data-header li a{color:#777}#content .data-header li img{float:right;width:24px;height:24px;vertical-align:middle}#content .data-header-minimal{border-bottom:1px solid #dfdfdf}#content .data-header-minimal ul{display:flex;flex-wrap:wrap;justify-content:flex-start;margin:0}#content .data-header-minimal li{display:inline-flex;min-width:10em;font-weight:normal;letter-spacing:normal}#content .data-header-minimal li a{color:#b9b9b9;padding:.3em .8em}#content .data-table{width:100%}#content .data-table td:first-of-type,#content .data-table th:first-of-type{width:5em}#content .data-table thead th{background-color:#607d8b;color:#fff}#content .data-table tbody td.cell-data{text-align:left}#content .data-table tbody td.cell-nodata{padding:0 .5em;text-align:left}#content .data-table tbody td.cell-actions{text-align:right}#content .data-table tbody td.cell-actions i{opacity:.5}#content .data-table tbody td.cell-actions i:hover{opacity:1}#content .data-rows ul{display:table;list-style:none;width:100%;margin:0 0 10px 0;padding:0;background-color:#fcfcfc}#content .data-rows li{float:left;display:block;padding:1em;color:#696969;text-align:center;min-height:2em}#content .data-rows li.cell-nodata{padding:1em 0;min-height:2em;text-align:left}#content .data-rows li.cell-actions{float:right;min-height:2em;padding:1em 0;text-align:left;background-color:#fcfcfc;width:15em}#content .data-rows li.cell-nodata img,#content .data-rows li.cell-actions img{width:24px;height:24px;margin:0 .5em}#content #resEventLog .data{width:100%}#content #resEventLog thead{text-align:center}#content #resEventLog tbody{width:100%;height:500px;overflow:auto}#content #resEventLog td{border-bottom:1px solid #d9d9d9}#content #resEventLog .cell{text-align:center}#content #resEventLog .cell-description{width:60%}#content #searchbox{background-color:#fcfcfc;vertical-align:middle;position:relative;height:auto;padding:.5em 1em;margin-bottom:2em}#content #searchbox form{display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;text-align:left}#content #searchbox .search-filters>*{margin:0 1em}#content #searchbox .search-filters .filter-buttons{display:inline-block}#content #searchbox .search-filters .filter-slider{width:10em}#content #searchbox .search-filters-tags{display:none;flex-grow:2}#content .btn-clear{opacity:.35;filter:alpha(opacity=35)}#content .btn-clear:hover{opacity:1;filter:alpha(opacity=100)}#content .actions-optional{display:none}#box-popup{min-width:25em;max-width:50em;margin:5em auto;padding:0;background-color:#fff}#box-popup.box-password-view{min-width:30em;max-width:35em}#box-popup>h2{width:100%;font-size:18px;color:#fff;background-color:#607d8b;margin:0 0 1em 0;padding:.5em 0;line-height:1em}#box-popup>table{width:100%;padding-bottom:1em}#box-popup select{width:220px}#box-popup #resFancyAccion{display:none}#box-popup #resCheck{display:inline-block;width:80%;height:4em;padding:1em 0}#box-popup.image{background-color:transparent;max-width:100%;margin:0 auto;border-radius:0}#box-popup.image img{width:auto;margin:0 auto}#box-popup.image>div.title{background-color:#607d8b;color:#fff;padding:.5em}#box-popup.help{min-height:100px;background-color:#f5f5f5}#box-popup.help p{font-size:14px;text-align:justify;line-height:2em}#box-complexity>div{text-align:justify;line-height:1.5em;margin-top:1em}#debug{float:left;text-align:left}#debuginfo{width:100%;min-height:10em;padding:1em;background-color:#fff8e1;text-align:left;line-height:1.5em}#debuginfo H3{text-align:center}.popup-data{min-width:400px;border:0;text-align:left;margin:0 .5em}.popup-data .descField{min-width:100px;font-weight:bold}footer{display:flex;justify-content:space-between;position:fixed;bottom:0;z-index:100;width:100%;padding:.5em 0;background-color:#f5f5f5;color:#b9b9b9;font-size:1em;box-shadow:0 -8px 6px -6px #c9c9c9;-webkit-box-shadow:0 -8px 6px -6px #c9c9c9;-moz-box-shadow:0 -8px 6px -6px #c9c9c9;display:flex;justify-content:space-between}footer .footer-parts{display:flex;justify-content:space-between}footer #footer-left{width:50%;margin:0 1em}footer #footer-right{width:50%;margin:0 1em;justify-content:flex-end;text-align:right}footer #updates{min-width:10em;text-align:center;cursor:pointer}footer #status{margin:0 1em}footer #status>div{display:inline-block}footer #status .status-info{padding:.5em}footer #session{text-align:left;color:#999;font-size:.8em}footer a{color:#b9b9b9}footer a:visited{color:#b9b9b9}footer #project a:hover{color:#a9c1d7;border-bottom:1px solid #a9c1d7}footer #updates a:hover{color:#a9c1d7}footer img{border:0;width:16px;height:16px;vertical-align:middle}.round,.round5{border-radius:5px!important;-moz-border-radius:5px!important;-webkit-border-radius:5px!important}.midround{border-radius:0 0 10px 10px!important;-moz-border-radius:0 0 10px 10px!important;-webkit-border-radius:0 0 10px 10px!important}.midroundup{border-radius:10px 10px 0 0!important;-moz-border-radius:10px 10px 0 0!important;-webkit-border-radius:10px 10px 0 0!important}.fullround{border-radius:50%!important;-moz-border-radius:50%!important;-webkit-border-radius:50%!important}.iconMini{width:16px!important;height:16px!important;vertical-align:middle}#content .error{width:350px;padding:15px;margin:0 auto;text-align:center;font-size:16px;line-height:1.5em;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}.hide{display:none!important}.btn-checks{padding:5px;margin:.2em 0;width:30em;border-bottom:1px solid #c9c9c9}.shadow{-webkit-box-shadow:2px 2px 3px -3px #a9a9a9;-moz-box-shadow:2px 2px 3px -3px #a9a9a9;box-shadow:1px 1px 2px #d9d9d9}.noRes{width:60%;padding:15px;background-color:#f9f9f9;color:#a9a9a9;border:#c9c9c9 1px solid;margin:20px auto;text-align:center;font-size:16px}.header-grey{background-color:#607d8b;color:#fff;min-height:2em}.no-background{background:none!important}.action-in-box{padding:1em;text-align:right}.action-in-box ul{list-style:none;margin:0;padding:0}.tab-data{margin:2em auto 0;width:75%}.item-actions{margin:1em auto}.tab-actions{margin:2em 0}.item-actions>ul,.tab-actions>ul{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;list-style:none;margin:0;padding:0}.item-actions>ul>li,.tab-actions>ul>li{margin-left:.5em}.fullWidth{max-width:100%!important}.filter-on{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;padding:.3em 1em}.global-on{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;padding:.3em 1em}.opacity50{filter:alpha(opacity=50);opacity:.5}.custom-combobox{position:relative;display:inline-block}.custom-combobox input{width:80%}.custom-combobox-toggle{position:absolute;top:0;bottom:0;margin-left:-1px;padding:0;*height:1.7em;*top:.1em}.custom-combobox-input{margin:0;padding:.3em}.passLevel{width:20px;height:20px;display:inline-block;position:relative;top:2px}.passLevel.strongest{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strongest:hover{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strong{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.strong:hover{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.good{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.good:hover{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.weak{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}.passLevel.weak:hover{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}#alert #alert-text{margin:15px auto;font-size:14px;font-weight:bold}#alert #alert-pass{width:50%;padding:10px;margin:15px auto;border:1px solid #c9c9c9;color:#555;font-weight:bold}.dialog-text,.dialog-user-text,.dialog-pass-text{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;padding:.5em;text-align:center;min-width:200px}.dialog-user-text{border-bottom:#d9d9d9 1px solid;color:#a9a9a9}.dialog-pass-text{border:transparent 1px solid;letter-spacing:.2em}.dialog-buttons{text-align:center;padding:.5em;border-top:1px solid #c9c9c9;line-height:2.5em}.dialog-clip-copy{color:#26a69a;background-color:#e0f2f1}.help-box{background-color:#fff!important;color:#607d8b}.help-box>*{font-weight:bold}.help-text{text-align:justify;line-height:1.5em;margin-top:1em}.tooltip{width:300px;max-width:300px;background-color:#777;color:#fff;z-index:101}.cursor-pointer{cursor:pointer}.password-actions{display:inline-block;width:12em}.password-actions>span,.password-actions i{margin-right:.6em}.custom-input-color{width:3em;height:1em;display:inline-block}.account-pass-image{height:32px;width:auto}.select-box{min-width:20em}fieldset.warning{padding:8px;border-radius:5px;color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}fieldset.warning legend{color:#ef5350!important}fieldset.warning a{color:#ef5350!important;font-weight:bold}#actions{width:100%;line-height:2em;margin-bottom:5em}#actions #logo{display:flex;width:100%;margin-bottom:30px;color:#607d8b;align-items:center;background:url("../imgs/logo_full_bg.png") left no-repeat;background-size:auto 150px;height:150px}#actions #page-title{width:100%;color:#607d8b;text-align:center}#actions #page-title h1{font-weight:bold;font-size:24px;letter-spacing:3px}#actions ul.errors{max-width:40vw;margin:0 auto;list-style:none;font-size:14px;text-align:left}#actions ul.errors>li{margin:1.5em auto;border-radius:3px;padding:1em .5em}#actions ul.errors>li.msg-critical{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}#actions ul.errors>li.msg-warning{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;color:#555}#actions ul.errors>li.msg-ok{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#actions ul.errors>li>p.hint{color:#555;font-size:12px}#actions ul.errors>li>p.hint i{margin-right:.5em}#actions form{width:450px;margin:0 auto;text-align:left}#actions form fieldset{margin-bottom:2em}#actions form fieldset legend{width:100%;color:#fff;font-size:14px;font-weight:bold;text-align:center;background-color:#607d8b;margin:1em 0;letter-spacing:.2em;padding:.2em 0}#actions div.buttons{margin-top:2em;text-align:center}#whatsNewIcon{text-align:center}#whatsNewIcon img{width:64px;height:64px}#whatsNewIcon h2{display:inline-block;color:#555;font-size:16px}#whatsNew{width:500px;background-color:#fffde1;padding:2em;line-height:1.5em;font-size:16px;color:#555;border:1px solid #d9d9d9;margin:0 auto 3em;display:none}#whatsNew ul{padding:0;border:0}#whatsNew li{padding-left:37px;line-height:32px;list-style:none}.help-box{display:none}.center{text-align:center!important}.right{text-align:right!important}.left{text-align:left!important}#login-container{width:40em;margin:0 auto;background:transparent url("../imgs/logo_full_bg.png") no-repeat top left;background-size:auto 10em}#login-container #boxSpacer{height:11em;background-color:transparent}#login-container #boxLogin{position:relative;margin:0 auto;width:100%;min-height:14em;padding:1em;background-color:#fff}#login-container #boxLogin #boxData{height:100%;min-height:14em;text-align:left;background-color:transparent}#login-container #boxLogin #boxData i{margin-right:.5em;opacity:.5}#login-container #boxLogin #boxData .extra-hidden{display:none}#login-container #boxLogin #boxButton{position:absolute;top:2em;right:2em}#login-container #boxLogin #boxActions{width:100%;text-align:right}#login-container #boxLogin #boxActions a{color:#c9c9c9}#login-container #boxLogout{margin-top:4em;width:100%}#login-container #boxLogout>div{margin:0 auto;width:250px;font-size:14px;padding:.5em;text-align:center;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}#login-container #boxUpdated{width:350px;margin:3em auto;font-size:14px;text-align:center;padding:.5em;color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#login-container #demo-info{margin:3em auto;color:#c9c9c9;border-top:1px solid #d9d9d9;border-bottom:1px solid #d9d9d9;padding:.5em}#login-container #demo-info ul{display:flex;justify-content:space-around;list-style:none}#login-container #demo-info ul li span{margin:0 2em}@media screen and (max-width:1000px){#content #searchbox .search-text{margin:0 auto}#content #searchbox .search-filters>*{margin:.5em 0}#content #searchbox .search-filters .selectize-control{width:100%}#content .data-container{width:100%}#content .data-container #title{width:90%}#content .data-container .selectize-control{width:100%}footer{display:none;justify-content:space-between;flex-wrap:wrap}footer .footer-parts{justify-content:space-between;flex-wrap:wrap}footer #footer-left,footer #footer-right{width:100%}footer .footer-parts>div{width:100%;padding:.5em 0}} \ No newline at end of file diff --git a/inc/themes/material-blue/css/styles.scss b/inc/themes/material-blue/css/styles.scss index 9a3cf8a2..33fe7bbe 100644 --- a/inc/themes/material-blue/css/styles.scss +++ b/inc/themes/material-blue/css/styles.scss @@ -597,6 +597,14 @@ } } +#box-complexity { + & > div { + text-align: justify; + line-height: 1.5em; + margin-top: 1em; + } +} + #debug { float: left; text-align: left; diff --git a/inc/themes/material-blue/js/app-theme.js b/inc/themes/material-blue/js/app-theme.js index 4707d619..c013712d 100644 --- a/inc/themes/material-blue/js/app-theme.js +++ b/inc/themes/material-blue/js/app-theme.js @@ -128,63 +128,53 @@ sysPass.Theme = function (Common) { }; + // FIXME // Diálogo de configuración de complejidad de clave var complexityDialog = function () { - $("
").dialog({ - modal: true, + + var content = + "
" + + "" + + "" + + "" + + "
" + + "" + + "" + + "
"; + + showDialog({ title: Common.config().LANG[29], - width: "400px", - open: function () { - var thisDialog = $(this); - - var content = - "" + - "" + - "" + - "
" + - "" + - "" + - "
" + - ""; - - thisDialog.html(content); - - // Recentrar después de insertar el contenido - thisDialog.dialog("option", "position", "center"); - - - // Actualizar componentes de MDL - thisDialog.ready(function () { - $("#checkbox-numbers").prop("checked", Common.passwordData.complexity.numbers); - $("#checkbox-uppercase").prop("checked", Common.passwordData.complexity.uppercase); - $("#checkbox-symbols").prop("checked", Common.passwordData.complexity.symbols); - $("#passlength").val(Common.passwordData.complexity.numlength); - - $("#btn-complexity").click(function () { - Common.passwordData.complexity.numbers = $(" #checkbox-numbers").is(":checked"); - Common.passwordData.complexity.uppercase = $("#checkbox-uppercase").is(":checked"); - Common.passwordData.complexity.symbols = $("#checkbox-symbols").is(":checked"); - Common.passwordData.complexity.numlength = parseInt($("#passlength").val()); - - thisDialog.dialog("close"); - }); - - // Actualizar objetos de MDL - componentHandler.upgradeDom(); - }); + text: content, + negative: { + title: Common.config().LANG[44] }, - // Forzar la eliminación del objeto para que ZeroClipboard siga funcionando al abrirlo de nuevo - close: function () { - $(this).dialog("destroy"); + positive: { + title: Common.config().LANG[43], + onClick: function (e) { + e.preventDefault(); + + Common.passwordData.complexity.numbers = $("#checkbox-numbers").is(":checked"); + Common.passwordData.complexity.uppercase = $("#checkbox-uppercase").is(":checked"); + Common.passwordData.complexity.symbols = $("#checkbox-symbols").is(":checked"); + Common.passwordData.complexity.numlength = parseInt($("#passlength").val()); + } + }, + cancelable: true, + contentStyle: {'max-width': '300px'}, + onLoaded: function () { + $("#checkbox-numbers").prop("checked", Common.passwordData.complexity.numbers); + $("#checkbox-uppercase").prop("checked", Common.passwordData.complexity.uppercase); + $("#checkbox-symbols").prop("checked", Common.passwordData.complexity.symbols); + $("#passlength").val(Common.passwordData.complexity.numlength); } }); }; diff --git a/inc/themes/material-blue/js/app-theme.min.js b/inc/themes/material-blue/js/app-theme.min.js index cb9cc1bc..4da4b534 100644 --- a/inc/themes/material-blue/js/app-theme.min.js +++ b/inc/themes/material-blue/js/app-theme.min.js @@ -1,18 +1,18 @@ -var $jscomp={scope:{},findInternal:function(a,f,c){a instanceof String&&(a=String(a));for(var g=a.length,k=0;k=e||58<=e&&64>=e||91<=e&&96>=e||123<=e&&126>=e)||!a.passwordData.complexity.numbers&&48<=e&&57>=e||!a.passwordData.complexity.uppercase&&65<=e&&90>=e||(l++,d+=String.fromCharCode(e));$("#viewPass").attr("title",d);var c=zxcvbn(d);a.passwordData.passLength=d.length;b?(l=b.parent(),e=$("#"+b.attr("id")+"R"),a.outputResult(c,b),b=new MaterialTextfield,l.find("input:password").val(d),l.addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID), -e.val(d).parent().addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID),a.encryptFormValue(e),l.find("#passLevel").show(500)):(a.outputResult(c),$("input:password, input.password").val(d),$("#passLevel").show(500))},k=function(){$("
").dialog({modal:!0,title:a.config().LANG[29],width:"400px",open:function(){var b=$(this),l='