diff --git a/config_override.php b/config_override.php index b0c374e6fe369bbe1168b1699dd2b5efcc1d09ca..342360724cc4b99ca1121edc0eac44fa141be26f 100644 --- a/config_override.php +++ b/config_override.php @@ -10,3 +10,14 @@ //Maximum number of Mailboxes in mail converter $max_mailboxes = 3; + +/** + * Configure runtime connectors to customization in core files. + * Ex: Sessions are currently handled by PHP default session handler. + * This can be customized using runtime connector hook and avoid core file modifications. + * array('session' => 'Vtiger_CustomSession_Handler') + */ +$runtime_connectors = array(); + +//Password Regex for validation +$validation_regex = array('password_regex' => '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})'); \ No newline at end of file diff --git a/cron/class.phpmailer.php b/cron/class.phpmailer.php deleted file mode 100755 index fba70242a465b0feb5fd5238f1fdb3143d41f766..0000000000000000000000000000000000000000 --- a/cron/class.phpmailer.php +++ /dev/null @@ -1,4061 +0,0 @@ -<?php -/** - * PHPMailer - PHP email creation and transport class. - * PHP Version 5 - * @package PHPMailer - * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project - * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> - * @author Jim Jagielski (jimjag) <jimjag@gmail.com> - * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> - * @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. - */ - -/** - * PHPMailer - PHP email creation and transport class. - * @package PHPMailer - * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> - * @author Jim Jagielski (jimjag) <jimjag@gmail.com> - * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> - * @author Brent R. Matzelle (original founder) - */ -class PHPMailer -{ - /** - * The PHPMailer Version number. - * @var string - */ - public $Version = '5.2.27'; - - /** - * Email priority. - * Options: null (default), 1 = High, 3 = Normal, 5 = low. - * When null, the header is not set at all. - * @var integer - */ - public $Priority = null; - - /** - * The character set of the message. - * @var string - */ - public $CharSet = 'UTF-8'; - - /** - * The MIME Content-type of the message. - * @var string - */ - public $ContentType = 'text/plain'; - - /** - * The message encoding. - * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". - * @var string - */ - public $Encoding = '8bit'; - - /** - * Holds the most recent mailer error message. - * @var string - */ - public $ErrorInfo = ''; - - /** - * The From email address for the message. - * @var string - */ - public $From = 'root@localhost'; - - /** - * The From name of the message. - * @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. - * @var string - */ - public $Sender = ''; - - /** - * The Return-Path of the message. - * If empty, it will be set to either From or Sender. - * @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. - * @var string - */ - public $Subject = ''; - - /** - * An HTML or plain text message body. - * If HTML then call isHTML(true). - * @var string - */ - public $Body = ''; - - /** - * The plain-text message body. - * 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. - * @var string - */ - public $AltBody = ''; - - /** - * 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/ - * @var string - */ - public $Ical = ''; - - /** - * The complete compiled MIME message body. - * @access protected - * @var string - */ - protected $MIMEBody = ''; - - /** - * The complete compiled MIME message headers. - * @var string - * @access protected - */ - protected $MIMEHeader = ''; - - /** - * Extra headers that createHeader() doesn't fold in. - * @var string - * @access protected - */ - protected $mailHeader = ''; - - /** - * Word-wrap the message body to this number of chars. - * 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". - * @var string - */ - public $Mailer = 'mail'; - - /** - * The path to the sendmail program. - * @var string - */ - public $Sendmail = '/usr/sbin/sendmail'; - - /** - * Whether mail() uses a fully sendmail-compatible MTA. - * One which supports sendmail's "-oi -f" options. - * @var boolean - */ - public $UseSendmailOptions = true; - - /** - * Path to PHPMailer plugins. - * Useful if the SMTP class is not in the PHP include path. - * @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, also known as read receipt. - * @var string - */ - public $ConfirmReadingTo = ''; - - /** - * 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. - * If empty, a unique id will be generated. - * You can set your own, but it must be in the format "<id@domain>", - * 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. - * @var string - */ - public $MessageDate = ''; - - /** - * SMTP hosts. - * Either a single hostname or multiple semicolon-delimited hostnames. - * 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. - * @var string - */ - public $Host = 'localhost'; - - /** - * The default SMTP server port. - * @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. If $Hostname is empty, PHPMailer attempts to find - * one with the same method described above for $Hostname. - * @var string - * @see PHPMailer::$Hostname - */ - public $Helo = ''; - - /** - * 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. - * @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. - * @var string - */ - public $Username = ''; - - /** - * SMTP password. - * @var string - */ - public $Password = ''; - - /** - * SMTP auth type. - * 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 - * @var string - */ - public $Realm = ''; - - /** - * SMTP workstation. - * Used for NTLM auth - * @var string - */ - public $Workstation = ''; - - /** - * The SMTP server timeout in seconds. - * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 - * @var integer - */ - public $Timeout = 300; - - /** - * SMTP class debug output mode. - * 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; - - /** - * How to handle debug output. - * Options: - * * `echo` Output plain-text as-is, appropriate for CLI - * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output - * * `error_log` Output to error log as configured in php.ini - * - * Alternatively, you can provide a callable expecting two params: a message string and the debug level: - * <code> - * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; - * </code> - * @var string|callable - * @see SMTP::$Debugoutput - */ - 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(). - * @var boolean - */ - public $SMTPKeepAlive = false; - - /** - * Whether to split multiple to addresses into multiple messages - * or send them all in one message. - * Only supported in `mail` and `sendmail` transports, not in SMTP. - * @var boolean - */ - public $SingleTo = false; - - /** - * Storage for addresses when SingleTo is enabled. - * @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 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. - * @var boolean - */ - public $AllowEmpty = false; - - /** - * The default line ending. - * @note The default remains "\n". We force CRLF where we know - * it must be used via self::CRLF. - * @var string - */ - public $LE = "\n"; - - /** - * DKIM selector. - * @var string - */ - public $DKIM_selector = ''; - - /** - * DKIM Identity. - * Usually the email address used as the source of the email. - * @var string - */ - public $DKIM_identity = ''; - - /** - * DKIM passphrase. - * Used if your key is encrypted. - * @var string - */ - public $DKIM_passphrase = ''; - - /** - * DKIM signing domain name. - * @example 'example.com' - * @var string - */ - public $DKIM_domain = ''; - - /** - * DKIM private key file path. - * @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. - * - * The function that handles the result of the send email action. - * It is called out by send() for each email sent. - * - * Value can be any php callable: http://www.php.net/is_callable - * - * Parameters: - * boolean $result result of the send action - * array $to email addresses of the recipients - * array $cc cc email addresses - * array $bcc bcc email addresses - * string $subject the subject - * string $body the email body - * string $from email address of sender - * @var string - */ - public $action_function = ''; - - /** - * 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. - * @var SMTP - * @access protected - */ - protected $smtp = null; - - /** - * The array of 'to' names and addresses. - * @var array - * @access protected - */ - protected $to = array(); - - /** - * The array of 'cc' names and addresses. - * @var array - * @access protected - */ - protected $cc = array(); - - /** - * The array of 'bcc' names and addresses. - * @var array - * @access protected - */ - protected $bcc = array(); - - /** - * The array of reply-to names and addresses. - * @var array - * @access protected - */ - protected $ReplyTo = array(); - - /** - * An array of all kinds of addresses. - * 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. - * @var array - * @access protected - */ - protected $attachment = array(); - - /** - * The array of custom headers. - * @var array - * @access protected - */ - protected $CustomHeader = array(); - - /** - * The most recent Message-ID (including angular brackets). - * @var string - * @access protected - */ - protected $lastMessageID = ''; - - /** - * The message's MIME type. - * @var string - * @access protected - */ - protected $message_type = ''; - - /** - * The array of MIME boundary strings. - * @var array - * @access protected - */ - protected $boundary = array(); - - /** - * The array of available languages. - * @var array - * @access protected - */ - protected $language = array(); - - /** - * The number of errors encountered. - * @var integer - * @access protected - */ - protected $error_count = 0; - - /** - * The S/MIME certificate file path. - * @var string - * @access protected - */ - protected $sign_cert_file = ''; - - /** - * The S/MIME key file path. - * @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. - * @var string - * @access protected - */ - protected $sign_key_pass = ''; - - /** - * Whether to throw exceptions for errors. - * @var boolean - * @access protected - */ - protected $exceptions = false; - - /** - * 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. - */ - const STOP_CONTINUE = 1; - - /** - * Error severity: message, plus full stop, critical error reached. - */ - const STOP_CRITICAL = 2; - - /** - * SMTP RFC standard line ending. - */ - const CRLF = "\r\n"; - - /** - * The maximum line length allowed by RFC 2822 section 2.1.1 - * @var integer - */ - const MAX_LINE_LENGTH = 998; - - /** - * Constructor. - * @param boolean $exceptions Should we throw external exceptions? - */ - public function __construct($exceptions = null) - { - if ($exceptions !== null) { - $this->exceptions = (boolean)$exceptions; - } - //Pick an appropriate debug output format automatically - $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); - } - - /** - * Destructor. - */ - public function __destruct() - { - //Close any open SMTP connection nicely - $this->smtpClose(); - } - - /** - * Call mail() in a safe_mode-aware fashion. - * 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 $subject Subject - * @param string $body Message Body - * @param string $header Additional Header(s) - * @param string $params Params - * @access private - * @return boolean - */ - private function mailPassthru($to, $subject, $body, $header, $params) - { - //Check overloading of mail function to avoid double-encoding - if (ini_get('mbstring.func_overload') & 1) { - $subject = $this->secureHeader($subject); - } else { - $subject = $this->encodeHeader($this->secureHeader($subject)); - } - - //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 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 <= 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, HTML-safe output - echo htmlentities( - preg_replace('/[\r\n]+/', '', $str), - ENT_QUOTES, - 'UTF-8' - ) - . "<br>\n"; - break; - case 'echo': - default: - //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 boolean $isHtml True for HTML mode. - * @return void - */ - public function isHTML($isHtml = true) - { - if ($isHtml) { - $this->ContentType = 'text/html'; - } else { - $this->ContentType = 'text/plain'; - } - } - - /** - * Send messages using SMTP. - * @return void - */ - public function isSMTP() - { - $this->Mailer = 'smtp'; - } - - /** - * Send messages using PHP's mail() function. - * @return void - */ - public function isMail() - { - $this->Mailer = 'mail'; - } - - /** - * Send messages using $Sendmail. - * @return void - */ - public function isSendmail() - { - $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() - { - $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 The email address to send to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addAddress($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 The email address to send to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addCC($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 The email address to send to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addBCC($address, $name = '') - { - return $this->addOrEnqueueAnAddress('bcc', $address, $name); - } - - /** - * Add a "Reply-To" address. - * @param string $address The email address to reply to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addReplyTo($address, $name = '') - { - return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); - } - - /** - * 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 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 (!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($error_message); - } - return false; - } - if (!$this->validateAddress($address)) { - $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; - } - if ($kind != 'Reply-To') { - if (!array_key_exists(strtolower($address), $this->all_recipients)) { - array_push($this->$kind, array($address, $name)); - $this->all_recipients[strtolower($address)] = true; - return true; - } - } else { - if (!array_key_exists(strtolower($address), $this->ReplyTo)) { - $this->ReplyTo[strtolower($address)] = array($address, $name); - return true; - } - } - return false; - } - - /** - * Parse and validate a string containing one or more RFC822-style comma-separated email addresses - * of the form "display name <address>" 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 boolean $auto Whether to also set the Sender address, defaults to true - * @throws phpmailerException - * @return boolean - */ - public function setFrom($address, $name = '', $auto = true) - { - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - // 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($error_message); - } - return false; - } - $this->From = $address; - $this->FromName = $name; - if ($auto) { - if (empty($this->Sender)) { - $this->Sender = $address; - } - } - return true; - } - - /** - * Return the Message-ID header of the last email. - * 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() - { - return $this->lastMessageID; - } - - /** - * Check that a string looks like an email address. - * @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 = null) - { - 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) { - $patternselect = 'php'; - } else { - $patternselect = 'noregex'; - } - } - } - switch ($patternselect) { - case 'pcre8': - /** - * 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. - */ - 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))?)' . - '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . - '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . - '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . - '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . - '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . - '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', - $address - ); - case 'pcre': - //An older regex that doesn't need a recent PCRE - return (boolean)preg_match( - '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . - '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . - '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . - '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . - '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . - '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . - '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . - '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . - '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . - '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', - $address - ); - 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); - 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 boolean false on error - See the ErrorInfo property for details of the error. - */ - public function send() - { - try { - if (!$this->preSend()) { - return false; - } - return $this->postSend(); - } catch (phpmailerException $exc) { - $this->mailHeader = ''; - $this->setError($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - return false; - } - } - - /** - * Prepare a message for sending. - * @throws phpmailerException - * @return boolean - */ - public function preSend() - { - try { - $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 ($this->alternativeExists()) { - $this->ContentType = 'multipart/alternative'; - } - - $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); - } - - // 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); - } else { - $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); - } - $this->mailHeader .= $this->headerLine( - 'Subject', - $this->encodeHeader($this->secureHeader(trim($this->Subject))) - ); - } - - // Sign with DKIM if enabled - if (!empty($this->DKIM_domain) - and !empty($this->DKIM_selector) - and (!empty($this->DKIM_private_string) - or (!empty($this->DKIM_private) - and self::isPermittedPath($this->DKIM_private) - and file_exists($this->DKIM_private) - ) - ) - ) { - $header_dkim = $this->DKIM_Add( - $this->MIMEHeader . $this->mailHeader, - $this->encodeHeader($this->secureHeader($this->Subject)), - $this->MIMEBody - ); - $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . - str_replace("\r\n", "\n", $header_dkim) . self::CRLF; - } - return true; - } catch (phpmailerException $exc) { - $this->setError($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - return false; - } - } - - /** - * Actually send a message. - * Send the email via the selected mechanism - * @throws phpmailerException - * @return boolean - */ - public function postSend() - { - try { - // Choose the mailer and send through it - switch ($this->Mailer) { - case 'sendmail': - case 'qmail': - return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); - case 'smtp': - return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); - case 'mail': - return $this->mailSend($this->MIMEHeader, $this->MIMEBody); - default: - $sendMethod = $this->Mailer.'Send'; - if (method_exists($this, $sendMethod)) { - return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); - } - - return $this->mailSend($this->MIMEHeader, $this->MIMEBody); - } - } catch (phpmailerException $exc) { - $this->setError($exc->getMessage()); - $this->edebug($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - } - return false; - } - - /** - * Send mail using the $Sendmail program. - * @param string $header The message headers - * @param string $body The message body - * @see PHPMailer::$Sendmail - * @throws phpmailerException - * @access protected - * @return boolean - */ - protected function sendmailSend($header, $body) - { - // 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') { - $sendmailFmt = '%s -f%s'; - } else { - $sendmailFmt = '%s -oi -f%s -t'; - } - } else { - if ($this->Mailer == 'qmail') { - $sendmailFmt = '%s'; - } else { - $sendmailFmt = '%s -oi -t'; - } - } - - // 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: ' . $toAddr . "\n"); - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - $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); - } - } - } else { - if (!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - $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); - } - } - return true; - } - - /** - * 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; - } - - /** - * Check whether a file path is of a permitted type. - * Used to reject URLs and phar files from functions that access local file paths, - * such as addAttachment. - * @param string $path A relative or absolute path to a file. - * @return bool - */ - protected static function isPermittedPath($path) - { - return !preg_match('#^[a-z]+://#i', $path); - } - - /** - * 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 - * @throws phpmailerException - * @access protected - * @return boolean - */ - protected function mailSend($header, $body) - { - $toArr = array(); - foreach ($this->to as $toaddr) { - $toArr[] = $this->addrFormat($toaddr); - } - $to = implode(', ', $toArr); - - $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 (!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); - } - $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 { - $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 (!$result) { - throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); - } - return true; - } - - /** - * Get an instance to use for SMTP operations. - * Override this function to load your own SMTP implementation - * @return SMTP - */ - public function getSMTPInstance() - { - if (!is_object($this->smtp)) { - $this->smtp = new SMTP; - } - return $this->smtp; - } - - /** - * 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. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @uses SMTP - * @access protected - * @return boolean - */ - protected function smtpSend($header, $body) - { - $bad_rcpt = array(); - if (!$this->smtpConnect($this->SMTPOptions)) { - throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); - } - 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 (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); - } - } - - // 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) { - $this->smtp->reset(); - } else { - $this->smtp->quit(); - $this->smtp->close(); - } - //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') . $errstr, - self::STOP_CONTINUE - ); - } - return true; - } - - /** - * 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 - * @access public - * @throws phpmailerException - * @return boolean - */ - public function smtpConnect($options = null) - { - if (is_null($this->smtp)) { - $this->smtp = $this->getSMTPInstance(); - } - - //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; - } - - $this->smtp->setTimeout($this->Timeout); - $this->smtp->setDebugLevel($this->SMTPDebug); - $this->smtp->setDebugOutput($this->Debugoutput); - $this->smtp->setVerp($this->do_verp); - $hosts = explode(';', $this->Host); - $lastexception = null; - - foreach ($hosts as $hostentry) { - $hostinfo = array(); - if (!preg_match( - '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/', - trim($hostentry), - $hostinfo - )) { - // Not a valid host entry - $this->edebug('Ignoring invalid host: ' . $hostentry); - 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 - $prefix = ''; - $secure = $this->SMTPSecure; - $tls = ($this->SMTPSecure == 'tls'); - if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { - $prefix = 'ssl://'; - $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 - $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; - $tport = (integer)$hostinfo[4]; - if ($tport > 0 and $tport < 65536) { - $port = $tport; - } - if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { - try { - if ($this->Helo) { - $hello = $this->Helo; - } else { - $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 EHLO after TLS negotiation - $this->smtp->hello($hello); - } - if ($this->SMTPAuth) { - if (!$this->smtp->authenticate( - $this->Username, - $this->Password, - $this->AuthType, - $this->Realm, - $this->Workstation - ) - ) { - throw new phpmailerException($this->lang('authenticate')); - } - } - return true; - } 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 - $this->smtp->close(); - // As we've caught all exceptions, just report whatever the last one was - if ($this->exceptions and !is_null($lastexception)) { - throw $lastexception; - } - return false; - } - - /** - * Close the active SMTP session if one exists. - * @return void - */ - public function smtpClose() - { - if (is_a($this->smtp, 'SMTP')) { - if ($this->smtp->connected()) { - $this->smtp->quit(); - $this->smtp->close(); - } - } - } - - /** - * 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 $lang_path Path to the language file directory, with trailing separator (slash) - * @return boolean - * @access public - */ - public function setLanguage($langcode = 'en', $lang_path = '') - { - // Backwards compatibility for renamed language codes - $renamed_langcodes = array( - 'br' => 'pt_br', - 'cz' => 'cs', - 'dk' => 'da', - 'no' => 'nb', - 'se' => 'sv', - 'sr' => 'rs' - ); - - 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.', - 'data_not_accepted' => 'SMTP Error: data not accepted.', - 'empty_message' => 'Message body empty', - 'encoding' => 'Unknown encoding: ', - 'execute' => 'Could not execute: ', - 'file_access' => 'Could not access file: ', - '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: ', - '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: ', - 'extension_missing' => 'Extension missing: ' - ); - 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'; - // There is no English translation file - if ($langcode != 'en') { - // Make sure language file path is readable - if (!self::isPermittedPath($lang_file) or !is_readable($lang_file)) { - $foundlang = false; - } else { - // Overwrite language-specific strings. - // This way we'll never have missing translation keys. - $foundlang = include $lang_file; - } - } - $this->language = $PHPMAILER_LANG; - return (boolean)$foundlang; // Returns false if language not found - } - - /** - * Get the array of strings for the current language. - * @return array - */ - public function getTranslations() - { - return $this->language; - } - - /** - * 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')) - * @return string - */ - public function addrAppend($type, $addr) - { - $addresses = array(); - 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') - * @return string - */ - public function addrFormat($addr) - { - if (empty($addr[1])) { // No name provided - return $this->secureHeader($addr[0]); - } else { - return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( - $addr[0] - ) . '>'; - } - } - - /** - * Word-wrap message. - * 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 boolean $qp_mode Whether to run in Quoted-Printable mode - * @access public - * @return string - */ - public function wrapText($message, $length, $qp_mode = false) - { - 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'); - $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); - } - - //Split message into lines - $lines = explode($this->LE, $message); - //Message will be rebuilt in here - $message = ''; - foreach ($lines as $line) { - $words = explode(' ', $line); - $buf = ''; - $firstword = true; - foreach ($words as $word) { - if ($qp_mode and (strlen($word) > $length)) { - $space_left = $length - strlen($buf) - $crlflen; - if (!$firstword) { - if ($space_left > 20) { - $len = $space_left; - if ($is_utf8) { - $len = $this->utf8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == '=') { - $len--; - } 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); - } else { - $message .= $buf . $soft_break; - } - $buf = ''; - } - while (strlen($word) > 0) { - if ($length <= 0) { - break; - } - $len = $length; - if ($is_utf8) { - $len = $this->utf8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == '=') { - $len--; - } 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); - } else { - $buf = $part; - } - } - } else { - $buf_o = $buf; - 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; - } - - return $message; - } - - /** - * Find the last character boundary prior to $maxLength in a utf-8 - * quoted-printable encoded string. - * Original written by Colin Brown. - * @access public - * @param string $encodedText utf-8 QP text - * @param integer $maxLength Find the last character boundary prior to this length - * @return integer - */ - public function utf8CharBoundary($encodedText, $maxLength) - { - $foundSplitPos = false; - $lookBack = 3; - while (!$foundSplitPos) { - $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); - $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 the encoded char was found at pos 0, it will fit - // otherwise reduce maxLength to start of the encoded char - if ($encodedCharPos > 0) { - $maxLength = $maxLength - ($lookBack - $encodedCharPos); - } - $foundSplitPos = true; - } 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 - $lookBack += 3; - } - } else { - // No encoded character found - $foundSplitPos = true; - } - } - return $maxLength; - } - - /** - * 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 - */ - public function setWordWrap() - { - if ($this->WordWrap < 1) { - return; - } - - switch ($this->message_type) { - case 'alt': - case 'alt_inline': - case 'alt_attach': - case 'alt_inline_attach': - $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); - break; - default: - $this->Body = $this->wrapText($this->Body, $this->WordWrap); - break; - } - } - - /** - * Assemble message headers. - * @access public - * @return string The assembled headers - */ - public function createHeader() - { - $result = ''; - - $result .= $this->headerLine('Date', $this->MessageDate == '' ? self::rfcDate() : $this->MessageDate); - - // To be created automatically by mail() - if ($this->SingleTo) { - if ($this->Mailer != 'mail') { - foreach ($this->to as $toaddr) { - $this->SingleToArray[] = $this->addrFormat($toaddr); - } - } - } 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:;'); - } - } - - $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); - - // sendmail and mail() extract Cc from the header before sending - if (count($this->cc) > 0) { - $result .= $this->addrAppend('Cc', $this->cc); - } - - // sendmail and mail() extract Bcc from the header before sending - if (( - $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' - ) - and count($this->bcc) > 0 - ) { - $result .= $this->addrAppend('Bcc', $this->bcc); - } - - if (count($this->ReplyTo) > 0) { - $result .= $this->addrAppend('Reply-To', $this->ReplyTo); - } - - // mail() sets the subject itself - if ($this->Mailer != 'mail') { - $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); - } - - // 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>', $this->uniqueid, $this->serverHostname()); - } - $result .= $this->headerLine('Message-ID', $this->lastMessageID); - if (!is_null($this->Priority)) { - $result .= $this->headerLine('X-Priority', $this->Priority); - } - if ($this->XMailer == '') { - $result .= $this->headerLine( - 'X-Mailer', - 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' - ); - } else { - $myXmailer = trim($this->XMailer); - if ($myXmailer) { - $result .= $this->headerLine('X-Mailer', $myXmailer); - } - } - - if ($this->ConfirmReadingTo != '') { - $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); - } - - // Add custom headers - foreach ($this->CustomHeader as $header) { - $result .= $this->headerLine( - trim($header[0]), - $this->encodeHeader(trim($header[1])) - ); - } - if (!$this->sign_key_file) { - $result .= $this->headerLine('MIME-Version', '1.0'); - $result .= $this->getMailMIME(); - } - - return $result; - } - - /** - * 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;'); - $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - case 'attach': - case 'inline_attach': - case 'alt_attach': - case 'alt_inline_attach': - $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); - $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - case 'alt': - case 'alt_inline': - $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); - $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - 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 - if ($this->Encoding != '7bit') { - // 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') { - $result .= $this->LE; - } - - return $result; - } - - /** - * Returns the whole MIME message. - * Includes complete headers and body. - * Only valid post preSend(). - * @see PHPMailer::preSend() - * @access public - * @return string - */ - public function getSentMIMEMessage() - { - 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 - */ - 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; - } - - $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 .= $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 .= $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], $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 .= $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], $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', ''); - $body .= $this->encodeString($this->Ical, $this->Encoding); - $body .= $this->LE . $this->LE; - } - $body .= $this->endBoundary($this->boundary[1]); - break; - case 'alt_inline': - $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], $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], $altBodyCharSet, 'text/plain', $altBodyEncoding); - $body .= $this->encodeString($this->AltBody, $altBodyEncoding); - $body .= $this->LE . $this->LE; - $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], $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], $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; - $body .= $this->endBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->attachAll('attachment', $this->boundary[1]); - break; - default: - // 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; - } - - if ($this->isError()) { - $body = ''; - } elseif ($this->sign_key_file) { - try { - if (!defined('PKCS7_TEXT')) { - 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 - $file = tempnam(sys_get_temp_dir(), 'mail'); - 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'); - //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 $exc) { - $body = ''; - if ($this->exceptions) { - throw $exc; - } - } - } - return $body; - } - - /** - * Return the start of a message boundary. - * @access protected - * @param string $boundary - * @param string $charSet - * @param string $contentType - * @param string $encoding - * @return string - */ - protected function getBoundary($boundary, $charSet, $contentType, $encoding) - { - $result = ''; - if ($charSet == '') { - $charSet = $this->CharSet; - } - if ($contentType == '') { - $contentType = $this->ContentType; - } - if ($encoding == '') { - $encoding = $this->Encoding; - } - $result .= $this->textLine('--' . $boundary); - $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); - $result .= $this->LE; - // 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; - } - - /** - * Return the end of a message boundary. - * @access protected - * @param string $boundary - * @return string - */ - protected function endBoundary($boundary) - { - return $this->LE . '--' . $boundary . '--' . $this->LE; - } - - /** - * Set the message type. - * PHPMailer only supports some preset message types, not arbitrary MIME structures. - * @access protected - * @return void - */ - protected function setMessageType() - { - $type = array(); - if ($this->alternativeExists()) { - $type[] = 'alt'; - } - if ($this->inlineImageExists()) { - $type[] = 'inline'; - } - if ($this->attachmentExists()) { - $type[] = 'attach'; - } - $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 - * @return string - */ - public function headerLine($name, $value) - { - return $name . ': ' . $value . $this->LE; - } - - /** - * Return a formatted mail line. - * @access public - * @param string $value - * @return string - */ - public function textLine($value) - { - return $value . $this->LE; - } - - /** - * 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. - * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client. - * If you need to do that, fetch the resource yourself and pass it in via a local file or string. - * @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 boolean - */ - public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') - { - try { - if (!self::isPermittedPath($path) or !@is_file($path)) { - 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 ($type == '') { - $type = self::filenameToType($path); - } - - $filename = basename($path); - if ($name == '') { - $name = $filename; - } - - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => $disposition, - 7 => 0 - ); - - } catch (phpmailerException $exc) { - $this->setError($exc->getMessage()); - $this->edebug($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - return false; - } - return true; - } - - /** - * Return the array of attachments. - * @return array - */ - public function getAttachments() - { - return $this->attachment; - } - - /** - * 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 - * @return string - */ - protected function attachAll($disposition_type, $boundary) - { - // Return text of body - $mime = array(); - $cidUniq = array(); - $incl = array(); - - // Add all attachments - foreach ($this->attachment as $attachment) { - // Check if it is a valid disposition_filter - if ($attachment[6] == $disposition_type) { - // Check for string attachment - $string = ''; - $path = ''; - $bString = $attachment[5]; - if ($bString) { - $string = $attachment[0]; - } else { - $path = $attachment[0]; - } - - $inclhash = md5(serialize($attachment)); - if (in_array($inclhash, $incl)) { - continue; - } - $incl[] = $inclhash; - $name = $attachment[2]; - $encoding = $attachment[3]; - $type = $attachment[4]; - $disposition = $attachment[6]; - $cid = $attachment[7]; - if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { - continue; - } - $cidUniq[$cid] = true; - - $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); - } - - // If a filename contains any of these chars, it should be quoted, - // but not otherwise: RFC2183 & RFC2045 5.1 - // Fixes a warning in IETF's msglint MIME checker - // Allow for bypassing the Content-Disposition header totally - if (!(empty($disposition))) { - $encoded_name = $this->encodeHeader($this->secureHeader($name)); - if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { - $mime[] = sprintf( - 'Content-Disposition: %s; filename="%s"%s', - $disposition, - $encoded_name, - $this->LE . $this->LE - ); - } else { - 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; - } - - // Encode as string attachment - if ($bString) { - $mime[] = $this->encodeString($string, $encoding); - if ($this->isError()) { - return ''; - } - $mime[] = $this->LE . $this->LE; - } else { - $mime[] = $this->encodeFile($path, $encoding); - if ($this->isError()) { - return ''; - } - $mime[] = $this->LE . $this->LE; - } - } - } - - $mime[] = sprintf('--%s--%s', $boundary, $this->LE); - - 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 $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @throws phpmailerException - * @access protected - * @return string - */ - protected function encodeFile($path, $encoding = 'base64') - { - try { - if (!self::isPermittedPath($path) or !file_exists($path)) { - throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); - } - $magic_quotes = get_magic_quotes_runtime(); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime(false); - } else { - //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); - $file_buffer = $this->encodeString($file_buffer, $encoding); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime($magic_quotes); - } else { - ini_set('magic_quotes_runtime', $magic_quotes); - } - } - return $file_buffer; - } catch (Exception $exc) { - $this->setError($exc->getMessage()); - return ''; - } - } - - /** - * Encode a string in requested format. - * Returns an empty string on failure. - * @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 - */ - public function encodeString($str, $encoding = 'base64') - { - $encoded = ''; - switch (strtolower($encoding)) { - case 'base64': - $encoded = chunk_split(base64_encode($str), 76, $this->LE); - break; - case '7bit': - case '8bit': - $encoded = $this->fixEOL($str); - // Make sure it ends with a line break - if (substr($encoded, -(strlen($this->LE))) != $this->LE) { - $encoded .= $this->LE; - } - break; - case 'binary': - $encoded = $str; - break; - case 'quoted-printable': - $encoded = $this->encodeQP($str); - break; - default: - $this->setError($this->lang('encoding') . $encoding); - break; - } - return $encoded; - } - - /** - * Encode a header string optimally. - * Picks shortest of Q, B, quoted-printable or none. - * @access public - * @param string $str - * @param string $position - * @return string - */ - public function encodeHeader($str, $position = 'text') - { - $matchcount = 0; - switch (strtolower($position)) { - case 'phrase': - if (!preg_match('/[\200-\377]/', $str)) { - // 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); - } else { - return ("\"$encoded\""); - } - } - $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); - break; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'comment': - $matchcount = preg_match_all('/[()"]/', $str, $matches); - // Intentional fall-through - case 'text': - default: - $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); - break; - } - - //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 ($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 - // multibyte strings without breaking lines within a character - $encoded = $this->base64EncodeWrapMB($str, "\n"); - } else { - $encoded = base64_encode($str); - $maxlen -= $maxlen % 4; - $encoded = trim(chunk_split($encoded, $maxlen, "\n")); - } - } else { - $encoding = 'Q'; - $encoded = $this->encodeQ($str, $position); - $encoded = $this->wrapText($encoded, $maxlen, true); - $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); - } - - $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); - $encoded = trim(str_replace("\n", $this->LE, $encoded)); - - return $encoded; - } - - /** - * Check if a string contains multi-byte characters. - * @access public - * @param string $str multi-byte text to wrap encode - * @return boolean - */ - public function hasMultiBytes($str) - { - if (function_exists('mb_strlen')) { - return (strlen($str) > mb_strlen($str, $this->CharSet)); - } else { // Assume no multibytes (we can't handle without mbstring functions anyway) - return false; - } - } - - /** - * 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 - * @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 $linebreak string to use as linefeed/end-of-line - * @return string - */ - public function base64EncodeWrapMB($str, $linebreak = null) - { - $start = '=?' . $this->CharSet . '?B?'; - $end = '?='; - $encoded = ''; - if ($linebreak === null) { - $linebreak = $this->LE; - } - - $mb_length = mb_strlen($str, $this->CharSet); - // Each line must have length <= 75, including $start and $end - $length = 75 - strlen($start) - strlen($end); - // Average multi-byte ratio - $ratio = $mb_length / strlen($str); - // Base64 has a 4:3 ratio - $avgLength = floor($length * $ratio * .75); - - for ($i = 0; $i < $mb_length; $i += $offset) { - $lookBack = 0; - do { - $offset = $avgLength - $lookBack; - $chunk = mb_substr($str, $i, $offset, $this->CharSet); - $chunk = base64_encode($chunk); - $lookBack++; - } while (strlen($chunk) > $length); - $encoded .= $chunk . $linebreak; - } - - // Chomp the last linefeed - $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 integer $line_max Number of chars allowed on a line before wrapping - * @return string - * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment - */ - public function encodeQP($string, $line_max = 76) - { - // 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 - $string = str_replace( - array('%20', '%0D%0A.', '%0D%0A', '%'), - array(' ', "\r\n=2E", "\r\n", '='), - rawurlencode($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 - * @param string $string - * @param integer $line_max - * @param boolean $space_conv - * @return string - * @deprecated Use encodeQP instead. - */ - public function encodeQPphp( - $string, - $line_max = 76, - /** @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 - * @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 - $pattern = ''; - $encoded = str_replace(array("\r", "\n"), '', $str); - switch (strtolower($position)) { - case 'phrase': - // RFC 2047 section 5.3 - $pattern = '^A-Za-z0-9!*+\/ -'; - break; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'comment': - // RFC 2047 section 5.2 - $pattern = '\(\)"'; - // 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 - $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 - $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) - 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 $disposition Disposition to use - * @return void - */ - public function addStringAttachment( - $string, - $filename, - $encoding = 'base64', - $type = '', - $disposition = 'attachment' - ) { - // If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($filename); - } - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $filename, - 2 => basename($filename), - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => $disposition, - 7 => 0 - ); - } - - /** - * Add an embedded (inline) attachment from a file. - * This can include images, sounds, and just about any other document type. - * 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. - * 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 boolean True on successfully adding an attachment - */ - public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') - { - if (!self::isPermittedPath($path) or !@is_file($path)) { - $this->setError($this->lang('file_access') . $path); - return false; - } - - // If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($path); - } - - $filename = basename($path); - if ($name == '') { - $name = $filename; - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => $disposition, - 7 => $cid - ); - return true; - } - - /** - * Add an embedded stringified attachment. - * 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 $name - * @param string $encoding File encoding (see $Encoding). - * @param string $type MIME type. - * @param string $disposition Disposition to use - * @return boolean True on successfully adding an attachment - */ - public function addStringEmbeddedImage( - $string, - $cid, - $name = '', - $encoding = 'base64', - $type = '', - $disposition = 'inline' - ) { - // If a MIME type is not specified, try to work it out from the name - if ($type == '' and !empty($name)) { - $type = self::filenameToType($name); - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $name, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => $disposition, - 7 => $cid - ); - return true; - } - - /** - * Check if an inline attachment is present. - * @access public - * @return boolean - */ - public function inlineImageExists() - { - foreach ($this->attachment as $attachment) { - if ($attachment[6] == 'inline') { - return true; - } - } - return false; - } - - /** - * Check if an attachment (non-inline) is present. - * @return boolean - */ - public function attachmentExists() - { - foreach ($this->attachment as $attachment) { - if ($attachment[6] == 'attachment') { - return true; - } - } - return false; - } - - /** - * Check if this message has an alternative body set. - * @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() - { - foreach ($this->to as $to) { - unset($this->all_recipients[strtolower($to[0])]); - } - $this->to = array(); - $this->clearQueuedAddresses('to'); - } - - /** - * Clear all CC recipients. - * @return void - */ - public function clearCCs() - { - foreach ($this->cc as $cc) { - unset($this->all_recipients[strtolower($cc[0])]); - } - $this->cc = array(); - $this->clearQueuedAddresses('cc'); - } - - /** - * Clear all BCC recipients. - * @return void - */ - public function clearBCCs() - { - foreach ($this->bcc as $bcc) { - 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() - { - $this->to = array(); - $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() - { - $this->attachment = array(); - } - - /** - * Clear all custom headers. - * @return void - */ - public function clearCustomHeaders() - { - $this->CustomHeader = array(); - } - - /** - * Add an error message to the error container. - * @access protected - * @param string $msg - * @return void - */ - protected function setError($msg) - { - $this->error_count++; - if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { - $lasterror = $this->smtp->getError(); - 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; - } - - /** - * 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 - date_default_timezone_set(@date_default_timezone_get()); - return date('D, j M Y H:i:s O'); - } - - /** - * Get the server hostname. - * Returns 'localhost.localdomain' if unknown. - * @access protected - * @return string - */ - protected function serverHostname() - { - $result = 'localhost.localdomain'; - if (!empty($this->Hostname)) { - $result = $this->Hostname; - } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { - $result = $_SERVER['SERVER_NAME']; - } elseif (function_exists('gethostname') && gethostname() !== false) { - $result = gethostname(); - } elseif (php_uname('n') !== false) { - $result = php_uname('n'); - } - return $result; - } - - /** - * Get an error message in the current language. - * @access protected - * @param string $key - * @return string - */ - protected function lang($key) - { - if (count($this->language) < 1) { - $this->setLanguage('en'); // set the default language - } - - 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 the key as a fallback - return $key; - } - } - - /** - * Check if an error occurred. - * @access public - * @return boolean True if an error did occur. - */ - public function isError() - { - return ($this->error_count > 0); - } - - /** - * 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 - */ - public function fixEOL($str) - { - // Normalise to \n - $nstr = str_replace(array("\r\n", "\r"), "\n", $str); - // Now convert LE as needed - if ($this->LE !== "\n") { - $nstr = str_replace("\n", $this->LE, $nstr); - } - return $nstr; - } - - /** - * 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 $value Header value - * @return void - */ - public function addCustomHeader($name, $value = null) - { - if ($value === null) { - // Value passed in as name:value - $this->CustomHeader[] = explode(':', $name, 2); - } else { - $this->CustomHeader[] = array($name, $value); - } - } - - /** - * 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. <img src="/images/a.png"> 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 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 (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($directory) > 1 && substr($directory, -1) != '/') { - $directory .= '/'; - } - if ($this->addEmbeddedImage( - $basedir . $directory . $filename, - $cid, - $filename, - 'base64', - self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) - ) - ) { - $message = preg_replace( - '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', - $images[1][$imgindex] . '="cid:' . $cid . '"', - $message - ); - } - } - } - } - $this->isHTML(true); - // 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 (!$this->alternativeExists()) { - $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . - self::CRLF . self::CRLF; - } - return $this->Body; - } - - /** - * Convert an HTML string into plain text. - * 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: - * <code> - * // 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(); - * }); - * </code> - * @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 (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))), - ENT_QUOTES, - $this->CharSet - ); - } - - /** - * Get the MIME type for a file extension. - * @param string $ext File extension - * @access public - * @return string MIME type of file. - * @static - */ - public static function _mime_types($ext = '') - { - $mimes = array( - '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', - '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', - 'phtml' => 'application/x-httpd-php', - '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', - 'shtml' => 'text/html', - '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' - ); - 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 - $qpos = strpos($filename, '?'); - if (false !== $qpos) { - $filename = substr($filename, 0, $qpos); - } - $pathinfo = self::mb_pathinfo($filename); - return self::_mime_types($pathinfo['extension']); - } - - /** - * 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 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 - * @return string|array - * @static - */ - public static function mb_pathinfo($path, $options = null) - { - $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); - $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']; - case PATHINFO_BASENAME: - case 'basename': - return $ret['basename']; - case PATHINFO_EXTENSION: - case 'extension': - return $ret['extension']; - case PATHINFO_FILENAME: - case 'filename': - return $ret['filename']; - default: - return $ret; - } - } - - /** - * 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: - * `$mail->set('SMTPSecure', 'tls');` - * is the same as: - * `$mail->SMTPSecure = 'tls';` - * @access public - * @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 = '') - { - if (property_exists($this, $name)) { - $this->$name = $value; - return true; - } else { - $this->setError($this->lang('variable_set') . $name); - return false; - } - } - - /** - * Strip newlines to prevent header injection. - * @access public - * @param string $str - * @return string - */ - public function secureHeader($str) - { - return trim(str_replace(array("\r", "\n"), '', $str)); - } - - /** - * 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 - * @access public - * @static - */ - public static function normalizeBreaks($text, $breaktype = "\r\n") - { - 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, $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 - */ - public function DKIM_QP($txt) - { - $line = ''; - for ($i = 0; $i < strlen($txt); $i++) { - $ord = ord($txt[$i]); - if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { - $line .= $txt[$i]; - } else { - $line .= '=' . sprintf('%02X', $ord); - } - } - return $line; - } - - /** - * Generate a DKIM signature. - * @access public - * @param string $signHeader - * @throws phpmailerException - * @return string The DKIM signature value - */ - public function DKIM_Sign($signHeader) - { - if (!defined('PKCS7_TEXT')) { - if ($this->exceptions) { - throw new phpmailerException($this->lang('extension_missing') . 'openssl'); - } - return ''; - } - $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 = openssl_pkey_get_private($privKeyStr); - } - //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 $signHeader Header - * @return string - */ - public function DKIM_HeaderC($signHeader) - { - $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); - $lines = explode("\r\n", $signHeader); - foreach ($lines as $key => $line) { - list($heading, $value) = explode(':', $line, 2); - $heading = strtolower($heading); - $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces - $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value - } - $signHeader = implode("\r\n", $lines); - return $signHeader; - } - - /** - * Generate a DKIM canonicalization body. - * @access public - * @param string $body Message Body - * @return string - */ - public function DKIM_BodyC($body) - { - if ($body == '') { - return "\r\n"; - } - // stabilize line endings - $body = str_replace("\r\n", "\n", $body); - $body = str_replace("\n", "\r\n", $body); - // END stabilize line endings - while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { - $body = substr($body, 0, strlen($body) - 2); - } - return $body; - } - - /** - * 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 - * @return string - */ - public function DKIM_Add($headers_line, $subject, $body) - { - $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) - $subject_header = "Subject: $subject"; - $headers = explode($this->LE, $headers_line); - $from_header = ''; - $to_header = ''; - $date_header = ''; - $current = ''; - foreach ($headers as $header) { - if (strpos($header, 'From:') === 0) { - $from_header = $header; - $current = 'from_header'; - } elseif (strpos($header, 'To:') === 0) { - $to_header = $header; - $current = 'to_header'; - } elseif (strpos($header, 'Date:') === 0) { - $date_header = $header; - $current = 'date_header'; - } else { - if (!empty($$current) && strpos($header, ' =?') === 0) { - $$current .= $header; - } else { - $current = ''; - } - } - } - $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', - $this->DKIM_QP($subject_header) - ); // Copied header fields (dkim-quoted-printable) - $body = $this->DKIM_BodyC($body); - $DKIMlen = strlen($body); // Length of body - $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: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" . - $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 - */ - public function getToAddresses() - { - return $this->to; - } - - /** - * 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 - */ - public function getCcAddresses() - { - return $this->cc; - } - - /** - * 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 - */ - public function getBccAddresses() - { - return $this->bcc; - } - - /** - * 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 - */ - public function getReplyToAddresses() - { - return $this->ReplyTo; - } - - /** - * 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 - */ - public function getAllRecipientAddresses() - { - return $this->all_recipients; - } - - /** - * Perform a callback. - * @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) - { - if (!empty($this->action_function) && is_callable($this->action_function)) { - $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); - call_user_func_array($this->action_function, $params); - } - } -} - -/** - * PHPMailer exception handler - * @package PHPMailer - */ -class phpmailerException extends Exception -{ - /** - * Prettify error message output - * @return string - */ - public function errorMessage() - { - $errorMsg = '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n"; - return $errorMsg; - } -} diff --git a/cron/class.smtp.php b/cron/class.smtp.php deleted file mode 100755 index 118cb20f6b9d952159fe88aff6b9dc4e9f2ad52c..0000000000000000000000000000000000000000 --- a/cron/class.smtp.php +++ /dev/null @@ -1,1276 +0,0 @@ -<?php -/** - * PHPMailer RFC821 SMTP email transport class. - * PHP Version 5 - * @package PHPMailer - * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project - * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> - * @author Jim Jagielski (jimjag) <jimjag@gmail.com> - * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> - * @author Brent R. Matzelle (original founder) - * @copyright 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. - */ - -/** - * PHPMailer RFC821 SMTP email transport class. - * 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 <phpmailer@synchromedia.co.uk> - */ -class SMTP -{ - /** - * The PHPMailer SMTP version number. - * @var string - */ - const VERSION = '5.2.27'; - - /** - * 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 maximum line length allowed by RFC 2822 section 2.1.1 - * @var integer - */ - 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.27'; - - /** - * SMTP server port number. - * @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. - * @var string - * @deprecated Use the `CRLF` constant instead - * @see SMTP::CRLF - */ - public $CRLF = "\r\n"; - - /** - * Debug output level. - * Options: - * * 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 = self::DEBUG_OFF; - - /** - * How to handle debug output. - * Options: - * * `echo` Output plain-text as-is, appropriate for CLI - * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output - * * `error_log` Output to error log as configured in php.ini - * - * Alternatively, you can provide a callable expecting two params: a message string and the debug level: - * <code> - * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; - * </code> - * @var string|callable - */ - public $Debugoutput = 'echo'; - - /** - * Whether to use VERP. - * @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 - * 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; - - /** - * 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 = 300; - - /** - * @var array Patterns to extract an SMTP transaction id from reply to a DATA command. - * The first capture group in each regex will be used as the ID. - */ - 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 (.*)/' - ); - - /** - * @var string The last transaction ID issued in response to a DATA command, - * if one was detected - */ - protected $last_smtp_transaction_id; - - /** - * The socket for the server connection. - * @var resource - */ - protected $smtp_conn; - - /** - * Error information, if any, for the last SMTP command. - * @var array - */ - protected $error = array( - 'error' => '', - 'detail' => '', - 'smtp_code' => '', - 'smtp_code_ex' => '' - ); - - /** - * The reply the server sent to us for HELO. - * If null, no HELO string has yet been received. - * @var string|null - */ - 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. - * @var string - */ - protected $last_reply = ''; - - /** - * 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, $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 - error_log($str); - break; - case 'html': - //Cleans up output a bit for a better looking, HTML-safe output - echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities( - preg_replace('/[\r\n]+/', '', $str), - ENT_QUOTES, - 'UTF-8' - ) . "<br>\n"; - break; - case 'echo': - default: - //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 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 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->setError(''); - // Make sure we are __not__ connected - if ($this->connected()) { - // Already connected, generate error - $this->setError('Already connected to a server'); - return false; - } - if (empty($port)) { - $port = self::DEFAULT_SMTP_PORT; - } - // Connect to the SMTP server - $this->edebug( - "Connection: opening to $host:$port, timeout=$timeout, options=" . - var_export($options, true), - self::DEBUG_CONNECTION - ); - $errno = 0; - $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 - ); - return false; - } - $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'); - // 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(); - $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); - return true; - } - - /** - * Initiate a TLS (encrypted) session. - * @access public - * @return boolean - */ - public function startTLS() - { - 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 - set_error_handler(array($this, 'errorHandler')); - $crypto_ok = stream_socket_enable_crypto( - $this->smtp_conn, - true, - $crypto_method - ); - restore_error_handler(); - return $crypto_ok; - } - - /** - * 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, XOAUTH2) - * @param string $realm The auth realm for NTLM - * @param string $workstation The auth workstation for NTLM - * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) - * @return bool True if successfully authenticated.* @access public - */ - public function authenticate( - $username, - $password, - $authtype = null, - $realm = '', - $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; 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 - if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { - return false; - } - // Send encoded username and password - if (!$this->sendCommand( - 'User & Password', - base64_encode("\0" . $username . "\0" . $password), - 235 - ) - ) { - return false; - } - break; - case 'LOGIN': - // Start authentication - if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { - return false; - } - if (!$this->sendCommand("Username", base64_encode($username), 334)) { - return false; - } - if (!$this->sendCommand("Password", base64_encode($password), 235)) { - 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 - * Bundled with Permission - * - * How to telnet in windows: - * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx - * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication - */ - require_once 'extras/ntlm_sasl_client.php'; - $temp = new stdClass; - $ntlm_client = new ntlm_sasl_client_class; - //Check that functions are available - 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 - - if (!$this->sendCommand( - 'AUTH NTLM', - 'AUTH NTLM ' . base64_encode($msg1), - 334 - ) - ) { - return false; - } - //Though 0 based, there is a white space after the 3 digit number - //msg2 - $challenge = substr($this->last_reply, 3); - $challenge = base64_decode($challenge); - $ntlm_res = $ntlm_client->NTLMResponse( - substr($challenge, 24, 8), - $password - ); - //msg3 - $msg3 = $ntlm_client->typeMsg3( - $ntlm_res, - $username, - $realm, - $workstation - ); - // send encoded username - return $this->sendCommand('Username', base64_encode($msg3), 235); - case 'CRAM-MD5': - // Start authentication - if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { - return false; - } - // Get the challenge - $challenge = base64_decode(substr($this->last_reply, 4)); - - // Build the response - $response = $username . ' ' . $this->hmac($challenge, $password); - - // send encoded credentials - return $this->sendCommand('Username', base64_encode($response), 235); - default: - $this->setError("Authentication method \"$authtype\" is not supported"); - return false; - } - return true; - } - - /** - * 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 - * @return string - */ - protected function hmac($data, $key) - { - if (function_exists('hash_hmac')) { - return hash_hmac('md5', $data, $key); - } - - // The following borrowed from - // http://php.net/manual/en/function.mhash.php#27225 - - // RFC 2104 HMAC implementation for php. - // Creates an md5 HMAC. - // Eliminates the need to install mhash to compute a HMAC - // by Lance Rushing - - $bytelen = 64; // byte length for md5 - if (strlen($key) > $bytelen) { - $key = pack('H*', md5($key)); - } - $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; - - return md5($k_opad . pack('H*', md5($k_ipad . $data))); - } - - /** - * Check connection state. - * @access public - * @return boolean True if connected. - */ - public function connected() - { - 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 - $this->edebug( - 'SMTP NOTICE: EOF caught while checking if connected', - self::DEBUG_CLIENT - ); - $this->close(); - return false; - } - return true; // everything looks good - } - return false; - } - - /** - * Close the socket and clean up the state of the class. - * Don't use this function without first trying to use QUIT. - * @see quit() - * @access public - * @return void - */ - public function close() - { - $this->setError(''); - $this->server_caps = null; - $this->helo_rply = null; - if (is_resource($this->smtp_conn)) { - // close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = null; //Makes for cleaner serialization - $this->edebug('Connection: closed', self::DEBUG_CONNECTION); - } - } - - /** - * Send an SMTP DATA command. - * Issues a data command and sends the msg_data to the server, - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a <CRLF> with the message headers - * and the message body being separated by and additional <CRLF>. - * Implements rfc 821: DATA <CRLF> - * @param string $msg_data Message data to send - * @access public - * @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 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 line breaks before exploding - $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); - - /* 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) && strpos($field, ' ') === false) { - $in_headers = true; - } - - foreach ($lines as $line) { - $lines_out = array(); - if ($in_headers and $line == '') { - $in_headers = false; - } - //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) { - //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 section 3.1.1 - if ($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - //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 - //Increase timelimit for end of DATA command - $savetimelimit = $this->Timelimit; - $this->Timelimit = $this->Timelimit * 2; - $result = $this->sendCommand('DATA END', '.', 250); - $this->recordLastTransactionID(); - //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 RFC 821: HELO <SP> <domain> <CRLF> - * and RFC 2821 EHLO. - * @param string $host The host name or IP to connect to - * @access public - * @return boolean - */ - public function hello($host = '') - { - //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() - * @param string $hello The HELO string - * @param string $host The hostname to say we are - * @access protected - * @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 - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more recipient - * commands may be called followed by a data command. - * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> - * @param string $from Source address of this message - * @access public - * @return boolean - */ - public function mail($from) - { - $useVerp = ($this->do_verp ? ' XVERP' : ''); - return $this->sendCommand( - 'MAIL FROM', - 'MAIL FROM:<' . $from . '>' . $useVerp, - 250 - ); - } - - /** - * 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 <CRLF> - * @param boolean $close_on_error Should the connection close if an error occurs? - * @access public - * @return boolean - */ - public function quit($close_on_error = true) - { - $noerror = $this->sendCommand('QUIT', 'QUIT', 221); - $err = $this->error; //Save any error - if ($noerror or $close_on_error) { - $this->close(); - $this->error = $err; //Restore any error from the quit command - } - return $noerror; - } - - /** - * Send an SMTP RCPT command. - * Sets the TO argument to $toaddr. - * Returns true if the recipient was accepted false if it was rejected. - * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> - * @param string $address The address the message is being sent to - * @access public - * @return boolean - */ - public function recipient($address) - { - return $this->sendCommand( - 'RCPT TO', - 'RCPT TO:<' . $address . '>', - array(250, 251) - ); - } - - /** - * Send an SMTP RSET command. - * Abort any transaction that is currently in progress. - * Implements rfc 821: RSET <CRLF> - * @access public - * @return boolean True on success. - */ - public function reset() - { - return $this->sendCommand('RSET', 'RSET', 250); - } - - /** - * 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 $commandstring The actual command to send - * @param integer|array $expect One or more expected integer success codes - * @access protected - * @return boolean True on success. - */ - protected function sendCommand($command, $commandstring, $expect) - { - if (!$this->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); - - $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->setError( - "$command command failed", - $detail, - $code, - $code_ex - ); - $this->edebug( - 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, - self::DEBUG_CLIENT - ); - return false; - } - - $this->setError(''); - return true; - } - - /** - * Send an SMTP SAML command. - * Starts a mail transaction from the email address specified in $from. - * Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more recipient - * commands may be called followed by a data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> - * @param string $from The address the message is from - * @access public - * @return boolean - */ - public function sendAndMail($from) - { - return $this->sendCommand('SAML', "SAML FROM:$from", 250); - } - - /** - * Send an SMTP VRFY command. - * @param string $name The name to verify - * @access public - * @return boolean - */ - public function verify($name) - { - 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 boolean - */ - public function noop() - { - 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 - * Implements from rfc 821: TURN <CRLF> - * @access public - * @return boolean - */ - public function turn() - { - $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 integer|boolean The number of bytes sent to the server or false on error - */ - public function client_send($data) - { - $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); - set_error_handler(array($this, 'errorHandler')); - $result = fwrite($this->smtp_conn, $data); - restore_error_handler(); - return $result; - } - - /** - * Get the latest error. - * @access public - * @return array - */ - public function getError() - { - 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 - */ - public function getLastReply() - { - return $this->last_reply; - } - - /** - * Read the SMTP server's response. - * Either before eof or socket timeout occurs on the operation. - * 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; - 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); - $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 response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled), - // or 4th character is a space, we are done reading, break the loop, - // string array access is a micro-optimisation over strlen - if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) { - break; - } - // Timed-out? Log and break - $info = stream_get_meta_data($this->smtp_conn); - if ($info['timed_out']) { - $this->edebug( - 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', - self::DEBUG_LOWLEVEL - ); - break; - } - // Now check if reads took too long - if ($endtime and time() > $endtime) { - $this->edebug( - 'SMTP -> get_lines(): timelimit reached (' . - $this->Timelimit . ' sec)', - self::DEBUG_LOWLEVEL - ); - break; - } - } - return $data; - } - - /** - * Enable or disable VERP address generation. - * @param boolean $enabled - */ - public function setVerp($enabled = false) - { - $this->do_verp = $enabled; - } - - /** - * Get VERP address generation mode. - * @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|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. - */ - public function setDebugOutput($method = 'echo') - { - $this->Debugoutput = $method; - } - - /** - * Get debug output method. - * @return string - */ - public function getDebugOutput() - { - return $this->Debugoutput; - } - - /** - * Set debug output level. - * @param integer $level - */ - public function setDebugLevel($level = 0) - { - $this->do_debug = $level; - } - - /** - * Get debug output level. - * @return integer - */ - public function getDebugLevel() - { - return $this->do_debug; - } - - /** - * Set SMTP timeout. - * @param integer $timeout - */ - public function setTimeout($timeout = 0) - { - $this->Timeout = $timeout; - } - - /** - * Get SMTP timeout. - * @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. - * @param string $errfile The file the error occurred in - * @param integer $errline The line number the error occurred on - */ - protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) - { - $notice = 'Connection failed.'; - $this->setError( - $notice, - $errno, - $errmsg - ); - $this->edebug( - $notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]", - self::DEBUG_CONNECTION - ); - } - - /** - * Extract and return the ID of the last SMTP transaction based on - * a list of patterns provided in SMTP::$smtp_transaction_id_patterns. - * Relies on the host providing the ID in response to a DATA command. - * If no reply has been received yet, it will return null. - * If no pattern was matched, it will return false. - * @return bool|null|string - */ - protected function recordLastTransactionID() - { - $reply = $this->getLastReply(); - - if (empty($reply)) { - $this->last_smtp_transaction_id = null; - } else { - $this->last_smtp_transaction_id = false; - foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { - if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { - $this->last_smtp_transaction_id = $matches[1]; - } - } - } - - return $this->last_smtp_transaction_id; - } - - /** - * Get the queue/transaction ID of the last SMTP transaction - * If no reply has been received yet, it will return null. - * If no pattern was matched, it will return false. - * @return bool|null|string - * @see recordLastTransactionID() - */ - public function getLastTransactionID() - { - return $this->last_smtp_transaction_id; - } -} diff --git a/cron/modules/SalesOrder/RecurringInvoice.service b/cron/modules/SalesOrder/RecurringInvoice.service index ccb87f5b54d1a6d44e24f5c1e8250f5fdeb35cb9..3028fb802cc2b88f51f890f3b656a33b0238f5a4 100644 --- a/cron/modules/SalesOrder/RecurringInvoice.service +++ b/cron/modules/SalesOrder/RecurringInvoice.service @@ -159,6 +159,7 @@ function getRecurringDate($recurringDate, $recurringFrequency) { case 'monthly' : $m = $m + 1; break; case 'quarterly' : $m = $m + 3; break; + case 'every 4 months': $m = $m + 4; break; case 'half-yearly' : $m = $m + 6; break; case 'yearly' : $y = $y + 1; break; @@ -185,4 +186,3 @@ function getRecurringDate($recurringDate, $recurringFrequency) { return array('validDate' => $validNextRecurringDate, 'nextRecurringDate' => $nextRecurringDate); } -?> diff --git a/cron/send_mail.php b/cron/send_mail.php index 384ea3598c1923450233cc2b0996e12fd90d0066..88efc165d31e3abae87c18e6562780f0882431e2 100755 --- a/cron/send_mail.php +++ b/cron/send_mail.php @@ -22,8 +22,8 @@ //file modified by richie -require("class.smtp.php"); -require("class.phpmailer.php"); +require_once("modules/Emails/class.smtp.php"); +require_once("modules/Emails/class.phpmailer.php"); require_once 'include/utils/CommonUtils.php'; function sendmail($to,$from,$subject,$contents,$mail_server,$mail_server_username,$mail_server_password,$filename,$smtp_auth='') diff --git a/data/CRMEntity.php b/data/CRMEntity.php index ec61d6fed0bf98fd55446e7cc9d3fc6838baaac9..2ff6a3c47640c7ccbd86a2867470917f8027d793 100644 --- a/data/CRMEntity.php +++ b/data/CRMEntity.php @@ -278,6 +278,23 @@ class CRMEntity { if ($module == 'Events') { $module = 'Calendar'; } + + $entityFields = Vtiger_Functions::getEntityModuleInfo($module); + $entityFieldNames = explode(',', $entityFields['fieldname']); + switch ($module) { + case 'HelpDesk': $entityFieldNames = array('ticket_title'); + break; + case 'Documents': $entityFieldNames = array('notes_title'); + break; + } + + $record_label = ''; + foreach($entityFieldNames as $entityFieldName) { + $record_label .= $this->column_fields[$entityFieldName]." "; + } + $label = decode_html($record_label); + $this->column_fields['label'] = $label; + if ($this->mode == 'edit') { $description_val = from_html($this->column_fields['description'], ($insertion_mode == 'edit') ? true : false); @@ -291,8 +308,8 @@ class CRMEntity { $acl = Vtiger_AccessControl::loadUserPrivileges($current_user->id); if ($acl->is_admin == true || $acl->profileGlobalPermission[1] == 0 || $acl->profileGlobalPermission[2] == 0 || $this->isWorkFlowFieldUpdate) { - $sql = "update vtiger_crmentity set smownerid=?, smgroupid=?,modifiedby=?,description=?, modifiedtime=? where crmid=?"; - $params = array($ownerid, $groupid, $current_user->id, $description_val, $adb->formatDate($date_var, true), $this->id); + $sql = "update vtiger_crmentity set smownerid=?, smgroupid=?,modifiedby=?,description=?, modifiedtime=?"; + $params = array($ownerid, $groupid, $current_user->id, $description_val, $adb->formatDate($date_var, true)); } else { $profileList = getCurrentUserProfileList(); $perm_qry = "SELECT columnname FROM vtiger_field INNER JOIN vtiger_profile2field ON vtiger_profile2field.fieldid = vtiger_field.fieldid INNER JOIN vtiger_def_org_field ON vtiger_def_org_field.fieldid = vtiger_field.fieldid WHERE vtiger_field.tabid = ? AND vtiger_profile2field.visible = 0 AND vtiger_profile2field.readonly = 0 AND vtiger_profile2field.profileid IN (" . generateQuestionMarks($profileList) . ") AND vtiger_def_org_field.visible = 0 and vtiger_field.tablename='vtiger_crmentity' and vtiger_field.displaytype in (1,3) and vtiger_field.presence in (0,2);"; @@ -302,13 +319,22 @@ class CRMEntity { $columname[] = $adb->query_result($perm_result, $i, "columnname"); } if (is_array($columname) && in_array("description", $columname)) { - $sql = "update vtiger_crmentity set smownerid=?, smgroupid=?, modifiedby=?,description=?, modifiedtime=? where crmid=?"; - $params = array($ownerid, $groupid, $current_user->id, $description_val, $adb->formatDate($date_var, true), $this->id); + $sql = "update vtiger_crmentity set smownerid=?, smgroupid=?, modifiedby=?,description=?, modifiedtime=?"; + $params = array($ownerid, $groupid, $current_user->id, $description_val, $adb->formatDate($date_var, true)); } else { - $sql = "update vtiger_crmentity set smownerid=?, smgroupid=?,modifiedby=?, modifiedtime=? where crmid=?"; - $params = array($ownerid, $groupid, $current_user->id, $adb->formatDate($date_var, true), $this->id); + $sql = "update vtiger_crmentity set smownerid=?, smgroupid=?,modifiedby=?, modifiedtime=?"; + $params = array($ownerid, $groupid, $current_user->id, $adb->formatDate($date_var, true)); } } + + if($label) { + $sql .= ", label = ? "; + array_push($params, trim($label)); + } + + $sql .= " where crmid=?"; + array_push($params,$this->id); + $adb->pquery($sql, $params); $this->column_fields['modifiedtime'] = $modified_date_var; $this->column_fields['modifiedby'] = $current_user->id; @@ -339,8 +365,19 @@ class CRMEntity { } $description_val = from_html($this->column_fields['description'], ($insertion_mode == 'edit') ? true : false); - $sql = "insert into vtiger_crmentity (crmid,smcreatorid,smownerid,smgroupid,setype,description,modifiedby,createdtime,modifiedtime,source) values(?,?,?,?,?,?,?,?,?,?)"; - $params = array($current_id, $current_user->id, $ownerid, $groupid, $module, $description_val, $current_user->id, $created_date_var, $modified_date_var,$source); + $params = array("crmid" => $current_id, "smcreatorid" => $current_user->id, "smownerid" => $ownerid, + "smgroupid" => $groupid, "setype" => $module, "description" => $description_val, + "modifiedby" => $current_user->id, "createdtime" => $created_date_var, + "modifiedtime" => $modified_date_var, "source" => $source); + + if($label) { + $params['label'] = trim($label); + } + + $insert_columns = array_keys($params); + $insert_data = array_values($params); + $sql = "insert into vtiger_crmentity (".implode(",",$insert_columns).") values(".generateQuestionMarks($insert_data).")"; + $adb->pquery($sql, $params); $this->column_fields['createdtime'] = $created_date_var; diff --git a/include/QueryGenerator/EnhancedQueryGenerator.php b/include/QueryGenerator/EnhancedQueryGenerator.php index 5b139793b72e6c4b06693080d5ab59ae76e45921..67c7275c49317c374a76cca4f1e1b4bf54087be8 100644 --- a/include/QueryGenerator/EnhancedQueryGenerator.php +++ b/include/QueryGenerator/EnhancedQueryGenerator.php @@ -726,7 +726,7 @@ class EnhancedQueryGenerator extends QueryGenerator { $startDateValue = explode(' ', $values[0]); $endDateValue = explode(' ', $values[1]); if (count($startDateValue) == 2 && count($endDateValue) == 2) { - $fieldSql .= " CAST(CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) AS DATETIME) $valueSql"; + $fieldSql .= " CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) $valueSql"; } else { $fieldSql .= "$dateFieldColumnName $valueSql"; } @@ -736,7 +736,7 @@ class EnhancedQueryGenerator extends QueryGenerator { } $values = explode(' ', $value); if (count($values) == 2) { - $fieldSql .= "$fieldGlue CAST(CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) AS DATETIME) $valueSql "; + $fieldSql .= "$fieldGlue CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) $valueSql "; } else { $fieldSql .= "$fieldGlue $dateFieldColumnName $valueSql"; } diff --git a/include/QueryGenerator/QueryGenerator.php b/include/QueryGenerator/QueryGenerator.php index bafbd821a7433f68c9553be0eb0f551593b9ae1c..3f452468efa6ad9880c977c59487aa631d10a2ff 100644 --- a/include/QueryGenerator/QueryGenerator.php +++ b/include/QueryGenerator/QueryGenerator.php @@ -799,7 +799,7 @@ class QueryGenerator { $startDateValue = explode(' ', $values[0]); $endDateValue = explode(' ', $values[1]); if(count($startDateValue) == 2 && count($endDateValue) == 2) { - $fieldSql .= " CAST(CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) AS DATETIME) $valueSql"; + $fieldSql .= " CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) $valueSql"; } else { $fieldSql .= "$dateFieldColumnName $valueSql"; } @@ -809,7 +809,7 @@ class QueryGenerator { } $values = explode(' ', $value); if(count($values) == 2) { - $fieldSql .= "$fieldGlue CAST(CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) AS DATETIME) $valueSql "; + $fieldSql .= "$fieldGlue CONCAT($dateFieldColumnName,' ',$timeFieldColumnName) $valueSql "; } else { $fieldSql .= "$fieldGlue $dateFieldColumnName $valueSql"; } diff --git a/include/Webservices/Custom/ChangePassword.php b/include/Webservices/Custom/ChangePassword.php index eafc3211e696d2902da2b17315ff68110e4bc956..716244b18c8645c1f8404b9b1f5de5b202357fdc 100644 --- a/include/Webservices/Custom/ChangePassword.php +++ b/include/Webservices/Custom/ChangePassword.php @@ -41,30 +41,45 @@ function vtws_changePassword($id, $oldPassword, $newPassword, $confirmPassword, WebServiceErrorCode::$INVALIDOLDPASSWORD)); } } - if(strcmp($newPassword, $confirmPassword) === 0) { - $db = PearDatabase::getInstance(); - $db->dieOnError = true; - $db->startTransaction(); - $success = $newUser->change_password($oldPassword, $newPassword, false); - $error = $db->hasFailedTransaction(); - $db->completeTransaction(); - if($error) { - throw new WebServiceException(WebServiceErrorCode::$DATABASEQUERYERROR, - vtws_getWebserviceTranslatedString('LBL_'. - WebServiceErrorCode::$DATABASEQUERYERROR)); - } - if(!$success) { - throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, - vtws_getWebserviceTranslatedString('LBL_'. - WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); - } - } else { - throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, - vtws_getWebserviceTranslatedString('LBL_'. - WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); - } + if(isPasswordStrong($newPassword)) { + if(strcmp($newPassword, $confirmPassword) === 0) { + $db = PearDatabase::getInstance(); + $db->dieOnError = true; + $db->startTransaction(); + $success = $newUser->change_password($oldPassword, $newPassword, false); + $error = $db->hasFailedTransaction(); + $db->completeTransaction(); + if($error) { + throw new WebServiceException(WebServiceErrorCode::$DATABASEQUERYERROR, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$DATABASEQUERYERROR)); + } + if(!$success) { + throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); + } + } else { + throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); + } + } else { + throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$PASSWORDNOTSTRONG)); + } VTWS_PreserveGlobal::flush(); return array('message' => 'Changed password successfully'); } } + +function isPasswordStrong($new_password){ + $runtime_configs = Vtiger_Runtime_Configs::getInstance(); + $password_regex = $runtime_configs->getValidationRegex('password_regex'); + if (preg_match('/'.$password_regex.'/i', $new_password) == 1) { + return true; + } + return false; +} ?> \ No newline at end of file diff --git a/include/Webservices/Utils.php b/include/Webservices/Utils.php index cd0d3cc580fb82ddfbdda0142725bf43e4b57692..2ac8e592f797da550eff5774c0ff4a960c6f78c6 100644 --- a/include/Webservices/Utils.php +++ b/include/Webservices/Utils.php @@ -1291,7 +1291,7 @@ function vtws_filedetails($fileData){ $fileName = $fileData['name']; $fileType = $fileData['type']; $fileName = html_entity_decode($fileName, ENT_QUOTES, vglobal('default_charset')); - $filenamewithpath = $fileData['path'].'_'.$fileData['encName']; + $filenamewithpath = $fileData['path'].$fileData['attachmentsid'].'_'.$fileData['storedname']; $filesize = filesize($filenamewithpath); $fileDetails['fileid'] = $fileData['attachmentsid']; $fileDetails['filename'] = $fileName; diff --git a/include/Webservices/WebServiceErrorCode.php b/include/Webservices/WebServiceErrorCode.php index 63e991f08c6cd98ab2286e8271827d415fd259e4..7b7a06d2327c04a08465a6019360ddeb60581a56 100644 --- a/include/Webservices/WebServiceErrorCode.php +++ b/include/Webservices/WebServiceErrorCode.php @@ -42,6 +42,7 @@ public static $POTENTIAL_RELATED_UPDATE_FAILED = "POTENTIAL_RELATEDLIST_UPDATE_FAILED"; public static $FAILED_TO_CREATE = "FAILED_TO_CREATE"; public static $INACTIVECURRENCY = "CURRENCY_INACTIVE"; + public static $PASSWORDNOTSTRONG = "PASSWORD_NOT_STRONG"; } ?> diff --git a/include/fields/DateTimeField.php b/include/fields/DateTimeField.php index 0fdce417a3fbdd98e306a56bae51fa0ca11eb49c..eb9dde1d86e59a12571ebae7ff8aed7452c1e592 100644 --- a/include/fields/DateTimeField.php +++ b/include/fields/DateTimeField.php @@ -372,7 +372,7 @@ class DateTimeField { case 'mm.dd.yyyy': list($m, $d, $y) = explode('.', $date); break; case 'dd.mm.yyyy': list($d, $m, $y) = explode('.', $date); break; case 'dd/mm/yyyy': list($d, $m, $y) = explode('/', $date); break; - case 'mm/dd/yyyy': list($d, $m, $y) = explode('/', $date); break; + case 'mm/dd/yyyy': list($m, $d, $y) = explode('/', $date); break; case 'mm-dd-yyyy': list($m, $d, $y) = explode('-', $date); break; case 'dd-mm-yyyy': list($d, $m, $y) = explode('-', $date); break; } diff --git a/include/utils/UserInfoUtil.php b/include/utils/UserInfoUtil.php index 2ce1811cc2c4d200d78e3cce48ab8814ec112da8..632e79778e048cfd512b512315b424c78a6657a0 100755 --- a/include/utils/UserInfoUtil.php +++ b/include/utils/UserInfoUtil.php @@ -1082,7 +1082,7 @@ function getRoleUsers($roleId) $roleRelatedUsers=Array(); for($i=0; $i<$num_rows; $i++) { - $roleRelatedUsers[$adb->query_result($result,$i,'userid')]=getFullNameFromQResult($result, $i, 'Users'); + $roleRelatedUsers[$adb->query_result($result,$i,'userid')]=$adb->query_result($result,$i,'userlabel'); } $log->debug("Exiting getRoleUsers method ..."); return $roleRelatedUsers; @@ -1302,14 +1302,14 @@ function getAllUserName() global $log; $log->debug("Entering getAllUserName() method ..."); global $adb; - $query="select * from vtiger_users where deleted=0"; + $query="select id, userlabel from vtiger_users where deleted=0"; $result = $adb->pquery($query, array()); $num_rows=$adb->num_rows($result); $user_details=Array(); for($i=0;$i<$num_rows;$i++) { $userid=$adb->query_result($result,$i,'id'); - $username=getFullNameFromQResult($result, $i, 'Users'); + $username=$adb->query_result($result,$i,'userlabel'); $user_details[$userid]=$username; } diff --git a/include/utils/VtlibUtils.php b/include/utils/VtlibUtils.php index 94e9b94c233c1b0f57c79050482c7d23fbee3a28..12b068873b3de9774376438c83da1e4ee2093b96 100644 --- a/include/utils/VtlibUtils.php +++ b/include/utils/VtlibUtils.php @@ -695,7 +695,7 @@ function vtlib_purify($input, $ignore = false) { 'data' => true ); - include_once ('libraries/htmlpurifier410/library/HTMLPurifier.auto.php'); + include_once __DIR__ . '/../../libraries/htmlpurifier410/library/HTMLPurifier.auto.php'; $config = HTMLPurifier_Config::createDefault(); $config->set('Core.Encoding', $use_charset); diff --git a/include/utils/utils.php b/include/utils/utils.php index a5a4e1b4afc5a9cae97a69f0c1d846f638b11bab..8f567566d834c4b281a0484dad6b294b8e5cb328 100755 --- a/include/utils/utils.php +++ b/include/utils/utils.php @@ -40,6 +40,7 @@ require_once 'vtlib/Vtiger/Deprecated.php'; require_once 'includes/runtime/Cache.php'; require_once 'modules/Vtiger/helpers/Util.php'; require_once 'vtlib/Vtiger/AccessControl.php'; +require_once 'includes/runtime/Configs.php'; // Constants to be defined here // For Migration status. @@ -136,24 +137,24 @@ function get_user_array($add_blank=true, $status="Active", $assigned_user="",$pr $temp_result = Array(); // Including deleted vtiger_users for now. if (empty($status)) { - $query = "SELECT id, user_name from vtiger_users"; + $query = "SELECT id, user_name, userlabel from vtiger_users"; $params = array(); } else { if($private == 'private') { $log->debug("Sharing is Private. Only the current user should be listed"); - $query = "select id as id,user_name as user_name,first_name,last_name from vtiger_users where id=? and status='Active' union select vtiger_user2role.userid as id,vtiger_users.user_name as user_name , - vtiger_users.first_name as first_name ,vtiger_users.last_name as last_name + $query = "select id as id,user_name as user_name,first_name,last_name,userlabel from vtiger_users where id=? and status='Active' union select vtiger_user2role.userid as id,vtiger_users.user_name as user_name , + vtiger_users.first_name as first_name ,vtiger_users.last_name as last_name, vtiger_users.userlabel AS userlabel from vtiger_user2role inner join vtiger_users on vtiger_users.id=vtiger_user2role.userid inner join vtiger_role on vtiger_role.roleid=vtiger_user2role.roleid where vtiger_role.parentrole like ? and status='Active' union select shareduserid as id,vtiger_users.user_name as user_name , - vtiger_users.first_name as first_name ,vtiger_users.last_name as last_name from vtiger_tmp_write_user_sharing_per inner join vtiger_users on vtiger_users.id=vtiger_tmp_write_user_sharing_per.shareduserid where status='Active' and vtiger_tmp_write_user_sharing_per.userid=? and vtiger_tmp_write_user_sharing_per.tabid=? and (user_name != 'admin' OR is_owner=1)"; + vtiger_users.first_name as first_name ,vtiger_users.last_name as last_name,vtiger_users.userlabel AS userlabel from vtiger_tmp_write_user_sharing_per inner join vtiger_users on vtiger_users.id=vtiger_tmp_write_user_sharing_per.shareduserid where status='Active' and vtiger_tmp_write_user_sharing_per.userid=? and vtiger_tmp_write_user_sharing_per.tabid=? and (user_name != 'admin' OR is_owner=1)"; $params = array($current_user->id, $current_user_parent_role_seq."::%", $current_user->id, getTabid($module)); } else { $log->debug("Sharing is Public. All vtiger_users should be listed"); - $query = "SELECT id, user_name,first_name,last_name from vtiger_users WHERE status=? and (user_name != 'admin' OR is_owner=1)"; + $query = "SELECT id, user_name,first_name,last_name,userlabel from vtiger_users WHERE status=? and (user_name != 'admin' OR is_owner=1)"; $params = array($status); } } @@ -174,7 +175,7 @@ function get_user_array($add_blank=true, $status="Active", $assigned_user="",$pr // Get the id and the name. while($row = $db->fetchByAssoc($result)) { - $temp_result[$row['id']] = getFullNameFromArray('Users', $row); + $temp_result[$row['id']] = $row['userlabel']; } $user_array = &$temp_result; diff --git a/includes/http/Session.php b/includes/http/Session.php index 789812e98096ac066cbe34c4ea8f386558a7a2b9..2c8257059f4bdf92062e61b7b0976f2bbd5309db 100644 --- a/includes/http/Session.php +++ b/includes/http/Session.php @@ -8,6 +8,16 @@ * All Rights Reserved. ************************************************************************************/ +/** + * Override default user-session storage functions if custom session connector exist. + */ +$runtime_configs = Vtiger_Runtime_Configs::getInstance(); +$custom_session_handlerclass = $runtime_configs->getConnector('session'); +if($custom_session_handlerclass) { + $handler = $custom_session_handlerclass::getInstance(); + session_set_save_handler($handler, true); +} + // Import dependencies include_once 'libraries/HTTP_Session2/HTTP/Session2.php'; diff --git a/includes/runtime/Cache.php b/includes/runtime/Cache.php index e89b800e96f62963a6a9e6dfdb1e8d5f4ddf4765..8f0f544b4618bd4dbb40175a396d60b551117239 100644 --- a/includes/runtime/Cache.php +++ b/includes/runtime/Cache.php @@ -17,7 +17,9 @@ class Vtiger_Cache { protected $connector; private function __construct() { - $this->connector = Vtiger_Cache_Connector::getInstance(); + $runtime_configs = Vtiger_Runtime_Configs::getInstance(); + $connector_class = $runtime_configs->getConnector('cache', 'Vtiger_Cache_Connector'); + $this->connector = $connector_class::getInstance(); } public static function getInstance(){ diff --git a/includes/runtime/Configs.php b/includes/runtime/Configs.php new file mode 100644 index 0000000000000000000000000000000000000000..1ced96a96301f2210cf034c2cafc7597c95f3b60 --- /dev/null +++ b/includes/runtime/Configs.php @@ -0,0 +1,61 @@ +<?php +/*+*********************************************************************************** + * The contents of this file are subject to the vtiger CRM Public License Version 1.0 + * ("License"); You may not use this file except in compliance with the License + * The Original Code is: vtiger CRM Open Source + * The Initial Developer of the Original Code is vtiger. + * Portions created by vtiger are Copyright (C) vtiger. + * All Rights Reserved. + *************************************************************************************/ + + require_once 'includes/runtime/BaseModel.php'; + + class Vtiger_Runtime_Configs extends Vtiger_Base_Model { + + private static $instance = false; + + public static function getInstance() { + if(self::$instance === false) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Function to fetch runtime connectors configured in config_override.php + * @params $identifier - Connector identifier Ex: session + * @params $default - Default connector class name. + */ + public function getConnector($identifier, $default = '') { + global $runtime_connectors; + + $connector = ''; + if(isset($runtime_connectors[$identifier])) { + $connector = $runtime_connectors[$identifier]; + } + + if(empty($connector) && !empty($default)) { + $connector = $default; + } + + return $connector; + } + + /** + * Function to fetch the value for given key + */ + public function getValidationRegex($key, $default = '') { + global $validation_regex; + + $value = ''; + if(isset($validation_regex[$key])) { + $value = $validation_regex[$key]; + } + + if(empty($value) && !empty($default)) { + $value = $default; + } + return $value; + } + } \ No newline at end of file diff --git a/kcfinder/core/uploader.php b/kcfinder/core/uploader.php index 42059a6e293457f0d943fe7facc755c6381e54a6..5bc1b968c2511737004d96d3001de65e8b508b06 100644 --- a/kcfinder/core/uploader.php +++ b/kcfinder/core/uploader.php @@ -12,6 +12,9 @@ * @link http://kcfinder.sunhater.com */ +require_once __DIR__ . '/../../include/utils/VtlibUtils.php'; +require_once __DIR__ . '/../../vtlib/Vtiger/Functions.php'; + class uploader { protected $config = array(); protected $opener = array(); diff --git a/languages/en_us/Vtiger.php b/languages/en_us/Vtiger.php index fec33408cb3c3829e387560eb50fc1ff12cb95e7..b93df6ce8be8a4de9ee36c93ca9c84760a280e53 100644 --- a/languages/en_us/Vtiger.php +++ b/languages/en_us/Vtiger.php @@ -1942,4 +1942,5 @@ $jsLanguageStrings = array( 'JS_RELATED_ACCOUNT_IS_NOT_AVAILABLE' => 'Related Organization record is not available', 'JS_RELATED_CONTACT_IS_NOT_AVAILABLE' => 'Related Contact record is not available', 'JS_REPEAT_DATE_SHOULD_BE_GREATER_THAN_START_DATE' => 'Repeat date should be greater than or equal to Start Date', + 'JS_PASSWORD_NOT_STRONG' => 'To keep your data safe, we suggest that you use a strong password <br> <ul> <li>Password should be at least 8 characters long </li> <li>Include at least one number </li> <li>Include at least one lowercase alphabet </li> <li>Include at least one uppercase alphabet </li> <li>Include at least one special character in the password </li> </ul>', ); diff --git a/languages/en_us/Webservices.php b/languages/en_us/Webservices.php index 5cac9203a02497b35b6229c8858016034861419a..8d0dfd545c22723a5343dca33afd9e44163aca8c 100644 --- a/languages/en_us/Webservices.php +++ b/languages/en_us/Webservices.php @@ -14,4 +14,5 @@ $languageStrings = array( 'LBL_DATABASE_QUERY_ERROR' => 'Database error while performing requested operation', 'LBL_INVALID_OLD_PASSWORD' => 'Invalid value given for old password.', 'LBL_NEW_PASSWORD_MISMATCH' => "New Password and confirm password don't match", + 'LBL_PASSWORD_NOT_STRONG' => 'To keep your data safe, we suggest that you use a strong password <br> <ul> <li>Password should be at least 8 characters long </li> <li>Include at least one number </li> <li>Include at least one lowercase alphabet </li> <li>Include at least one uppercase alphabet </li> <li>Include at least one special character in the password </li> </ul>', ); \ No newline at end of file diff --git a/layouts/v7/modules/Install/Step4.tpl b/layouts/v7/modules/Install/Step4.tpl index f358877d786088e4ab182ef530e6bc1e71a7c5e0..63a659f15fad0bf95bf832efeddb4cf85f47f0f7 100644 --- a/layouts/v7/modules/Install/Step4.tpl +++ b/layouts/v7/modules/Install/Step4.tpl @@ -28,6 +28,7 @@ <div class="row hide" id="errorMessage"></div> <div class="row"> <div class="col-sm-6"> + <input type='hidden' name='pwd_regex' value= {ZEND_json::encode($PWD_REGEX)} /> <table class="config-table input-table"> <thead> <tr><th colspan="2">{vtranslate('LBL_DATABASE_INFORMATION', 'Install')}</th></tr> diff --git a/layouts/v7/modules/Install/resources/Index.js b/layouts/v7/modules/Install/resources/Index.js index af13ce3359902a69cdb56c0b81b87b575d80266c..46a63970354432612a9039dbbf40fabbd743bdfc 100644 --- a/layouts/v7/modules/Install/resources/Index.js +++ b/layouts/v7/modules/Install/resources/Index.js @@ -73,7 +73,7 @@ jQuery.Class('Install_Index_Js', {}, { }); jQuery('input[name="password"]').on('blur', function (e) { - var retypePassword = jQuery('input[name="retype_password"]'); + var retypePassword = jQuery('input[name="retype_password"]'); if (retypePassword.val() != '' && retypePassword.val() !== jQuery(e.currentTarget).val()) { jQuery('#passwordError').html('Please re-enter passwords. The \"Password\" and \"Re-type password\" values do not match.'); } else { @@ -114,6 +114,13 @@ jQuery.Class('Install_Index_Js', {}, { error = true; } + var passwordField = jQuery('input[name="password"]'); + if(passwordField.val() != ''){ + if(!vtUtils.isPasswordStrong(passwordField.val())) { + error = true; + var passwordNotStrong = true; + } + } var emailField = jQuery('input[name="admin_email"]'); var regex = /^[_/a-zA-Z0-9*]+([!"#$%&'()*+,./:;<=>?\^_`{|}~-]?[a-zA-Z0-9/_/-])*@[a-zA-Z0-9]+([\_\-\.]?[a-zA-Z0-9]+)*\.([\-\_]?[a-zA-Z0-9])+(\.?[a-zA-Z0-9]+)?$/; if (!regex.test(emailField.val()) && emailField.val() != '') { @@ -133,7 +140,16 @@ jQuery.Class('Install_Index_Js', {}, { 'Warning! Invalid email address.' + '</div>' + '</div>'; - } else { + } else if(passwordNotStrong){ + content = '<div class="col-sm-12">' + + '<div class="alert errorMessageContent">' + + '<button class="close" data-dismiss="alert" type="button">x</button>' + + 'To keep your data safe, we suggest that you use a strong password <br>'+ + '<ul> <li>Password should be at least 8 characters long </li> <li>Include at least one number </li><li>Include at least one lowercase alphabet </li> <li>Include at least one uppercase alphabet </li>'+ + '<li>Include at least one special character in the password </li> </ul>' + + '</div>' + + '</div>'; + }else { content = '<div class="col-sm-12">' + '<div class="alert errorMessageContent">' + '<button class="close" data-dismiss="alert" type="button">x</button>' + diff --git a/layouts/v7/modules/Users/DetailViewBlockView.tpl b/layouts/v7/modules/Users/DetailViewBlockView.tpl index 33ba6ff0023b9b68204654283e635585ab0e7bab..18f2528e965f31245df4260364218a0e7b773286 100644 --- a/layouts/v7/modules/Users/DetailViewBlockView.tpl +++ b/layouts/v7/modules/Users/DetailViewBlockView.tpl @@ -9,6 +9,7 @@ {strip} <input type=hidden name="timeFormatOptions" data-value='{$DAY_STARTS}' /> + <input type='hidden' name='pwd_regex' value= {ZEND_json::encode($PWD_REGEX)} /> {foreach key=BLOCK_LABEL_KEY item=FIELD_MODEL_LIST from=$RECORD_STRUCTURE} {if $BLOCK_LABEL_KEY neq 'LBL_CALENDAR_SETTINGS'} {assign var=BLOCK value=$BLOCK_LIST[$BLOCK_LABEL_KEY]} diff --git a/layouts/v7/modules/Users/EditView.tpl b/layouts/v7/modules/Users/EditView.tpl index 244ea2a65d2f6c76151c48200dead5d30f46fad9..a8126d8eaf03ad3b2c63efc9a2688ecffa518419 100644 --- a/layouts/v7/modules/Users/EditView.tpl +++ b/layouts/v7/modules/Users/EditView.tpl @@ -40,6 +40,7 @@ <input type="hidden" name="defaultCallDuration" value="{$USER_MODEL->get('callduration')}" /> <input type="hidden" name="defaultOtherEventDuration" value="{$USER_MODEL->get('othereventduration')}" /> <input type="hidden" name="isPreference" value="{$IS_PREFERENCE}" /> + <input type='hidden' name='pwd_regex' value= {ZEND_json::encode($PWD_REGEX)} /> {if $IS_RELATION_OPERATION } <input type="hidden" name="sourceModule" value="{$SOURCE_MODULE}" /> <input type="hidden" name="sourceRecord" value="{$SOURCE_RECORD}" /> diff --git a/layouts/v7/modules/Users/FPLogin.tpl b/layouts/v7/modules/Users/FPLogin.tpl index e383b6c3cfb1a2b0c9b445a993d1dd4202d2f789..8d4412a6d10ea1c692f4dab9ba2d2e890bb1f955 100644 --- a/layouts/v7/modules/Users/FPLogin.tpl +++ b/layouts/v7/modules/Users/FPLogin.tpl @@ -12,7 +12,7 @@ {*<DIV>TEMPLATE: layout/modules/Users/FPLogin.tpl</DIV>*} {if $ERROR} - Error, please retry setting the password!! + {$MESSAGE} {else} <h4>Loading .... </h4> <form class="form-horizontal" name="login" id="login" method="post" action="../../../index.php?module=Users&action=Login"> diff --git a/layouts/v7/modules/Users/ListViewHeader.tpl b/layouts/v7/modules/Users/ListViewHeader.tpl index 0bf4e1c24174521b7deeb62ff31147bd99098b9f..719ec63c4e30667e77b01de068838abc75c7beb4 100644 --- a/layouts/v7/modules/Users/ListViewHeader.tpl +++ b/layouts/v7/modules/Users/ListViewHeader.tpl @@ -10,6 +10,7 @@ {strip} <div class="listViewPageDiv" id="listViewContent"> <div class="col-sm-12 col-xs-12 full-height"> + <input type='hidden' name='pwd_regex' value= {ZEND_json::encode($PWD_REGEX)} /> <div id="listview-actions" class="listview-actions-container"> <div class = "row"> <div class="btn-group col-md-2"></div> diff --git a/layouts/v7/modules/Users/resources/Detail.js b/layouts/v7/modules/Users/resources/Detail.js index 9de7ede0a17a7999699eae23b45fedf48b20268c..9970c095b3ef4db78fa77a68f42fe471dca87cfd 100644 --- a/layouts/v7/modules/Users/resources/Detail.js +++ b/layouts/v7/modules/Users/resources/Detail.js @@ -28,35 +28,41 @@ Vtiger_Detail_Js("Users_Detail_Js",{ var old_password = form.find('[name="old_password"]'); var userid = form.find('[name="userid"]').val(); - if(new_password.val() === confirm_password.val()){ - var params = { - 'data' : { - 'module': app.getModuleName(), - 'action' : "SaveAjax", - 'mode' : 'savePassword', - 'old_password' : old_password.val(), - 'new_password' : new_password.val(), - 'userid' : userid - } - }; + if(vtUtils.isPasswordStrong(new_password.val())) { + if(new_password.val() === confirm_password.val()){ + var params = { + 'data' : { + 'module': app.getModuleName(), + 'action' : "SaveAjax", + 'mode' : 'savePassword', + 'old_password' : old_password.val(), + 'new_password' : new_password.val(), + 'userid' : userid + } + }; - app.request.post(params).then( - function(err, data) { - if(err == null){ - app.helper.hideModal(); - var successMessage = app.vtranslate(data.message); - app.helper.showSuccessNotification({"message":successMessage}); - }else{ - app.helper.showErrorNotification({"message":err}); - return false; - } - } - ); - } else { - var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); - app.helper.showErrorNotification({"message":errorMessage}); - return false; - } + app.request.post(params).then( + function(err, data) { + if(err == null){ + app.helper.hideModal(); + var successMessage = app.vtranslate(data.message); + app.helper.showSuccessNotification({"message":successMessage}); + }else{ + app.helper.showErrorNotification({"message":err}); + return false; + } + } + ); + } else { + var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } + }else{ + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } } }; form.vtValidate(params); @@ -156,6 +162,11 @@ Vtiger_Detail_Js("Users_Detail_Js",{ var form = jQuery(form); var new_password = form.find('[name="new_password"]'); var confirm_password = form.find('[name="confirm_password"]'); + if(!vtUtils.isPasswordStrong(new_password.val())) { + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } if (new_password.val() !== confirm_password.val()) { var params = { diff --git a/layouts/v7/modules/Users/resources/Edit.js b/layouts/v7/modules/Users/resources/Edit.js index 09d594d14e5e515a59b6c2a32235a3fc5580bd2c..551617fe67fe4fc0348f9e0207d99277f66284b1 100644 --- a/layouts/v7/modules/Users/resources/Edit.js +++ b/layouts/v7/modules/Users/resources/Edit.js @@ -17,19 +17,24 @@ Vtiger_Edit_Js("Users_Edit_Js",{},{ */ registerRecordPreSaveEvent : function(form){ var thisInstance = this; + if(typeof form == 'undefined') { + var form = this.getForm(); + } app.event.on(Vtiger_Edit_Js.recordPresaveEvent, function(e, data) { + var editForm = thisInstance.getForm(); + editForm.find('.saveButton').attr('disabled',true); var userName = jQuery('input[name="user_name"]').val(); var newPassword = jQuery('input[name="user_password"]').val(); var confirmPassword = jQuery('input[name="confirm_password"]').val(); var record = jQuery('input[name="record"]').val(); - var firstName = jQuery('input[name="first_name"]').val(); - var lastName = jQuery('input[name="last_name"]').val(); - var specialChars = /[<\>\"\,]/; - if((specialChars.test(firstName)) || (specialChars.test(lastName))) { - app.helper.showErrorNotification({message :app.vtranslate('JS_COMMA_NOT_ALLOWED_USERS')}); - e.preventDefault(); - return false; - } + var firstName = jQuery('input[name="first_name"]').val(); + var lastName = jQuery('input[name="last_name"]').val(); + var specialChars = /[<\>\"\,]/; + if((specialChars.test(firstName)) || (specialChars.test(lastName))) { + app.helper.showErrorNotification({message :app.vtranslate('JS_COMMA_NOT_ALLOWED_USERS')}); + e.preventDefault(); + return false; + } var firstName = jQuery('input[name="first_name"]').val(); var lastName = jQuery('input[name="last_name"]').val(); if((firstName.indexOf(',') !== -1) || (lastName.indexOf(',') !== -1)) { @@ -38,10 +43,16 @@ Vtiger_Edit_Js("Users_Edit_Js",{},{ return false; } if(record == ''){ - if(newPassword != confirmPassword){ - app.helper.showErrorNotification({message :app.vtranslate('JS_REENTER_PASSWORDS')}); - e.preventDefault(); - } + if(!vtUtils.isPasswordStrong(newPassword)) { + app.helper.showErrorNotification({message :app.vtranslate('JS_PASSWORD_NOT_STRONG')}); + editForm.find('.saveButton').removeAttr('disabled'); + e.preventDefault(); + return false; + } + if(newPassword != confirmPassword){ + app.helper.showErrorNotification({message :app.vtranslate('JS_REENTER_PASSWORDS')}); + e.preventDefault(); + } if(!(userName in thisInstance.duplicateCheckCache)) { e.preventDefault(); diff --git a/layouts/v7/modules/Users/resources/List.js b/layouts/v7/modules/Users/resources/List.js index a618adcd093e17183aca54a2f79c3e443566c997..cc041dc2cce3bb8bafc9c092083cdb816e910e5a 100644 --- a/layouts/v7/modules/Users/resources/List.js +++ b/layouts/v7/modules/Users/resources/List.js @@ -192,32 +192,38 @@ Settings_Vtiger_List_Js("Settings_Users_List_Js",{ var old_password = form.find('[name="old_password"]'); var userid = form.find('[name="userid"]').val(); - if(new_password.val() === confirm_password.val()){ - var params = { - 'data' : { - 'module': app.getModuleName(), - 'action' : "SaveAjax", - 'mode' : 'savePassword', - 'old_password' : old_password.val(), - 'new_password' : new_password.val(), - 'userid' : userid - } - }; - - app.request.post(params).then( - function(err, data) { - if(err == null){ - app.helper.hideModal(); - var successMessage = app.vtranslate(data.message); - app.helper.showSuccessNotification({"message":successMessage}); - }else{ - app.helper.showErrorNotification({"message":err}); - return false; - } - } - ); - } else { - var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); + if(vtUtils.isPasswordStrong(new_password.val())) { + if(new_password.val() === confirm_password.val()){ + var params = { + 'data' : { + 'module': app.getModuleName(), + 'action' : "SaveAjax", + 'mode' : 'savePassword', + 'old_password' : old_password.val(), + 'new_password' : new_password.val(), + 'userid' : userid + } + }; + + app.request.post(params).then( + function(err, data) { + if(err == null){ + app.helper.hideModal(); + var successMessage = app.vtranslate(data.message); + app.helper.showSuccessNotification({"message":successMessage}); + }else{ + app.helper.showErrorNotification({"message":err}); + return false; + } + } + ); + } else { + var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } + } else { + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); app.helper.showErrorNotification({"message":errorMessage}); return false; } @@ -245,6 +251,11 @@ Settings_Vtiger_List_Js("Settings_Users_List_Js",{ var form = jQuery(form); var new_password = form.find('[name="new_password"]'); var confirm_password = form.find('[name="confirm_password"]'); + if(!vtUtils.isPasswordStrong(new_password.val())) { + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } if (new_password.val() !== confirm_password.val()) { var params = { diff --git a/layouts/v7/modules/Vtiger/Comment.tpl b/layouts/v7/modules/Vtiger/Comment.tpl index 5980b51dab69187181379bbd213795532c49085b..0764a0dd502d6f7ff84e21944f46c521a76dc62e 100644 --- a/layouts/v7/modules/Vtiger/Comment.tpl +++ b/layouts/v7/modules/Vtiger/Comment.tpl @@ -19,7 +19,7 @@ <div class="col-lg-12"> <div class="media"> <div class="media-left title" id="{$COMMENT->getId()}"> - {assign var=CREATOR_NAME value=$COMMENT->getCommentedByName()} + {assign var=CREATOR_NAME value={decode_html($COMMENT->getCommentedByName())}} <div class="col-lg-2 recordImage commentInfoHeader" style ="width:50px; height:50px; font-size: 30px;" data-commentid="{$COMMENT->getId()}" data-parentcommentid="{$COMMENT->get('parent_comments')}" data-relatedto = "{$COMMENT->get('related_to')}"> {assign var=IMAGE_PATH value=$COMMENT->getImagePath()} {if !empty($IMAGE_PATH)} diff --git a/layouts/v7/modules/Vtiger/DetailViewHeaderTitle.tpl b/layouts/v7/modules/Vtiger/DetailViewHeaderTitle.tpl index a2777bdfaa470ae182aafdca2767daa5685e32b9..4032e90a20a862270d51a26cfe57298b502bfc76 100644 --- a/layouts/v7/modules/Vtiger/DetailViewHeaderTitle.tpl +++ b/layouts/v7/modules/Vtiger/DetailViewHeaderTitle.tpl @@ -24,7 +24,7 @@ {foreach item=NAME_FIELD from=$MODULE_MODEL->getNameFields()} {assign var=FIELD_MODEL value=$MODULE_MODEL->getField($NAME_FIELD)} {if $FIELD_MODEL->getPermissions()} - <span class="{$NAME_FIELD}">{$RECORD->get($NAME_FIELD)}</span> + <span class="{$NAME_FIELD}">{decode_html($RECORD->get($NAME_FIELD))}</span> {/if} {/foreach} </span> diff --git a/layouts/v7/modules/Vtiger/EmailPreview.tpl b/layouts/v7/modules/Vtiger/EmailPreview.tpl index db4bce28727bba8d681caed7b1e4c1f4ac5d9234..d97ef089889a2938e6b9732b606a974bc8ef2ddc 100644 --- a/layouts/v7/modules/Vtiger/EmailPreview.tpl +++ b/layouts/v7/modules/Vtiger/EmailPreview.tpl @@ -146,7 +146,7 @@ </div> </div> </div> - <textarea style="display:none;" id="iframeDescription">{$RECORD->get('description')}</textarea> + <textarea style="display:none;" id="iframeDescription">{decode_html($RECORD->get('description'))}</textarea> <div class="row email-info-row"> <div class="col-lg-2" style="padding-right:10px;"> <div class="pull-right">{vtranslate('LBL_DESCRIPTION',$MODULE)}</div> diff --git a/layouts/v7/modules/Vtiger/Header.tpl b/layouts/v7/modules/Vtiger/Header.tpl index b6bb5e99e4131c99a394a19e63a0ff232e74dbc5..1a11b569f5d809a6ca226f3e400a4d6d21b67fcc 100644 --- a/layouts/v7/modules/Vtiger/Header.tpl +++ b/layouts/v7/modules/Vtiger/Header.tpl @@ -15,19 +15,19 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/todc/css/bootstrap.min.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/todc/css/docs.min.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/todc/css/todc-bootstrap.min.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/font-awesome/css/font-awesome.min.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/jquery/select2/select2.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/select2-bootstrap/select2-bootstrap.css'> - <link type='text/css' rel='stylesheet' href='libraries/bootstrap/js/eternicode-bootstrap-datepicker/css/datepicker3.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/jquery/jquery-ui-1.11.3.custom/jquery-ui.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/vt-icons/style.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/animate/animate.min.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/jquery/malihu-custom-scrollbar/jquery.mCustomScrollbar.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/jquery/jquery.qtip.custom/jquery.qtip.css'> - <link type='text/css' rel='stylesheet' href='layouts/v7/lib/jquery/daterangepicker/daterangepicker.css'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/todc/css/bootstrap.min.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/todc/css/docs.min.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/todc/css/todc-bootstrap.min.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/font-awesome/css/font-awesome.min.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/jquery/select2/select2.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/select2-bootstrap/select2-bootstrap.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("libraries/bootstrap/js/eternicode-bootstrap-datepicker/css/datepicker3.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/jquery/jquery-ui-1.11.3.custom/jquery-ui.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/vt-icons/style.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/animate/animate.min.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/jquery/malihu-custom-scrollbar/jquery.mCustomScrollbar.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/jquery/jquery.qtip.custom/jquery.qtip.css")}'> + <link type='text/css' rel='stylesheet' href='{vresource_url("layouts/v7/lib/jquery/daterangepicker/daterangepicker.css")}'> <input type="hidden" id="inventoryModules" value={ZEND_JSON::encode($INVENTORY_MODULES)}> @@ -59,8 +59,8 @@ var _USERMETA; {if $CURRENT_USER_MODEL} _USERMETA = { 'id' : "{$CURRENT_USER_MODEL->get('id')}", 'menustatus' : "{$CURRENT_USER_MODEL->get('leftpanelhide')}", - 'currency' : "{$USER_CURRENCY_SYMBOL}", 'currencySymbolPlacement' : "{$CURRENT_USER_MODEL->get('currency_symbol_placement')}", - 'currencyGroupingPattern' : "{$CURRENT_USER_MODEL->get('currency_grouping_pattern')}", 'truncateTrailingZeros' : "{$CURRENT_USER_MODEL->get('truncate_trailing_zeros')}"}; + 'currency' : "{decode_html($USER_CURRENCY_SYMBOL)}", 'currencySymbolPlacement' : "{$CURRENT_USER_MODEL->get('currency_symbol_placement')}", + 'currencyGroupingPattern' : "{$CURRENT_USER_MODEL->get('currency_grouping_pattern')}", 'truncateTrailingZeros' : "{$CURRENT_USER_MODEL->get('truncate_trailing_zeros')}",'userlabel':"{decode_html($CURRENT_USER_MODEL->get('userlabel'))}",}; {/if} </script> </head> diff --git a/layouts/v7/modules/Vtiger/JSResources.tpl b/layouts/v7/modules/Vtiger/JSResources.tpl index fc4fa9b219ed0e7a4958a1874a2254fcf46c8897..ff7ac3d1cd5ca34c71e7629bed3f96b98cfe5ddb 100644 --- a/layouts/v7/modules/Vtiger/JSResources.tpl +++ b/layouts/v7/modules/Vtiger/JSResources.tpl @@ -10,34 +10,34 @@ ********************************************************************************/ -->*} {strip} - <script type="text/javascript" src="layouts/v7/lib/jquery/purl.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/select2/select2.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery.class.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery-ui-1.11.3.custom/jquery-ui.js"></script> - <script type="text/javascript" src="layouts/v7/lib/todc/js/popper.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/todc/js/bootstrap.min.js"></script> - <script type="text/javascript" src="libraries/jquery/jstorage.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery-validation/jquery.validate.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery.slimscroll.min.js"></script> - <script type="text/javascript" src="libraries/jquery/jquery.ba-outside-events.min.js"></script> - <script type="text/javascript" src="libraries/jquery/defunkt-jquery-pjax/jquery.pjax.js"></script> - <script type="text/javascript" src="libraries/jquery/multiplefileupload/jquery_MultiFile.js"></script> - <script type="text/javascript" src="resources/jquery.additions.js"></script> - <script type="text/javascript" src="layouts/v7/lib/bootstrap-notify/bootstrap-notify.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/websockets/reconnecting-websocket.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery-play-sound/jquery.playSound.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/malihu-custom-scrollbar/jquery.mousewheel.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/malihu-custom-scrollbar/jquery.mCustomScrollbar.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/autoComplete/jquery.textcomplete.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery.qtip.custom/jquery.qtip.js"></script> - <script type="text/javascript" src="libraries/jquery/jquery-visibility.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/momentjs/moment.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/daterangepicker/moment.min.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/daterangepicker/jquery.daterangepicker.js"></script> - <script type="text/javascript" src="layouts/v7/lib/jquery/jquery.timeago.js"></script> - <script type="text/javascript" src="libraries/jquery/ckeditor/ckeditor.js"></script> - <script type="text/javascript" src="libraries/jquery/ckeditor/adapters/jquery.js"></script> - <script type='text/javascript' src='layouts/v7/lib/anchorme_js/anchorme.min.js'></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/purl.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/select2/select2.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery.class.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery-ui-1.11.3.custom/jquery-ui.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/todc/js/popper.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/todc/js/bootstrap.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/jstorage.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery-validation/jquery.validate.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery.slimscroll.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/jquery.ba-outside-events.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/defunkt-jquery-pjax/jquery.pjax.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/multiplefileupload/jquery_MultiFile.js')}"></script> + <script type="text/javascript" src="{vresource_url('resources/jquery.additions.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/bootstrap-notify/bootstrap-notify.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/websockets/reconnecting-websocket.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery-play-sound/jquery.playSound.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/malihu-custom-scrollbar/jquery.mousewheel.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/malihu-custom-scrollbar/jquery.mCustomScrollbar.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/autoComplete/jquery.textcomplete.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery.qtip.custom/jquery.qtip.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/jquery-visibility.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/momentjs/moment.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/daterangepicker/moment.min.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/daterangepicker/jquery.daterangepicker.js')}"></script> + <script type="text/javascript" src="{vresource_url('layouts/v7/lib/jquery/jquery.timeago.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/ckeditor/ckeditor.js')}"></script> + <script type="text/javascript" src="{vresource_url('libraries/jquery/ckeditor/adapters/jquery.js')}"></script> + <script type='text/javascript' src="{vresource_url('layouts/v7/lib/anchorme_js/anchorme.min.js')}"></script> <script type="text/javascript" src="{vresource_url('layouts/v7/modules/Vtiger/resources/Class.js')}"></script> <script type='text/javascript' src="{vresource_url('layouts/v7/resources/helper.js')}"></script> <script type="text/javascript" src="{vresource_url('layouts/v7/resources/application.js')}"></script> @@ -60,7 +60,7 @@ <script type="text/javascript" src="{vresource_url('layouts/v7/resources/v7_client_compat.js')}"></script> <!-- Added in the end since it should be after less file loaded --> - <script type="text/javascript" src="libraries/bootstrap/js/less.min.js"></script> + <script type="text/javascript" src="{vresource_url('libraries/bootstrap/js/less.min.js')}"></script> <!-- Enable tracking pageload time --> <script type="text/javascript"> diff --git a/layouts/v7/modules/Vtiger/ListColumnsEdit.tpl b/layouts/v7/modules/Vtiger/ListColumnsEdit.tpl index 710745bbdafd21d4de900be73e60b5575bd3c9d1..ead442bda2a451feeff55fef348ab6f85e50ce2f 100644 --- a/layouts/v7/modules/Vtiger/ListColumnsEdit.tpl +++ b/layouts/v7/modules/Vtiger/ListColumnsEdit.tpl @@ -67,7 +67,7 @@ {if $FIELD_MODEL->getDisplayType() eq '6'} {continue} {/if} - <div class="instafilta-target item {if array_key_exists($FIELD_MODEL->getCustomViewColumnName(), $SELECTED_FIELDS)}hide{/if}" data-cv-columnname="{$FIELD_MODEL->getCustomViewColumnName()}" data-columnname='{$FIELD_MODEL->get('column')}' data-field-id='{$FIELD_MODEL->getId()}'> + <div class="instafilta-target item {if array_key_exists(decode_html($FIELD_MODEL->getCustomViewColumnName()), $SELECTED_FIELDS)}hide{/if}" data-cv-columnname="{$FIELD_MODEL->getCustomViewColumnName()}" data-columnname='{$FIELD_MODEL->get('column')}' data-field-id='{$FIELD_MODEL->getId()}'> <span class="fieldLabel">{vtranslate($FIELD_MODEL->get('label'),$FIELD_MODULE_NAME)}</span> </div> {/foreach} diff --git a/layouts/v7/modules/Vtiger/PicklistColorMap.tpl b/layouts/v7/modules/Vtiger/PicklistColorMap.tpl index 94f8538cc31b87fa68e9d29de7f36ac2e6110200..4701f13598d8da05b4d4cfe7430df48b260b67b4 100644 --- a/layouts/v7/modules/Vtiger/PicklistColorMap.tpl +++ b/layouts/v7/modules/Vtiger/PicklistColorMap.tpl @@ -16,7 +16,7 @@ {/if} {assign var=PICKLIST_COLOR_MAP value=Settings_Picklist_Module_Model::getPicklistColorMap($FIELD_NAME, true)} {foreach item=PICKLIST_COLOR key=PICKLIST_VALUE from=$PICKLIST_COLOR_MAP} - {assign var=PICKLIST_TEXT_COLOR value=decode_html(Settings_Picklist_Module_Model::getTextColor($PICKLIST_COLOR))} + {assign var=PICKLIST_TEXT_COLOR value= decode_html(Settings_Picklist_Module_Model::getTextColor($PICKLIST_COLOR))} {assign var=CONVERTED_PICKLIST_VALUE value=Vtiger_Util_Helper::convertSpaceToHyphen($PICKLIST_VALUE)} .picklist-{$FIELD_MODEL->getId()}-{Vtiger_Util_Helper::escapeCssSpecialCharacters($CONVERTED_PICKLIST_VALUE)} { background-color: {$PICKLIST_COLOR}; diff --git a/layouts/v7/modules/Vtiger/QuickViewCommentsList.tpl b/layouts/v7/modules/Vtiger/QuickViewCommentsList.tpl index e0f5b95cfc8e94664fe69ba477a8445c69c8ed79..f7424325937b26fa951580b594a5b06e21177ea4 100644 --- a/layouts/v7/modules/Vtiger/QuickViewCommentsList.tpl +++ b/layouts/v7/modules/Vtiger/QuickViewCommentsList.tpl @@ -17,7 +17,7 @@ <div class="recentCommentsBody row"> <br> {foreach key=index item=COMMENT from=$COMMENTS} - {assign var=CREATOR_NAME value=$COMMENT->getCommentedByName()} + {assign var=CREATOR_NAME value={decode_html($COMMENT->getCommentedByName())}} <div class="commentDetails"> <div class="singleComment"> {assign var=PARENT_COMMENT_MODEL value=$COMMENT->getParentCommentModel()} diff --git a/layouts/v7/modules/Vtiger/RelatedActivities.tpl b/layouts/v7/modules/Vtiger/RelatedActivities.tpl index b9c0bdb3b54745c30bea56963535bd045fe3fe19..4858094ca04ff7726c166c4b4bb4508f90a1520d 100644 --- a/layouts/v7/modules/Vtiger/RelatedActivities.tpl +++ b/layouts/v7/modules/Vtiger/RelatedActivities.tpl @@ -113,8 +113,8 @@ <input type="hidden" class="fieldname" value='{$FIELD_MODEL->get('name')}' data-prev-value='{$FIELD_MODEL->getDisplayValue($FIELD_MODEL->get('fieldvalue'))}' /> {/if} </span> - </div> - {/if} + {/if} + </div> {/if} </div> </div> diff --git a/layouts/v7/modules/Vtiger/partials/SidebarEssentials.tpl b/layouts/v7/modules/Vtiger/partials/SidebarEssentials.tpl index cc6f933ef402db95dc6cf3fd81bb25afa985b384..f822cd53032df0172d520ae3934b1e862d1abb8c 100644 --- a/layouts/v7/modules/Vtiger/partials/SidebarEssentials.tpl +++ b/layouts/v7/modules/Vtiger/partials/SidebarEssentials.tpl @@ -23,6 +23,7 @@ <div class="list-menu-content"> {assign var="CUSTOM_VIEW_NAMES" value=array()} {if $CUSTOM_VIEWS && count($CUSTOM_VIEWS) > 0} + {assign var="IS_ADMIN" value=$CURRENT_USER_MODEL->isAdminUser()} <!-- Libertus Mod --> {foreach key=GROUP_LABEL item=GROUP_CUSTOM_VIEWS from=$CUSTOM_VIEWS} {if $GROUP_LABEL neq 'Mine' && $GROUP_LABEL neq 'Shared'} {continue} @@ -40,7 +41,7 @@ {assign var=count value=0} {assign var=LISTVIEW_URL value=$MODULE_MODEL->getListViewUrl()} {foreach item="CUSTOM_VIEW" from=$GROUP_CUSTOM_VIEWS name="customView"} - {assign var=IS_DEFAULT value=$CUSTOM_VIEW->isDefault()} + {assign var="IS_DEFAULT" value=$CUSTOM_VIEW->isDefault()} {assign var="CUSTOME_VIEW_RECORD_MODEL" value=CustomView_Record_Model::getInstanceById($CUSTOM_VIEW->getId())} {assign var="MEMBERS" value=$CUSTOME_VIEW_RECORD_MODEL->getMembers()} {assign var="LIST_STATUS" value=$CUSTOME_VIEW_RECORD_MODEL->get('status')} @@ -56,12 +57,22 @@ <div class="pull-right"> <span class="js-popover-container" style="cursor:pointer;"> <span class="fa fa-angle-down" rel="popover" data-toggle="popover" aria-expanded="true" - {if $CUSTOM_VIEW->isMine() and $CUSTOM_VIEW->get('viewname') neq 'All'} - data-deletable="{if $CUSTOM_VIEW->isDeletable()}true{else}false{/if}" data-editable="{if $CUSTOM_VIEW->isEditable()}true{else}false{/if}" - {if $CUSTOM_VIEW->isEditable()} data-editurl="{$CUSTOM_VIEW->getEditUrl()}{/if}" {if $CUSTOM_VIEW->isDeletable()} {if $SHARED_MEMBER_COUNT eq 1 or $LIST_STATUS eq 3} data-shared="1"{/if} data-deleteurl="{$CUSTOM_VIEW->getDeleteUrl()}"{/if} - {/if} - toggleClass="fa {if $IS_DEFAULT}fa-check-square-o{else}fa-square-o{/if}" data-filter-id="{$CUSTOM_VIEW->getId()}" - data-is-default="{$IS_DEFAULT}" data-defaulttoggle="{$CUSTOM_VIEW->getToggleDefaultUrl()}" data-default="{$CUSTOM_VIEW->getDuplicateUrl()}" data-isMine="{if $CUSTOM_VIEW->isMine()}true{else}false{/if}"> + {if ($CUSTOM_VIEW->isMine() || $IS_ADMIN) && $CUSTOM_VIEW->get('viewname') neq 'All'} + data-deletable="{if $CUSTOM_VIEW->isDeletable()}true{else}false{/if}" + data-editable="{if $CUSTOM_VIEW->isEditable()}true{else}false{/if}" + {if $CUSTOM_VIEW->isEditable()} data-editurl="{$CUSTOM_VIEW->getEditUrl()}{/if}" + {if $CUSTOM_VIEW->isDeletable()} + {if $SHARED_MEMBER_COUNT eq 1 or $LIST_STATUS eq 3} data-shared="1"{/if} + data-deleteurl="{$CUSTOM_VIEW->getDeleteUrl()}" + {/if} + {/if} + toggleClass="fa {if $IS_DEFAULT}fa-check-square-o{else}fa-square-o{/if}" + data-filter-id="{$CUSTOM_VIEW->getId()}" + data-is-default="{$IS_DEFAULT}" + data-defaulttoggle="{$CUSTOM_VIEW->getToggleDefaultUrl()}" + data-default="{$CUSTOM_VIEW->getDuplicateUrl()}" + data-isMine="{if $CUSTOM_VIEW->isMine()}true{else}false{/if}" + data-isadmin="{if $IS_ADMIN}true{else}false{/if}"> </span> </span> </div> diff --git a/layouts/v7/modules/Vtiger/partials/Topbar.tpl b/layouts/v7/modules/Vtiger/partials/Topbar.tpl index febd704847fcaed929f67a796629e7018ad2c70f..8ea4a0f2be380a8d11068e6c9369c3a2e1c20c1e 100644 --- a/layouts/v7/modules/Vtiger/partials/Topbar.tpl +++ b/layouts/v7/modules/Vtiger/partials/Topbar.tpl @@ -152,7 +152,7 @@ <li class="dropdown"> <div> <a href="#" class="userName dropdown-toggle pull-right" data-toggle="dropdown" role="button"> - <span class="fa fa-user" aria-hidden="true" title="{$USER_MODEL->get('first_name')} {$USER_MODEL->get('last_name')} + <span class="fa fa-user" aria-hidden="true" title="{$USER_MODEL->get('userlabel')} ({$USER_MODEL->get('user_name')})"></span> <span class="link-text-xs-only hidden-lg hidden-md hidden-sm">{$USER_MODEL->getName()}</span> </a> diff --git a/layouts/v7/modules/Vtiger/resources/Field.js b/layouts/v7/modules/Vtiger/resources/Field.js index 5bbed20b1ddbf429bb41cfa295fe2f08c5620dee..43587a9a0f694f04e985a8c75be82cf13c80ff15 100644 --- a/layouts/v7/modules/Vtiger/resources/Field.js +++ b/layouts/v7/modules/Vtiger/resources/Field.js @@ -475,7 +475,7 @@ Vtiger_Field_Js('Vtiger_Date_Field_Js',{},{ */ getUi : function() { //wrappig with another div for consistency - var html = '<div class="referencefield-wrapper"><div class="input-group date">'+ + var html = '<div class=""><div class="input-group date">'+ '<input class="inputElement dateField form-control" type="text" data-rule-date="true" data-format="'+ this.getDateFormat() +'" name="'+ this.getName() +'" value="'+ this.getValue() + '" />'+ '<span class="input-group-addon"><i class="fa fa-calendar"></i></span>'+ '</div></div>'; @@ -495,9 +495,9 @@ Vtiger_Field_Js('Vtiger_Currency_Field_Js',{},{ getUi : function() { //wrappig with another div for consistency - var html = '<div class="referencefield-wrapper"><div class="input-group">'+ + var html = '<div class=""><div class="input-group">'+ '<span class="input-group-addon" id="basic-addon1">'+this.getCurrencySymbol()+'</span>'+ - '<input class="inputElement" type="text" name="'+ this.getName() +'" data-rule-currency="true" value="'+ this.getValue() + '" />'+ + '<input class="inputElement width100per" type="text" name="'+ this.getName() +'" data-rule-currency="true" value="'+ this.getValue() + '" />'+ '</div></div>'; var element = jQuery(html); return this.addValidationToElement(element); @@ -560,7 +560,7 @@ Vtiger_Field_Js('Vtiger_Time_Field_Js',{},{ * @return - input text field */ getUi : function() { - var html = '<div class="referencefield-wrapper">'+'<div class="input-group time">'+ + var html = '<div class="">'+'<div class="input-group time">'+ '<input class="timepicker-default form-control inputElement" type="text" data-format="'+ this.getTimeFormat() +'" name="'+ this.getName() +'" value="'+ this.getValue() + '" />'+ '<span class="input-group-addon"><i class="fa fa-clock-o"></i></span>'+ '</div>'+'</div>'; diff --git a/layouts/v7/modules/Vtiger/resources/ListSidebar.js b/layouts/v7/modules/Vtiger/resources/ListSidebar.js index a8ecae4d28011dbb83ca4e5b77ff7b09bbdef55d..735d43e282d675162366efc980a6947ceecbc827 100644 --- a/layouts/v7/modules/Vtiger/resources/ListSidebar.js +++ b/layouts/v7/modules/Vtiger/resources/ListSidebar.js @@ -168,23 +168,33 @@ Vtiger.Class('Vtiger_ListSidebar_Js',{},{ toggleEle.attr('data-is-default', jQuery(ele).data('is-default')); toggleEle.attr('data-filter-id', jQuery(ele).data('filter-id')); contentEle.find('.toggleDefault i').attr('class', jQuery(ele).attr('toggleClass')); - editEle.attr('data-id', jQuery(ele).data('id')); + editEle.attr('data-id', jQuery(ele).data('id')); deleteEle.attr('data-id', jQuery(ele).data('id')); - if(jQuery(ele).data('ismine') === false){ + // Libertus Mod - data-isadmin also added to SideBarEssentials.tpl + if((jQuery(ele).data('ismine') === false) && (jQuery(ele).data('isadmin') === false)) { contentEle.find('.editFilter').css("display", "none"); contentEle.find('.deleteFilter').css("display","none"); } + + if (!jQuery(ele).data('deletable')) { + contentEle.find('.deleteFilter').remove(); // This propogates to the next iteration of the each() method; removing the entire li + } else { + if(contentEle.find('li').hasClass('deleteFilter') === false) { + contentEle.find('ul').prepend(deleteEle); // Add back if missing + } + contentEle.find('.deleteFilter').removeClass('disabled'); + } + if (!jQuery(ele).data('editable')) { - contentEle.find('.editFilter').remove(); + contentEle.find('.editFilter').remove(); // This propogates to the next iteration of the each() method; removing the entire li } else { + if(contentEle.find('li').hasClass('editFilter') === false) { + contentEle.find('ul').prepend(editEle); // Add back if missing + } contentEle.find('.editFilter').removeClass('disabled'); } - if (!jQuery(ele).data('deletable')) { - contentEle.find('.deleteFilter').remove(); - } else { - contentEle.find('.deleteFilter').removeClass('disabled'); - } + var options = { html: true, placement: 'left', diff --git a/layouts/v7/modules/Vtiger/resources/Utils.js b/layouts/v7/modules/Vtiger/resources/Utils.js index c51009b48d412c9d050e8d58079915bbaa794d2e..26cae5e2d7a57232cbb14991d3e3cdb59c9af20f 100644 --- a/layouts/v7/modules/Vtiger/resources/Utils.js +++ b/layouts/v7/modules/Vtiger/resources/Utils.js @@ -135,7 +135,9 @@ var vtUtils = { showShortcuts: true, autoClose : false, duration : 500 - }); + }).on('datepicker-opened', function(e){ + vtUtils.addMask(jQuery('.date-picker-wrapper:visible')); + }).on('datepicker-closed',vtUtils.removeMask); }else{ var elementDateFormat = element.data('dateFormat'); if(typeof elementDateFormat !== 'undefined') { @@ -375,5 +377,36 @@ var vtUtils = { return string.replace(tags, function ($0, $1) { return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; }); - } + }, + + addMask: function (container) { + if (container.length && jQuery('#vt-mask').length == 0) { + var mask = '<div id="vt-mask" class="vt-page-mask" ></div>' + container.before(mask); + } + }, + + removeMask: function () { + if (jQuery('#vt-mask').length) { + jQuery('#vt-mask').remove(); + } + }, + + isPasswordStrong : function(password) { + /* + * ^ The password string will start this way + * (?=.*[a-z]) The string must contain at least 1 lowercase alphabetical character + * (?=.*[A-Z]) The string must contain at least 1 uppercase alphabetical character + * (?=.*[0-9]) The string must contain at least 1 numeric character + * (?=.*[!@#\$%\^&\*]) The string must contain at least one special character, but we are escaping reserved RegEx characters to avoid conflict + * (?=.{8,}) The string must be eight characters or longer + */ + var password_regex = jQuery('[name="pwd_regex"]').val(); + if((typeof password_regex != 'undefined') && (password_regex != '')){ + var strongPasswordRegex = new RegExp(password_regex); + var isStrong = strongPasswordRegex.test(password)? true : false; + return isStrong; + } + return false; + }, } diff --git a/layouts/v7/modules/Vtiger/uitypes/Currency.tpl b/layouts/v7/modules/Vtiger/uitypes/Currency.tpl index 2b7ca02d7f9381b786ee7ebd92a3154c407bceca..fb79b42457c8abdb1984c19b56ed3ed1bb1914d5 100644 --- a/layouts/v7/modules/Vtiger/uitypes/Currency.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/Currency.tpl @@ -16,8 +16,8 @@ {assign var="FIELD_NAME" value=$FIELD_MODEL->getFieldName()} {/if} {if $FIELD_MODEL->get('uitype') eq '71'} -<div class="input-group"> - <span class="input-group-addon">{$USER_MODEL->get('currency_symbol')}</span> +<div class="input-group inputElement"> + <span class="input-group-addon input-group-addon-right">{$USER_MODEL->get('currency_symbol')}</span> <input id="{$MODULE}_editView_fieldName_{$FIELD_NAME}" type="text" class="inputElement currencyField" name="{$FIELD_NAME}" value="{$FIELD_MODEL->getEditViewDisplayValue($FIELD_MODEL->get('fieldvalue'))}" {if !empty($SPECIAL_VALIDATOR)}data-validator='{Zend_Json::encode($SPECIAL_VALIDATOR)}'{/if} {if $FIELD_INFO["mandatory"] eq true} data-rule-required = "true" {/if} data-rule-currency='true' diff --git a/layouts/v7/modules/Vtiger/uitypes/OwnerFieldSearchView.tpl b/layouts/v7/modules/Vtiger/uitypes/OwnerFieldSearchView.tpl index 4fdd538d585da954607ce22c4d4277fcec339812..a48cef1382b4424748f27f2dc41c4ab37fff98cb 100644 --- a/layouts/v7/modules/Vtiger/uitypes/OwnerFieldSearchView.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/OwnerFieldSearchView.tpl @@ -40,7 +40,7 @@ {if count($ALL_ACTIVEGROUP_LIST) gt 0} <optgroup label="{vtranslate('LBL_GROUPS')}"> {foreach key=OWNER_ID item=OWNER_NAME from=$ALL_ACTIVEGROUP_LIST} - <option value="{$OWNER_NAME}" data-picklistvalue= '{$OWNER_NAME}' {if in_array(trim($OWNER_NAME),$SEARCH_VALUES)} selected {/if} + <option value="{$OWNER_NAME}" data-picklistvalue= '{$OWNER_NAME}' {if in_array(trim(decode_html($OWNER_NAME)),$SEARCH_VALUES)} selected {/if} {if array_key_exists($OWNER_ID, $ACCESSIBLE_GROUP_LIST)} data-recordaccess=true {else} data-recordaccess=false {/if} > {$OWNER_NAME} </option> diff --git a/layouts/v7/modules/Vtiger/uitypes/OwnerGroupFieldSearchView.tpl b/layouts/v7/modules/Vtiger/uitypes/OwnerGroupFieldSearchView.tpl index 51edb7f9ee6784a66ddf3d9b1a1a8726dc84bf45..847603524c2464f94bc4ba12cc6566c65e9e2e3c 100644 --- a/layouts/v7/modules/Vtiger/uitypes/OwnerGroupFieldSearchView.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/OwnerGroupFieldSearchView.tpl @@ -26,7 +26,7 @@ <select class="select2 listSearchContributor {$ASSIGNED_USER_ID}"name="{$ASSIGNED_USER_ID}" multiple id="group_id" style="display:none"> {if count($ALL_ACTIVEGROUP_LIST) gt 0} {foreach key=OWNER_ID item=OWNER_NAME from=$ALL_ACTIVEGROUP_LIST} - <option value="{$OWNER_NAME}" data-picklistvalue= '{$OWNER_NAME}' {if in_array(trim($OWNER_NAME),$SEARCH_VALUES)} selected {/if} + <option value="{$OWNER_NAME}" data-picklistvalue= '{$OWNER_NAME}' {if in_array(trim(decode_html($OWNER_NAME)),$SEARCH_VALUES)} selected {/if} {if array_key_exists($OWNER_ID, $ACCESSIBLE_GROUP_LIST)} data-recordaccess=true {else} data-recordaccess=false {/if} > {$OWNER_NAME} </option> diff --git a/layouts/v7/modules/Vtiger/uitypes/String.tpl b/layouts/v7/modules/Vtiger/uitypes/String.tpl index 00ec549f4208b9fc32266f91d059df3358108c72..8d4f57446a9ed77ee9514bf164408d20a67c0150 100644 --- a/layouts/v7/modules/Vtiger/uitypes/String.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/String.tpl @@ -16,7 +16,7 @@ {if (!$FIELD_NAME)} {assign var="FIELD_NAME" value=$FIELD_MODEL->getFieldName()} {/if} - <input id="{$MODULE}_editView_fieldName_{$FIELD_NAME}" type="text" data-fieldname="{$FIELD_NAME}" data-fieldtype="string" class="inputElement {if $FIELD_MODEL->isNameField()}nameField{/if}" name="{$FIELD_NAME}" value="{$FIELD_MODEL->get('fieldvalue')}" + <input id="{$MODULE}_editView_fieldName_{$FIELD_NAME}" type="text" data-fieldname="{$FIELD_NAME}" data-fieldtype="string" class="inputElement {if $FIELD_MODEL->isNameField()}nameField{/if}" name="{$FIELD_NAME}" value="{decode_html($FIELD_MODEL->get('fieldvalue'))|htmlentities}" {if $FIELD_MODEL->get('uitype') eq '3' || $FIELD_MODEL->get('uitype') eq '4'|| $FIELD_MODEL->isReadOnly()} {if $FIELD_MODEL->get('uitype') neq '106'} readonly diff --git a/layouts/v7/skins/vtiger/style.less b/layouts/v7/skins/vtiger/style.less index bb8ce42475d8eba3a3c6cd0d553c90c206a74500..9a09e0f159da72e718685883415d9aa57db27377 100644 --- a/layouts/v7/skins/vtiger/style.less +++ b/layouts/v7/skins/vtiger/style.less @@ -1656,7 +1656,8 @@ ul.unstyled{ .listViewEntries .relatedListEntryValues .value{ vertical-align: middle; width: 80%; - display: inline-block; + display: inline-block; + max-width: 300px; } .floatThead-table{ @@ -2679,6 +2680,12 @@ strong, b, th{ border-left:1px solid #ddd; } +.input-group.inputElement .input-group-addon-right{ + border:none; + border-left:0px; + border-right:1px solid #ddd; +} + .fieldBlockContainer textarea.inputElement{ height : auto; } @@ -6292,6 +6299,9 @@ hr{ .width75per{ width: 75%; } +.width100per{ + width: 100% !important; +} #_mbox_pwd{ padding: 3px 8px; } @@ -6896,7 +6906,7 @@ div.tooltip-inner{ width:80% !important; } .input-group-addon{ - line-height: 1.5; + line-height: inherit; border-width: thin; } .input-group{ @@ -8665,4 +8675,20 @@ body .fc { border-width: thin; padding: 30px; } +} + +#vt-mask{ + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; } \ No newline at end of file diff --git a/layouts/vlayout/modules/Emails/EmailPreviewPrint.tpl b/layouts/vlayout/modules/Emails/EmailPreviewPrint.tpl index f1c1d829031aeaba3c41a923c35c77dd26757594..dc75e49041880f00f0acce8fc69310461f85ebb1 100644 --- a/layouts/vlayout/modules/Emails/EmailPreviewPrint.tpl +++ b/layouts/vlayout/modules/Emails/EmailPreviewPrint.tpl @@ -20,7 +20,7 @@ {assign var="TO_EMAIL" value=$TO_EMAILS|replace:'[':''} {assign var="TO_EMAIL_VALUE" value=$TO_EMAIL|replace:'"':''} <span style="position:absolute;right:6%;top:3%;font-family:'Lucida Grande';font-size:15px"> - {$USER_MODEL->get('last_name')} {$USER_MODEL->get('first_name')} <{$USER_MODEL->get('email1')}> + {$USER_MODEL->get('userlabel')} <{$USER_MODEL->get('email1')}> </span><hr/> <span style="font-family:'Lucida Grande';font-size:15px"> {$RECORD->get('subject')} diff --git a/modules/Calendar/CalendarCommon.php b/modules/Calendar/CalendarCommon.php index cfecd4161fba2e0a6e0740ab8c8a25b28a9e83c1..93ea23f7eea69c46923fcf1ab544662976470903 100644 --- a/modules/Calendar/CalendarCommon.php +++ b/modules/Calendar/CalendarCommon.php @@ -225,12 +225,11 @@ function generateIcsAttachment($record) { $fileName = str_replace(' ', '_', decode_html($record['subject'])); $assignedUserId = $record['user_id']; $userModel = Users_Record_Model::getInstanceById($assignedUserId, 'Users'); - $firstName = $userModel->entity->column_fields['first_name']; - $lastName = $userModel->entity->column_fields['last_name']; + $userLabel = $userModel->entity->column_fields['userlabel']; $email = $userModel->entity->column_fields['email1']; $fp = fopen('test/upload/'.$fileName.'.ics', "w"); fwrite($fp, "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\n"); - fwrite($fp, "ORGANIZER;CN=".$firstName." ".$lastName.":MAILTO:".$email."\n"); + fwrite($fp, "ORGANIZER;CN=".$userLabel.":MAILTO:".$email."\n"); fwrite($fp, "DTSTART:".date('Ymd\THis\Z', strtotime($record['st_date_time']))."\n"); fwrite($fp, "DTEND:".date('Ymd\THis\Z', strtotime($record['end_date_time']))."\n"); fwrite($fp, "DTSTAMP:".date('Ymd\THis\Z')."\n"); diff --git a/modules/Calendar/models/Module.php b/modules/Calendar/models/Module.php index adfd7ed18202e1c4030f2ce14c66174e2ac92f8f..8cccc7648df9028f2234a84b0892c9f6aa4efe10 100644 --- a/modules/Calendar/models/Module.php +++ b/modules/Calendar/models/Module.php @@ -305,11 +305,11 @@ class Calendar_Module_Model extends Vtiger_Module_Model { $db = PearDatabase::getInstance(); $currentUser = Users_Record_Model::getCurrentUserModel(); if($currentUser->isAdminUser()) { - $query = "SELECT first_name,last_name, id AS userid + $query = "SELECT userlabel, id AS userid FROM vtiger_users WHERE status='Active' AND (user_name != 'admin' OR is_owner = 1) AND id <> ?"; $result = $db->pquery($query, array($id)); } else { - $query = "SELECT vtiger_users.first_name,vtiger_users.last_name, vtiger_users.id AS userid + $query = "SELECT vtiger_users.userlabel, vtiger_users.id AS userid FROM vtiger_sharedcalendar RIGHT JOIN vtiger_users ON vtiger_sharedcalendar.userid=vtiger_users.id AND vtiger_users.status= 'Active' WHERE vtiger_sharedcalendar.sharedid=? OR (vtiger_users.status='Active' AND vtiger_users.calendarsharedtype='public' AND vtiger_users.id <> ?)"; $result = $db->pquery($query, array($id, $id)); @@ -319,7 +319,7 @@ class Calendar_Module_Model extends Vtiger_Module_Model { $userIds = Array(); for($i=0; $i<$rows; $i++){ $id = $db->query_result($result,$i,'userid'); - $userName = $db->query_result($result,$i,'first_name').' '.$db->query_result($result,$i,'last_name'); + $userName = $db->query_result($result,$i,'userlabel'); $userIds[$id] =$userName; } diff --git a/modules/CustomView/CustomView.php b/modules/CustomView/CustomView.php index af3ea9c50a6ec8a4ea64fbca1272d75940fc1add..9860ad30079b6287a557bc5dd302dc1c2914dec6 100644 --- a/modules/CustomView/CustomView.php +++ b/modules/CustomView/CustomView.php @@ -209,7 +209,7 @@ class CustomView extends CRMEntity { if ($markselected == false) $selected = ''; - $ssql = "select vtiger_customview.*, vtiger_users.first_name,vtiger_users.last_name from vtiger_customview inner join vtiger_tab on vtiger_tab.name = vtiger_customview.entitytype + $ssql = "select vtiger_customview.*, vtiger_users.first_name,vtiger_users.last_name,vtiger_users.userlabel from vtiger_customview inner join vtiger_tab on vtiger_tab.name = vtiger_customview.entitytype left join vtiger_users on vtiger_customview.userid = vtiger_users.id "; $ssql .= " where vtiger_tab.tabid=?"; $sparams = array($tabid); @@ -230,7 +230,7 @@ class CustomView extends CRMEntity { if ($cvrow['status'] == CV_STATUS_DEFAULT || $cvrow['userid'] == $current_user->id) { $disp_viewname = $viewname; } else { - $userName = getFullNameFromArray('Users', $cvrow); + $userName = $cvrow['userlabel']; $disp_viewname = $viewname . " [" . $userName . "] "; } @@ -1182,9 +1182,9 @@ class CustomView extends CRMEntity { if ($this->customviewmodule == 'Calendar' && ($columns[1] == 'date_start' || $columns[1] == 'due_date')) { $tableColumnSql = ''; if ($columns[1] == 'date_start') { - $tableColumnSql = "CAST((CONCAT(date_start,' ',time_start)) AS DATETIME)"; + $tableColumnSql = "(CONCAT(date_start,' ',time_start))"; } else { - $tableColumnSql = "CAST((CONCAT(due_date,' ',time_end)) AS DATETIME)"; + $tableColumnSql = "(CONCAT(due_date,' ',time_end))"; } } else { $tableColumnSql = $columns[0] . "." . $columns[1]; diff --git a/modules/Emails/Emails.php b/modules/Emails/Emails.php index e05b9fa3d06f81b73e48ef4091a0dd45542e4ec9..1a552f8bd4f992229ba0c9a8c22052adaf5555af 100644 --- a/modules/Emails/Emails.php +++ b/modules/Emails/Emails.php @@ -380,7 +380,7 @@ class Emails extends CRMEntity { onclick=\"return window.open("index.php?module=Users&return_module=Emails&action=Popup&popuptype=detailview&select=enable&form=EditView&form_submit=true&return_id=' . $id . '&recordid=' . $id . '","test","width=640,height=520,resizable=0,scrollbars=0");\" type="button">'; - $query = 'SELECT vtiger_users.id, vtiger_users.first_name,vtiger_users.last_name, vtiger_users.user_name, vtiger_users.email1, vtiger_users.email2, vtiger_users.secondaryemail , vtiger_users.phone_home, vtiger_users.phone_work, vtiger_users.phone_mobile, vtiger_users.phone_other, vtiger_users.phone_fax from vtiger_users inner join vtiger_salesmanactivityrel on vtiger_salesmanactivityrel.smid=vtiger_users.id and vtiger_salesmanactivityrel.activityid=?'; + $query = 'SELECT vtiger_users.id, vtiger_users.first_name,vtiger_users.last_name, vtiger_users.user_name, vtiger_users.email1, vtiger_users.email2, vtiger_users.secondaryemail , vtiger_users.phone_home, vtiger_users.phone_work, vtiger_users.phone_mobile, vtiger_users.phone_other, vtiger_users.phone_fax,vtiger_users.userlabel from vtiger_users inner join vtiger_salesmanactivityrel on vtiger_salesmanactivityrel.smid=vtiger_users.id and vtiger_salesmanactivityrel.activityid=?'; $result = $adb->pquery($query, array($id)); $noofrows = $adb->num_rows($result); @@ -397,12 +397,7 @@ class Emails extends CRMEntity { $entries = Array(); - if (is_admin($current_user)) { - $entries[] = getFullNameFromArray('Users', $row); - } else { - $entries[] = getFullNameFromArray('Users', $row); - } - + $entries[] = $row['userlabel']; $entries[] = $row['user_name']; $entries[] = $row['email1']; if ($email == '') diff --git a/modules/Emails/mail.php b/modules/Emails/mail.php index 5824fbdb73ad5d59e5b18475d9e3e8e41065c909..58d9f5f6caa9fb1fa612f15e22a12987e3971d9f 100755 --- a/modules/Emails/mail.php +++ b/modules/Emails/mail.php @@ -153,11 +153,11 @@ function addSignature($contents, $fromname, $fromEmail = '') { $sign = VTCacheUtils::getUserSignature($fromname); if ($sign == null) { $sign = VTCacheUtils::getUserSignature($fromEmail); - $result = $adb->pquery("select signature, first_name, last_name from vtiger_users where user_name=? or user_name=? or email1=? or email2=? or secondaryemail=?", array($fromname, $fromEmail, $fromEmail, $fromEmail, $fromEmail)); + $result = $adb->pquery("select signature, userlabel from vtiger_users where user_name=? or user_name=? or email1=? or email2=? or secondaryemail=?", array($fromname, $fromEmail, $fromEmail, $fromEmail, $fromEmail)); $sign = $adb->query_result($result,0,"signature"); VTCacheUtils::setUserSignature($fromname, $sign); VTCacheUtils::setUserSignature($fromEmail, $sign); - VTCacheUtils::setUserFullName($fromname, $adb->query_result($result,0,"first_name").' '.$adb->query_result($result,0,"last_name")); + VTCacheUtils::setUserFullName($fromname, $adb->query_result($result,0,"userlabel")); } $sign = nl2br($sign); @@ -215,10 +215,10 @@ function setMailerProperties($mail,$subject,$contents,$from_email,$from_name,$to $userFullName = $HELPDESK_SUPPORT_NAME; } if(empty($userFullName)) { - $rs = $adb->pquery("select first_name,last_name from vtiger_users where user_name=?", array($from_name)); + $rs = $adb->pquery("select first_name,last_name,userlabel from vtiger_users where user_name=?", array($from_name)); $num_rows = $adb->num_rows($rs); if($num_rows > 0) { - $fullName = getFullNameFromQResult($rs, 0, 'Users'); + $fullName = $adb->query_result($rs, 0, 'userlabel'); VTCacheUtils::setUserFullName($from_name, $fullName); } } else { diff --git a/modules/Emails/models/Record.php b/modules/Emails/models/Record.php index 11fe808dd6baa4a3c89b53681bc5a6a1d45212f9..d92abdf56dad8b1ac73f9d4bb183ef3ee6d1da14 100644 --- a/modules/Emails/models/Record.php +++ b/modules/Emails/models/Record.php @@ -182,7 +182,7 @@ class Emails_Record_Model extends Vtiger_Record_Model { //Adding attachments to mail if(is_array($attachments)) { foreach($attachments as $attachment) { - $fileNameWithPath = $rootDirectory.$attachment['path'].$attachment['fileid']."_".$attachment['storedname']; + $fileNameWithPath = $rootDirectory.$attachment['filenamewithpath']; if(is_file($fileNameWithPath)) { $mailer->AddAttachment($fileNameWithPath, $attachment['attachment']); } @@ -277,12 +277,24 @@ class Emails_Record_Model extends Vtiger_Record_Model { $attachmentsList = array(); if($numOfRows) { for($i=0; $i<$numOfRows; $i++) { - $attachmentsList[$i]['fileid'] = $db->query_result($attachmentRes, $i, 'attachmentsid'); - $attachmentsList[$i]['attachment'] = decode_html($db->query_result($attachmentRes, $i, 'name')); - $attachmentsList[$i]['storedname'] = decode_html($db->query_result($attachmentRes, $i, 'storedname')); - $path = $db->query_result($attachmentRes, $i, 'path'); + $attachmentId = $db->query_result($attachmentRes, $i, 'attachmentsid'); + $rawFileName = $db->query_result($attachmentRes, $i, 'name'); + $storedName = $db->query_result($attachmentRes, $i, 'storedname'); + $path = $db->query_result($attachmentRes, $i, 'path'); + if($storedName) { + $filename = $storedName; + } else { + $filename = $rawFileName; + } + $attachmentsList[$i]['attachment'] = decode_html($rawFileName); + $attachmentsList[$i]['fileid'] = $attachmentId; + $attachmentsList[$i]['storedname'] = decode_html($storedName); $attachmentsList[$i]['path'] = $path; - $attachmentsList[$i]['size'] = filesize($path.$attachmentsList[$i]['fileid'].'_'.$attachmentsList[$i]['storedname']); + $saved_filename = $attachmentId."_".$filename; + $filenamewithpath = $path.$saved_filename; + $filesize = filesize($filenamewithpath); + $attachmentsList[$i]['filenamewithpath'] = $filenamewithpath; + $attachmentsList[$i]['size'] = $filesize; $attachmentsList[$i]['type'] = $db->query_result($attachmentRes, $i, 'type'); $attachmentsList[$i]['cid'] = $db->query_result($attachmentRes, $i, 'cid'); } diff --git a/modules/HelpDesk/models/Module.php b/modules/HelpDesk/models/Module.php index e0b2c4acfad56cf225365c626216287bdc157833..1ba6037196d9b5a81ad68c5043a48a6e8e6ffd23 100644 --- a/modules/HelpDesk/models/Module.php +++ b/modules/HelpDesk/models/Module.php @@ -69,7 +69,7 @@ class HelpDesk_Module_Model extends Vtiger_Module_Model { if(in_array('Open', $picklistvaluesmap)) $params[] = 'Open'; if(count($params) > 0) { - $result = $db->pquery('SELECT count(*) AS count, COALESCE(vtiger_groups.groupname,concat(vtiger_users.first_name, " " ,vtiger_users.last_name)) as name, COALESCE(vtiger_groups.groupid,vtiger_users.id) as id FROM vtiger_troubletickets + $result = $db->pquery('SELECT count(*) AS count, COALESCE(vtiger_groups.groupname,vtiger_users.userlabel) as name, COALESCE(vtiger_groups.groupid,vtiger_users.id) as id FROM vtiger_troubletickets INNER JOIN vtiger_crmentity ON vtiger_troubletickets.ticketid = vtiger_crmentity.crmid LEFT JOIN vtiger_users ON vtiger_users.id=vtiger_crmentity.smownerid AND vtiger_users.status="ACTIVE" LEFT JOIN vtiger_groups ON vtiger_groups.groupid=vtiger_crmentity.smownerid diff --git a/modules/Install/views/Index.php b/modules/Install/views/Index.php index 54a29e5f49bfd76b83e7b3f86111cdbd0e6a9003..61097a2adf417a509ec35443e1cc2d7da0b7f8d2 100644 --- a/modules/Install/views/Index.php +++ b/modules/Install/views/Index.php @@ -111,6 +111,10 @@ class Install_Index_view extends Vtiger_View_Controller { $viewer->assign('ADMIN_LASTNAME', $defaultParameters['admin_lastname']); $viewer->assign('ADMIN_PASSWORD', $defaultParameters['admin_password']); $viewer->assign('ADMIN_EMAIL', $defaultParameters['admin_email']); + + $runtime_configs = Vtiger_Runtime_Configs::getInstance(); + $password_regex = $runtime_configs->getValidationRegex('password_regex'); + $viewer->assign('PWD_REGEX', $password_regex); $viewer->view('Step4.tpl', $moduleName); } diff --git a/modules/Migration/models/Module.php b/modules/Migration/models/Module.php index 4e1bdc24ddac0e8fc0d1b60d0b56e75e4e738c3b..3c85143ea3415a45be261e734ffb6988a07fd78d 100644 --- a/modules/Migration/models/Module.php +++ b/modules/Migration/models/Module.php @@ -46,6 +46,7 @@ class Migration_Module_Model extends Vtiger_Module_Model { array('711' => '7.1.1'), array('720' => '7.2.0'), array('730' => '7.3.0'), + array('740' => '7.4.0'), ); return $versions; } diff --git a/modules/Migration/schema/660_to_700.php b/modules/Migration/schema/660_to_700.php index bbe645d9c5deddf3598961f5df9dc02813a860d5..3ee263c843c80bcf2ec9cbe93de06d022a4551ee 100644 --- a/modules/Migration/schema/660_to_700.php +++ b/modules/Migration/schema/660_to_700.php @@ -2225,9 +2225,6 @@ if(defined('VTIGER_UPGRADE')) { rename('modules/Vtiger/resources', 'modules/Vtiger/resources_650'); } - //Update existing package modules - Install_Utils_Model::installModules(); - //recalculate user files to finish RecalculateSharingRules(); diff --git a/modules/Migration/schema/701_to_710.php b/modules/Migration/schema/701_to_710.php index 7562d9775ccd0776a4a41f1f55a6605a8fea3d1c..5610f0c5058a97c893c11b13483ad2f9f9e54cdb 100644 --- a/modules/Migration/schema/701_to_710.php +++ b/modules/Migration/schema/701_to_710.php @@ -426,8 +426,5 @@ if (defined('VTIGER_UPGRADE')) { } //END::Updating custom view and report columns, filters for createdtime and modifiedtime fields as typeofdata (T~...) is being transformed to (DT~...) - //Update existing package modules - Install_Utils_Model::installModules(); - echo '<br>Succecssfully vtiger version updated to <b>7.1.0</b><br>'; } diff --git a/modules/Migration/schema/711_to_720.php b/modules/Migration/schema/711_to_720.php index 8a75abe106a4f715588103df37b73a7701564a17..8ef2b7b493c6fd41c4a4f953ec57ee22a16943ee 100644 --- a/modules/Migration/schema/711_to_720.php +++ b/modules/Migration/schema/711_to_720.php @@ -18,7 +18,4 @@ if (defined('VTIGER_UPGRADE')) { if(!in_array($columnName,$columns)) { $db->pquery('ALTER TABLE vtiger_attachments ADD COLUMN storedname varchar(255) NULL AFTER path', array()); } - - //Update existing package modules - Install_Utils_Model::installModules(); } diff --git a/modules/Migration/schema/720_to_730.php b/modules/Migration/schema/720_to_730.php index 78f27fb6235761b5d5c933d1df365e58487ec34d..d048909f4299812fc2e5a43129018382b39c96cf 100644 --- a/modules/Migration/schema/720_to_730.php +++ b/modules/Migration/schema/720_to_730.php @@ -297,7 +297,4 @@ if (defined('VTIGER_UPGRADE')) { $db->pquery('ALTER TABLE vtiger_calendar_default_activitytypes ADD COLUMN conditions VARCHAR(255) DEFAULT ""', array()); echo 'Conditions column in vtiger_calendar_default_activitytypes updated'; - - //Update existing package modules - Install_Utils_Model::installModules(); } \ No newline at end of file diff --git a/modules/Migration/schema/730_to_740.php b/modules/Migration/schema/730_to_740.php new file mode 100644 index 0000000000000000000000000000000000000000..563270f25458b9dc5dc5404179556b6a343608e6 --- /dev/null +++ b/modules/Migration/schema/730_to_740.php @@ -0,0 +1,51 @@ +<?php +/*+******************************************************************************** + * The contents of this file are subject to the vtiger CRM Public License Version 1.0 + * ("License"); You may not use this file except in compliance with the License + * The Original Code is: vtiger CRM Open Source + * The Initial Developer of the Original Code is vtiger. + * Portions created by vtiger are Copyright (C) vtiger. + * All Rights Reserved. + *********************************************************************************/ + +if (defined('VTIGER_UPGRADE')) { + global $current_user, $adb; + $db = PearDatabase::getInstance(); + + $eventManager = new VTEventsManager($db); + $className = 'Vtiger_RecordLabelUpdater_Handler'; + $eventManager->unregisterHandler($className); + echo "Unregistered record label update handler.<br>"; + + $moduleName = 'Users'; + $moduleModel = Vtiger_Module_Model::getInstance($moduleName); + $fieldName = 'userlabel'; + $blockModel = Vtiger_Block_Model::getInstance('LBL_MORE_INFORMATION', $moduleModel); + if ($blockModel) { + $fieldModel = Vtiger_Field_Model::getInstance($fieldName, $moduleModel); + if (!$fieldModel) { + $fieldModel = new Vtiger_Field(); + $fieldModel->name = $fieldName; + $fieldModel->label = 'User Label'; + $fieldModel->table = 'vtiger_users'; + $fieldModel->columntype = 'VARCHAR(255)'; + $fieldModel->typeofdata = 'V~O'; + $fieldModel->displaytype= 3; + $blockModel->addField($fieldModel); + echo "<br>Successfully added <b>$fieldName</b> field to <b>$moduleName</b><br>"; + } + } + $db->pquery("UPDATE vtiger_users SET $fieldName=TRIM(CONCAT(first_name, ' ' , last_name))", array()); + echo "<br>Successfully updated <b>$fieldName</b> value as concatenate of firstname and lastname for <b>$moduleName</b> module<br>"; + + vimport('~modules/Users/CreateUserPrivilegeFile.php'); + $result = $db->pquery('SELECT id FROM vtiger_users', array()); + $count = $db->num_rows($result); + while ($row = $db->fetch_array($result)) { + $userId = $row['id']; + createUserPrivilegesfile($userId); + echo "<br>Successfully recreated <b>User's privileges</b> file for id:<b>$userId</b><br>"; + } + echo "<br>Successfully completed concatenate of firstname and lastname as label in <b>$moduleName</b> module<br>"; + +} \ No newline at end of file diff --git a/modules/Migration/views/Index.php b/modules/Migration/views/Index.php index caecf7dba720fbd2bc1f23719ababfe80ae7d160..f83ca730551b919501fc55df85bdfa729a09ca4d 100644 --- a/modules/Migration/views/Index.php +++ b/modules/Migration/views/Index.php @@ -129,6 +129,19 @@ class Migration_Index_View extends Vtiger_View_Controller { echo "<table class='config-table'><tr><th><span><b><font color='red'> There is no Database Changes from ".$migrateVersions[$i]." ==> ".$migrateVersions[$i+1]."</font></b></span></th></tr></table>"; } } + + //During migration process we need to upgrade the package changes + if(defined('VTIGER_UPGRADE')) { + + echo "<table class='config-table'><tr><th><span><b><font color='red'> Upgrading Modules -- Starts. </font></b></span></th></tr></table>"; + echo "<table class='config-table'>"; + + //Update existing package modules + Install_Utils_Model::installModules(); + + echo "<table class='config-table'><tr><th><span><b><font color='red'>Upgrading Modules -- Ends.</font></b></span></th></tr></table>"; + + } //update vtiger version in db $migrationModuleModel->updateVtigerVersion(); diff --git a/modules/Potentials/models/Module.php b/modules/Potentials/models/Module.php index bb6a9a7a342bc4e0214120d1085187c3424dc2b7..cc90a4899688ce31c7bac35925c3c5cfd84a7231 100644 --- a/modules/Potentials/models/Module.php +++ b/modules/Potentials/models/Module.php @@ -99,7 +99,7 @@ class Potentials_Module_Model extends Vtiger_Module_Model { $params[] = $picklistValue; } - $result = $db->pquery('SELECT COUNT(*) AS count, concat(first_name," ",last_name) as last_name, vtiger_potential.sales_stage, vtiger_groups.groupname FROM vtiger_potential + $result = $db->pquery('SELECT COUNT(*) AS count, vtiger_users.userlabel as last_name, vtiger_potential.sales_stage, vtiger_groups.groupname FROM vtiger_potential INNER JOIN vtiger_crmentity ON vtiger_potential.potentialid = vtiger_crmentity.crmid AND vtiger_crmentity.deleted = 0 LEFT JOIN vtiger_users ON vtiger_users.id=vtiger_crmentity.smownerid AND vtiger_users.status="ACTIVE" LEFT JOIN vtiger_groups ON vtiger_groups.groupid=vtiger_crmentity.smownerid'.Users_Privileges_Model::getNonAdminAccessControlQuery($this->getName()).' @@ -134,7 +134,7 @@ class Potentials_Module_Model extends Vtiger_Module_Model { unset($picklistvaluesmap['Closed Won']);unset($picklistvaluesmap['Closed Lost']); foreach($picklistvaluesmap as $picklistValue) $params[] = $picklistValue; - $result = $db->pquery('SELECT sum(amount) AS amount, concat(first_name," ",last_name) as last_name, vtiger_potential.sales_stage FROM vtiger_potential + $result = $db->pquery('SELECT sum(amount) AS amount, vtiger_users.userlabel as last_name, vtiger_potential.sales_stage FROM vtiger_potential INNER JOIN vtiger_crmentity ON vtiger_potential.potentialid = vtiger_crmentity.crmid INNER JOIN vtiger_users ON vtiger_users.id=vtiger_crmentity.smownerid AND vtiger_users.status="ACTIVE" AND vtiger_crmentity.deleted = 0 '.Users_Privileges_Model::getNonAdminAccessControlQuery($this->getName()). @@ -168,7 +168,7 @@ class Potentials_Module_Model extends Vtiger_Module_Model { $params[] = $dateFilter['end']; } - $result = $db->pquery('SELECT sum(amount) amount, concat(first_name," ",last_name) as last_name,vtiger_users.id as id,DATE_FORMAT(closingdate, "%d-%m-%Y") AS closingdate FROM vtiger_potential + $result = $db->pquery('SELECT sum(amount) amount, vtiger_users.userlabel as last_name,vtiger_users.id as id,DATE_FORMAT(closingdate, "%d-%m-%Y") AS closingdate FROM vtiger_potential INNER JOIN vtiger_crmentity ON vtiger_potential.potentialid = vtiger_crmentity.crmid INNER JOIN vtiger_users ON vtiger_users.id=vtiger_crmentity.smownerid AND vtiger_users.status="ACTIVE" AND vtiger_crmentity.deleted = 0 '.Users_Privileges_Model::getNonAdminAccessControlQuery($this->getName()).'WHERE sales_stage = ? '.' '.$dateFilterSql.' GROUP BY smownerid', $params); diff --git a/modules/Reports/ReportRun.php b/modules/Reports/ReportRun.php index 0dc0f675c63ebd64d67b21b32e31dd25e2356035..b4515a308f0dcd7dffe2fee63de31b967cb78cd0 100644 --- a/modules/Reports/ReportRun.php +++ b/modules/Reports/ReportRun.php @@ -541,7 +541,7 @@ class ReportRun extends CRMEntity { if ($module == 'Emails') { $columnSQL = "YEAR(cast(concat($emailTableName.date_start,' ',$emailTableName.time_start) as DATE)) AS Emails_Date_Sent_Year"; } else { - $columnSQL = "YEAR(cast(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) as DATETIME)) AS Calendar_Start_Date_and_Time_Year"; + $columnSQL = "YEAR(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start)) AS Calendar_Start_Date_and_Time_Year"; } } else if ($selectedfields[0] == "vtiger_crmentity" . $this->primarymodule) { $columnSQL = "YEAR(vtiger_crmentity." . $selectedfields[1] . ") AS '" . decode_html($header_label) . "_Year'"; @@ -554,7 +554,7 @@ class ReportRun extends CRMEntity { if ($module == 'Emails') { $columnSQL = "MONTHNAME(cast(concat($emailTableName.date_start,' ',$emailTableName.time_start) as DATE)) AS Emails_Date_Sent_Month"; } else { - $columnSQL = "MONTHNAME(cast(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) as DATETIME)) AS Calendar_Start_Date_and_Time_Month"; + $columnSQL = "MONTHNAME(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start)) AS Calendar_Start_Date_and_Time_Month"; } } else if ($selectedfields[0] == "vtiger_crmentity" . $this->primarymodule) { $columnSQL = "MONTHNAME(vtiger_crmentity." . $selectedfields[1] . ") AS '" . decode_html($header_label) . "_Month'"; @@ -567,7 +567,7 @@ class ReportRun extends CRMEntity { if ($module == 'Emails') { $columnSQL = "CONCAT('Week ',WEEK(cast(concat($emailTableName.date_start,' ',$emailTableName.time_start) as DATE), 1)) AS Emails_Date_Sent_Week"; } else { - $columnSQL = "CONCAT('Week ',WEEK(cast(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) as DATETIME), 1)) AS Calendar_Start_Date_and_Time_Week"; + $columnSQL = "CONCAT('Week ',WEEK(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start), 1)) AS Calendar_Start_Date_and_Time_Week"; } } else if ($selectedfields[0] == "vtiger_crmentity" . $this->primarymodule) { $columnSQL = "CONCAT('Week ',WEEK(vtiger_crmentity." . $selectedfields[1] . ", 1)) AS '" . decode_html($header_label) . "_Week'"; @@ -580,7 +580,7 @@ class ReportRun extends CRMEntity { if ($module == 'Emails') { $columnSQL = "date_format(cast(concat($emailTableName.date_start,' ',$emailTableName.time_start) as DATE), '%M %Y') AS Emails_Date_Sent_Month"; } else { - $columnSQL = "date_format(cast(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) as DATETIME), '%M %Y') AS Calendar_Start_Date_and_Time_Month"; + $columnSQL = "date_format(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start), '%M %Y') AS Calendar_Start_Date_and_Time_Month"; } } else if ($selectedfields[0] == "vtiger_crmentity" . $this->primarymodule) { $columnSQL = "date_format(vtiger_crmentity." . $selectedfields[1] . ", '%M %Y') AS '" . decode_html($header_label) . "_Month'"; @@ -593,7 +593,7 @@ class ReportRun extends CRMEntity { if ($module == 'Emails') { $columnSQL = "cast(concat($emailTableName.date_start,' ',$emailTableName.time_start) as DATE) AS Emails_Date_Sent"; } else { - $columnSQL = "cast(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) as DATETIME) AS Calendar_Start_Date_and_Time"; + $columnSQL = "concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) AS Calendar_Start_Date_and_Time"; } } else if ($selectedfields[0] == "vtiger_crmentity" . $this->primarymodule) { $columnSQL = "vtiger_crmentity." . $selectedfields[1] . " AS '" . decode_html($header_label) . "'"; @@ -608,7 +608,7 @@ class ReportRun extends CRMEntity { if ($module == 'Emails') { $columnSQL = "cast(concat($emailTableName.date_start,' ',$emailTableName.time_start) as DATE) AS Emails_Date_Sent"; } else { - $columnSQL = "cast(concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) as DATETIME) AS Calendar_Start_Date_and_Time"; + $columnSQL = "concat(vtiger_activity.date_start,' ',vtiger_activity.time_start) AS Calendar_Start_Date_and_Time"; } } elseif (stristr($selectedfields[0], "vtiger_users") && ($selectedfields[1] == 'user_name')) { $temp_module_from_tablename = str_replace("vtiger_users", "", $selectedfields[0]); @@ -1079,7 +1079,7 @@ class ReportRun extends CRMEntity { $endDateTime = "DATE_FORMAT('$endDateTime', '%m%d')"; } else { if ($selectedFields[0] == 'vtiger_activity' && ($selectedFields[1] == 'date_start')) { - $tableColumnSql = 'CAST((CONCAT(date_start, " ", time_start)) AS DATETIME)'; + $tableColumnSql = '(CONCAT(date_start, " ", time_start))'; } else { if (empty($emailTableName)) { $tableColumnSql = $selectedFields[0] . '.' . $selectedFields[1]; @@ -1110,7 +1110,7 @@ class ReportRun extends CRMEntity { } if ($selectedFields[0] == 'vtiger_activity' && ($selectedFields[1] == 'date_start')) { - $tableColumnSql = 'CAST((CONCAT(date_start, " ", time_start)) AS DATETIME)'; + $tableColumnSql = '(CONCAT(date_start, " ", time_start))'; } else { if (empty($emailTableName)) { $tableColumnSql = $selectedFields[0] . '.' . $selectedFields[1]; @@ -1532,7 +1532,7 @@ class ReportRun extends CRMEntity { } else { if ($selectedfields[0] == 'vtiger_activity' && ($selectedfields[1] == 'date_start')) { $tableColumnSql = ''; - $tableColumnSql = "CAST((CONCAT(date_start,' ',time_start)) AS DATETIME)"; + $tableColumnSql = "(CONCAT(date_start,' ',time_start))"; } else { $tableColumnSql = $selectedfields[0] . "." . $selectedfields[1]; } diff --git a/modules/Reports/models/ListView.php b/modules/Reports/models/ListView.php index 6bcfb4aef0abeabc226c08002aec6f21fb3480e4..f1557fa0032d361a25aafc5ea5edd4ae8e58b2b6 100644 --- a/modules/Reports/models/ListView.php +++ b/modules/Reports/models/ListView.php @@ -100,7 +100,7 @@ class Reports_ListView_Model extends Vtiger_ListView_Model { if (!empty($orderBy) && $orderBy === 'smownerid') { $fieldModel = Vtiger_Field_Model::getInstance('assigned_user_id', $moduleModel); if ($fieldModel->getFieldDataType() == 'owner') { - $orderBy = 'COALESCE(CONCAT(vtiger_users.first_name,vtiger_users.last_name),vtiger_groups.groupname)'; + $orderBy = 'COALESCE(vtiger_users.userlabel,vtiger_groups.groupname)'; } } if(!empty($orderBy)) { diff --git a/modules/Settings/Groups/models/Member.php b/modules/Settings/Groups/models/Member.php index bcc72c1e075e9e9b42f71449a280fe804155af6a..9a3dc90fe1a25c4b917d2d87bfbc6c0c2e83a8ce 100644 --- a/modules/Settings/Groups/models/Member.php +++ b/modules/Settings/Groups/models/Member.php @@ -102,7 +102,7 @@ class Settings_Groups_Member_Model extends Vtiger_Base_Model { $tableName = $tables[self::MEMBER_TYPE_USERS]['table']; $tableIndex = $tables[self::MEMBER_TYPE_USERS]['index']; $refIndex = $tables[self::MEMBER_TYPE_USERS]['refIndex']; - $sql = "SELECT vtiger_users.id, vtiger_users.last_name, vtiger_users.first_name FROM vtiger_users + $sql = "SELECT vtiger_users.id, vtiger_users.last_name, vtiger_users.first_name, vtiger_users.userlabel FROM vtiger_users INNER JOIN $tableName ON $tableName.$refIndex = vtiger_users.id WHERE $tableName.$tableIndex = ?"; $params = array($groupModel->getId()); @@ -113,7 +113,7 @@ class Settings_Groups_Member_Model extends Vtiger_Base_Model { $row = $db->query_result_rowdata($result, $i); $userId = $row['id']; $qualifiedId = self::getQualifiedId(self::MEMBER_TYPE_USERS, $userId); - $name = getFullNameFromArray('Users', $row); + $name = $row['userlabel']; $member = new self(); $members[$qualifiedId] = $member->set('id', $qualifiedId)->set('name', $name)->set('userId', $userId); } diff --git a/modules/Settings/Vtiger/models/ListView.php b/modules/Settings/Vtiger/models/ListView.php index 1e944f10c12bcfb9408ad44043befb162c0daa67..0ffe2428a446c98ec83432c24afc95a06a60557b 100644 --- a/modules/Settings/Vtiger/models/ListView.php +++ b/modules/Settings/Vtiger/models/ListView.php @@ -72,7 +72,7 @@ class Settings_Vtiger_ListView_Model extends Vtiger_Base_Model { if (!empty($orderBy) && $orderBy === 'smownerid') { $fieldModel = Vtiger_Field_Model::getInstance('assigned_user_id', $moduleModel); if ($fieldModel->getFieldDataType() == 'owner') { - $orderBy = 'COALESCE(CONCAT(vtiger_users.first_name,vtiger_users.last_name),vtiger_groups.groupname)'; + $orderBy = 'COALESCE(vtiger_users.userlabel,vtiger_groups.groupname)'; } } if (!empty($orderBy)) { diff --git a/modules/Users/Users.php b/modules/Users/Users.php index 6c024b967b63c8567bdb775120487fdcf970f5f1..232ad18e8d0ad3a9826d1db9896dd17ccc87d7cd 100755 --- a/modules/Users/Users.php +++ b/modules/Users/Users.php @@ -112,7 +112,7 @@ class Users extends CRMEntity { ); //Default Fields for Email Templates -- Pavani - var $emailTemplate_defaultFields = array('first_name','last_name','title','department','phone_home','phone_mobile','signature','email1','email2','address_street','address_city','address_state','address_country','address_postalcode'); + var $emailTemplate_defaultFields = array('first_name','last_name','userlabel','title','department','phone_home','phone_mobile','signature','email1','email2','address_street','address_city','address_state','address_country','address_postalcode'); var $popup_fields = array('last_name'); @@ -621,14 +621,14 @@ class Users extends CRMEntity { } function fill_in_additional_detail_fields() { - $query = "SELECT u1.first_name, u1.last_name from vtiger_users u1, vtiger_users u2 where u1.id = u2.reports_to_id AND u2.id = ? and u1.deleted=0"; + $query = "SELECT u1.first_name, u1.last_name, u1.userlabel from vtiger_users u1, vtiger_users u2 where u1.id = u2.reports_to_id AND u2.id = ? and u1.deleted=0"; $result =$this->db->pquery($query, array($this->id), true, "Error filling in additional detail vtiger_fields") ; $row = $this->db->fetchByAssoc($result); $this->log->debug("additional detail query results: $row"); if($row != null) { - $this->reports_to_name = stripslashes(getFullNameFromArray('Users', $row)); + $this->reports_to_name = stripslashes($row['userlabel']); } else { $this->reports_to_name = ''; @@ -768,6 +768,20 @@ class Users extends CRMEntity { // We will set the crypt_type based on the insertion_mode $crypt_type = ''; + // userlabel is a field. So, setting to column_fields will take care for update and insert as well + if($table_name == 'vtiger_users') { + $entityFields = Vtiger_Functions::getEntityModuleInfo($module); + $entityFieldNames = explode(',', $entityFields['fieldname']); + + $userlabel = ''; + foreach($entityFieldNames as $entityFieldName) { + $userlabel .= $this->column_fields[$entityFieldName]." "; + } + $userlabel = trim(decode_html($userlabel)); + + $this->column_fields['userlabel'] = strip_tags($userlabel); + } + if($insertion_mode == 'edit') { $update = ''; $update_params = array(); @@ -1795,7 +1809,7 @@ class Users extends CRMEntity { $reportsTo = null; foreach($allUsers as $user) { $userName = strtolower($user->get('user_name')); - $firstLastName = strtolower($user->get('first_name')." ".$user->get('last_name')); + $firstLastName = strtolower($user->get('userlabel')); if(strtolower($fieldValue) == $userName || strtolower($fieldValue) == $firstLastName) { $reportsTo = $user->getId(); break; diff --git a/modules/Users/actions/ForgotPassword.php b/modules/Users/actions/ForgotPassword.php index 38556c0214a8909a9b58dddcfb06c89906b76cae..721e3262e7b3489aec7c1de4d3cb972c58c1fccf 100644 --- a/modules/Users/actions/ForgotPassword.php +++ b/modules/Users/actions/ForgotPassword.php @@ -43,13 +43,19 @@ class Users_ForgotPassword_Action { $userId = getUserId_Ol($userName); $user = Users::getActiveAdminUser(); $wsUserId = vtws_getWebserviceEntityId('Users', $userId); - vtws_changePassword($wsUserId, '', $newPassword, $confirmPassword, $user); + try{ + vtws_changePassword($wsUserId, '', $newPassword, $confirmPassword, $user); + } catch (Exception $e){ + $viewer->assign('ERROR', true); + $viewer->assign('MESSAGE', html_entity_decode($e->getMessage())); + } } else { $viewer->assign('ERROR', true); + $viewer->assign('MESSAGE', 'Error, please retry setting the password!!'); } - $shortURLModel->delete(); - $viewer->assign('USERNAME', $userName); - $viewer->assign('PASSWORD', $newPassword); + $shortURLModel->delete(); + $viewer->assign('USERNAME', $userName); + $viewer->assign('PASSWORD', $newPassword); $viewer->view('FPLogin.tpl', 'Users'); } diff --git a/modules/Users/models/Module.php b/modules/Users/models/Module.php index f875d3c5024d4feae09f2bc8367c9c485e341ed7..e08a3042e0f7c6b38fc5beffcd10d3c820143eca 100644 --- a/modules/Users/models/Module.php +++ b/modules/Users/models/Module.php @@ -46,10 +46,10 @@ class Users_Module_Model extends Vtiger_Module_Model { if(!empty($searchValue)) { $db = PearDatabase::getInstance(); - $query = 'SELECT * FROM vtiger_users WHERE (first_name LIKE ? OR last_name LIKE ?) AND status = ?'; + $query = 'SELECT * FROM vtiger_users WHERE userlabel LIKE ? AND status = ?'; $currentUser = Users_Record_Model::getCurrentUserModel(); $allSubordinates = $currentUser->getAllSubordinatesByReportsToField($currentUser->getId()); - $params = array("%$searchValue%", "%$searchValue%", 'Active'); + $params = array("%$searchValue%", 'Active'); // do not allow the subordinates if(count($allSubordinates) > 0) { diff --git a/modules/Users/models/Record.php b/modules/Users/models/Record.php index 206b8aac48a07bbea5c097cb9049585292f4e989..b6a0c43c692bcef3638059411d8c76c9ad1e9a3b 100644 --- a/modules/Users/models/Record.php +++ b/modules/Users/models/Record.php @@ -275,7 +275,7 @@ class Users_Record_Model extends Vtiger_Record_Model { if($subordinateRoleUsers) { foreach($subordinateRoleUsers as $role=>$users) { foreach($users as $user) { - $subordinateUsers[$user] = $privilegesModel->get('first_name').' '.$privilegesModel->get('last_name'); + $subordinateUsers[$user] = $privilegesModel->get('userlabel'); } } } @@ -458,7 +458,7 @@ class Users_Record_Model extends Vtiger_Record_Model { $currentUserRoleModel = Settings_Roles_Record_Model::getInstanceById($this->getRole()); $childernRoles = $currentUserRoleModel->getAllChildren(); $users = $this->getAllUsersOnRoles($childernRoles); - $currentUserDetail = array($this->getId() => $this->get('first_name').' '.$this->get('last_name')); + $currentUserDetail = array($this->getId() => $this->get('userlabel')); $users = $currentUserDetail + $users; return $users; } @@ -488,14 +488,12 @@ class Users_Record_Model extends Vtiger_Record_Model { for($i=0; $i<$noOfUsers; ++$i) { $userIds[] = $db->query_result($result, $i, 'userid'); } - $query = 'SELECT id, first_name, last_name FROM vtiger_users WHERE status = ? AND id IN ('. generateQuestionMarks($userIds).')'; + $query = 'SELECT id, userlabel FROM vtiger_users WHERE status = ? AND id IN ('. generateQuestionMarks($userIds).')'; $result = $db->pquery($query, array('ACTIVE', $userIds)); $noOfUsers = $db->num_rows($result); for($j=0; $j<$noOfUsers; ++$j) { $userId = $db->query_result($result, $j,'id'); - $firstName = $db->query_result($result, $j, 'first_name'); - $lastName = $db->query_result($result, $j, 'last_name'); - $subUsers[$userId] = $firstName .' '.$lastName; + $subUsers[$userId] = $db->query_result($result, $j, 'userlabel'); } } return $subUsers; @@ -807,7 +805,12 @@ class Users_Record_Model extends Vtiger_Record_Model { * @return <String> - Entity Display Name for the record */ public function getDisplayName() { - return getFullNameFromArray($this->getModuleName(),$this->getData()); + $userLabel = $this->get('userlabel'); + + if (!$userLabel) { + $userLabel = getFullNameFromArray($this->getModuleName(),$this->getData()); + } + return $userLabel; } /** diff --git a/modules/Users/views/List.php b/modules/Users/views/List.php index 40bff8e2273cee67f5e593119a6e496017bf78ff..6daceada7b59aa2eaececf5cd26d395138ff5526 100644 --- a/modules/Users/views/List.php +++ b/modules/Users/views/List.php @@ -167,6 +167,10 @@ class Users_List_View extends Settings_Vtiger_List_View { $viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel()); $viewer->assign('SEARCH_VALUE', $searchValue); $viewer->assign('SEARCH_DETAILS', $searchParams); + + $runtime_configs = Vtiger_Runtime_Configs::getInstance(); + $password_regex = $runtime_configs->getValidationRegex('password_regex'); + $viewer->assign('PWD_REGEX', $password_regex); } /** diff --git a/modules/Users/views/PreferenceDetail.php b/modules/Users/views/PreferenceDetail.php index 154544d3f55115a62d547cbab687e136a8e53bb2..8df592a4bade5dcef8a54ee074cf44f1e435ae2c 100644 --- a/modules/Users/views/PreferenceDetail.php +++ b/modules/Users/views/PreferenceDetail.php @@ -172,6 +172,9 @@ class Users_PreferenceDetail_View extends Vtiger_Detail_View { $viewer->assign("DAY_STARTS", Zend_Json::encode($dayStartPicklistValues)); $viewer->assign('IMAGE_DETAILS', $recordModel->getImageDetails()); + $runtime_configs = Vtiger_Runtime_Configs::getInstance(); + $password_regex = $runtime_configs->getValidationRegex('password_regex'); + $viewer->assign('PWD_REGEX', $password_regex); return parent::process($request); } diff --git a/modules/Users/views/PreferenceEdit.php b/modules/Users/views/PreferenceEdit.php index cc1e4032604a127e3d2082fc649dbc9cd3460027..108680c552c91ddd9a44efd64fcd978c689fbbf4 100644 --- a/modules/Users/views/PreferenceEdit.php +++ b/modules/Users/views/PreferenceEdit.php @@ -121,7 +121,7 @@ Class Users_PreferenceEdit_View extends Vtiger_Edit_View { $fieldsInfo[$fieldName] = $fieldModel->getFieldInfo(); } $viewer->assign('FIELDS_INFO', json_encode($fieldsInfo)); - + if($display) { $this->preProcessDisplay($request); } @@ -150,6 +150,10 @@ Class Users_PreferenceEdit_View extends Vtiger_Edit_View { $viewer->assign("DAY_STARTS", Zend_Json::encode($dayStartPicklistValues)); $viewer->assign('TAG_CLOUD', $recordModel->getTagCloudStatus()); $viewer->assign('USER_MODEL', Users_Record_Model::getCurrentUserModel()); + + $runtime_configs = Vtiger_Runtime_Configs::getInstance(); + $password_regex = $runtime_configs->getValidationRegex('password_regex'); + $viewer->assign('PWD_REGEX', $password_regex); parent::process($request); } diff --git a/modules/Vtiger/RecordLabelUpdater.php b/modules/Vtiger/RecordLabelUpdater.php deleted file mode 100644 index 2c4447f88fa2c8fdb7f32dffdbc1f8eeb343f6a9..0000000000000000000000000000000000000000 --- a/modules/Vtiger/RecordLabelUpdater.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -/* +*********************************************************************************** - * The contents of this file are subject to the vtiger CRM Public License Version 1.0 - * ("License"); You may not use this file except in compliance with the License - * The Original Code is: vtiger CRM Open Source - * The Initial Developer of the Original Code is vtiger. - * Portions created by vtiger are Copyright (C) vtiger. - * All Rights Reserved. - * *********************************************************************************** */ -require_once 'include/events/VTEventHandler.inc'; - -class Vtiger_RecordLabelUpdater_Handler extends VTEventHandler { - - function handleEvent($eventName, $data) { - global $adb; - - if ($eventName == 'vtiger.entity.aftersave') { - $labelInfo = getEntityName($data->getModuleName(), $data->getId(), true); - - if ($labelInfo) { - $label = decode_html($labelInfo[$data->getId()]); - $adb->pquery('UPDATE vtiger_crmentity SET label=? WHERE crmid=?', array($label, $data->getId())); - } - } - } -} \ No newline at end of file diff --git a/modules/Vtiger/handlers/RecordLabelUpdater.php b/modules/Vtiger/handlers/RecordLabelUpdater.php deleted file mode 100644 index b4472d3d2bf564b4cff1c974ef9628800c3eeb44..0000000000000000000000000000000000000000 --- a/modules/Vtiger/handlers/RecordLabelUpdater.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/* +*********************************************************************************** - * The contents of this file are subject to the vtiger CRM Public License Version 1.0 - * ("License"); You may not use this file except in compliance with the License - * The Original Code is: vtiger CRM Open Source - * The Initial Developer of the Original Code is vtiger. - * Portions created by vtiger are Copyright (C) vtiger. - * All Rights Reserved. - * *********************************************************************************** */ -require_once 'include/events/VTEventHandler.inc'; - -class Vtiger_RecordLabelUpdater_Handler extends VTEventHandler { - - function handleEvent($eventName, $data) { - global $adb; - - if ($eventName == 'vtiger.entity.aftersave') { - $record = $data->getId(); - $module = $data->getModuleName(); - - if($module === 'Users') { - return; - } - - $labelInfo = getEntityName($module, $record, true); - - if ($labelInfo) { - $label = decode_html($labelInfo[$data->getId()]); - $adb->pquery('UPDATE vtiger_crmentity SET label=? WHERE crmid=?', array($label, $record)); - } - } - } -} \ No newline at end of file diff --git a/modules/Vtiger/models/RelationListView.php b/modules/Vtiger/models/RelationListView.php index 74e829bec76437437709147fcc5797b477078712..d09fc9d2853a600a919452a2aba974deb8cad382 100644 --- a/modules/Vtiger/models/RelationListView.php +++ b/modules/Vtiger/models/RelationListView.php @@ -318,7 +318,7 @@ class Vtiger_RelationListView_Model extends Vtiger_Base_Model { $query = $selectAndFromClause.' WHERE '.$whereCondition; $query .= ' ORDER BY '.$qualifiedOrderBy.'.label '.$sortOrder; } elseif($orderByFieldModuleModel && $orderByFieldModuleModel->isOwnerField()) { - $query .= ' ORDER BY COALESCE(CONCAT(vtiger_users.first_name,vtiger_users.last_name),vtiger_groups.groupname) '.$sortOrder; + $query .= ' ORDER BY COALESCE(vtiger_users.userlabel,vtiger_groups.groupname) '.$sortOrder; } else{ // Qualify the the column name with table to remove ambugity $qualifiedOrderBy = $orderBy; diff --git a/modules/Vtiger/uitypes/Reference.php b/modules/Vtiger/uitypes/Reference.php index 3ac8f7e77494f1eaa18a0ebd3a7cb288fbdc94a7..2769a45538a146f81e2e7e45f613f42c03e5a84f 100644 --- a/modules/Vtiger/uitypes/Reference.php +++ b/modules/Vtiger/uitypes/Reference.php @@ -46,9 +46,9 @@ class Vtiger_Reference_UIType extends Vtiger_Base_UIType { $referenceModuleName = $referenceModule->get('name'); if($referenceModuleName == 'Users') { $db = PearDatabase::getInstance(); - $nameResult = $db->pquery('SELECT first_name, last_name FROM vtiger_users WHERE id = ?', array($value)); + $nameResult = $db->pquery('SELECT userlabel FROM vtiger_users WHERE id = ?', array($value)); if($db->num_rows($nameResult)) { - return $db->query_result($nameResult, 0, 'first_name').' '.$db->query_result($nameResult, 0, 'last_name'); + return $db->query_result($nameResult, 0, 'userlabel'); } } else { $fieldModel = $this->get('field'); diff --git a/modules/com_vtiger_workflow/VTWorkflowManager.inc b/modules/com_vtiger_workflow/VTWorkflowManager.inc index 0ea8f1235af3b2a4337ded33ab62b9e1dc936f2f..bfd394c5dc3b3ce68bd9279379c3013ea0ee85a7 100644 --- a/modules/com_vtiger_workflow/VTWorkflowManager.inc +++ b/modules/com_vtiger_workflow/VTWorkflowManager.inc @@ -24,6 +24,7 @@ class VTWorkflowManager{ function __construct($adb){ $this->adb = $adb; + $this->setMaxAllowedScheduledWorkflows(); } function save($workflow){ @@ -105,13 +106,24 @@ class VTWorkflowManager{ $result = $adb->pquery($query, $params); return $adb->query_result($result, 0, 'count'); } + + /** + * Function to set max allowed scheduled workflow count as per global defaults + */ + function setMaxAllowedScheduledWorkflows(){ + global $max_scheduled_workflows; + if(!empty($max_scheduled_workflows)){ + vglobal('max_scheduled_workflows', $max_scheduled_workflows); + }else{ + vglobal('max_scheduled_workflows', 10); + } + } /** * Function returns the maximum allowed scheduled workflows * @return int */ function getMaxAllowedScheduledWorkflows() { - vglobal('max_scheduled_workflows', 10); return vglobal('max_scheduled_workflows'); } diff --git a/modules/com_vtiger_workflow/tasks/VTEmailTask.inc b/modules/com_vtiger_workflow/tasks/VTEmailTask.inc index cc8b6eb5c4b5d4ed82535567b95ce06a9f7798c1..ff514b2d511ccbd9d30b73cb7963246241db00a1 100644 --- a/modules/com_vtiger_workflow/tasks/VTEmailTask.inc +++ b/modules/com_vtiger_workflow/tasks/VTEmailTask.inc @@ -256,11 +256,11 @@ class VTEmailTask extends VTTask{ } else { if ($userObj) { $fromEmail = $userObj->email1; - $fromName = trim($userObj->first_name.' '.$userObj->last_name); + $fromName = $userObj->userlabel; } else { $fromEmail = $this->getDefaultFromEmail(); $userObj = Users::getActiveAdminUser(); - $fromName = trim($userObj->first_name.' '.$userObj->last_name); + $fromName = $userObj->userlabel; } } diff --git a/packages/vtiger/mandatory/ModTracker.zip b/packages/vtiger/mandatory/ModTracker.zip index 2890f3939517b195682c8d74f11309d6718630b2..192cbcecdc0a1aa78968179c36dd3b02239e6bb3 100644 Binary files a/packages/vtiger/mandatory/ModTracker.zip and b/packages/vtiger/mandatory/ModTracker.zip differ diff --git a/packages/vtiger/mandatory/PBXManager.zip b/packages/vtiger/mandatory/PBXManager.zip index 07720357484e4878f38e42a5fd296ffb599f1bbe..3227486951dbe1aad0abcc85eba0acceaeeef9b3 100644 Binary files a/packages/vtiger/mandatory/PBXManager.zip and b/packages/vtiger/mandatory/PBXManager.zip differ diff --git a/packages/vtiger/optional/CustomerPortal.zip b/packages/vtiger/optional/CustomerPortal.zip index d81610c26a208ca3563d9e7fb607064b57d7700a..ac46c5b1c6d6793ca69c6ae251469ddaa1b45765 100644 Binary files a/packages/vtiger/optional/CustomerPortal.zip and b/packages/vtiger/optional/CustomerPortal.zip differ diff --git a/packages/vtiger/optional/MexicanSpanishLanguagePack_es_mx.zip b/packages/vtiger/optional/MexicanSpanishLanguagePack_es_mx.zip index 1437cd38b9760740291814e39ef8eb4af990ad7d..8883bf590ed374f9faef469de48c8ccfa495ac84 100644 Binary files a/packages/vtiger/optional/MexicanSpanishLanguagePack_es_mx.zip and b/packages/vtiger/optional/MexicanSpanishLanguagePack_es_mx.zip differ diff --git a/packages/vtiger/optional/ModComments.zip b/packages/vtiger/optional/ModComments.zip index a1e37d05c84937d116fe22278de9d221f6e0ab05..bba05805eec71b34c2cd9dbd062a9fda1ef45be7 100644 Binary files a/packages/vtiger/optional/ModComments.zip and b/packages/vtiger/optional/ModComments.zip differ diff --git a/pkg/vtiger/modules/CustomerPortal/modules/CustomerPortal/apis/DownloadFile.php b/pkg/vtiger/modules/CustomerPortal/modules/CustomerPortal/apis/DownloadFile.php index c8260dda8db6e8922a0d84fe144430c510e00b4b..110b6141249dabbffb95e0aa41b2fc0231c6e01a 100644 --- a/pkg/vtiger/modules/CustomerPortal/modules/CustomerPortal/apis/DownloadFile.php +++ b/pkg/vtiger/modules/CustomerPortal/modules/CustomerPortal/apis/DownloadFile.php @@ -63,18 +63,22 @@ class CustomerPortal_DownloadFile extends CustomerPortal_API_Abstract { $fileres = $adb->pquery($fileidQuery, array($id)); $fileid = $adb->query_result($fileres, 0, 'attachmentsid'); - $filepathQuery = 'SELECT path,name FROM vtiger_attachments WHERE attachmentsid = ?'; - $fileres = $adb->pquery($filepathQuery, array($fileid)); - $filepath = $adb->query_result($fileres, 0, 'path'); - $filename = $adb->query_result($fileres, 0, 'name'); - $filename = decode_html($filename); + $filepathQuery = 'SELECT path,name,storedname FROM vtiger_attachments WHERE attachmentsid = ?'; + $fileres = $adb->pquery($filepathQuery, array($fileid)); + $filepath = $adb->query_result($fileres, 0, 'path'); + if($adb->query_result($fileres, 0, 'storedname')) { + $filename = $adb->query_result($fileres, 0, 'storedname'); + } else { + $filename = $adb->query_result($fileres, 0, 'name'); + } + $realfilename = decode_html($adb->query_result($fileres, 0, 'name')); - $saved_filename = $fileid."_".$filename; - $filenamewithpath = $filepath.$saved_filename; - $filesize = filesize($filenamewithpath); - $fileDetails = array(); - $fileDetails['fileid'] = $fileid; - $fileDetails['filename'] = $filename; + $saved_filename = $fileid."_".$filename; + $filenamewithpath = $filepath.$saved_filename; + $filesize = filesize($filenamewithpath); + $fileDetails = array(); + $fileDetails['fileid'] = $fileid; + $fileDetails['filename'] = $realfilename; $fileDetails['filetype'] = $filetype; $fileDetails['filesize'] = $filesize; $fileDetails['filecontents'] = base64_encode(file_get_contents($filenamewithpath)); @@ -86,8 +90,13 @@ class CustomerPortal_DownloadFile extends CustomerPortal_API_Abstract { //construct path for attachment and get file size and type details $attachmentDetails = $rawAttachmentDetails[0]; $fileid = $attachmentDetails['attachmentsid']; - $filename = $attachmentDetails['name']; + $filename = $attachmentDetails['storedname']; + if(empty($filename)){ + $filename = $attachmentDetails['name']; + } $filepath = $attachmentDetails['path']; + $realfilename = decode_html($attachmentDetails['name']); + $saved_filename = $fileid."_".$filename; $filenamewithpath = $filepath.$saved_filename; $filesize = filesize($filenamewithpath); @@ -96,7 +105,7 @@ class CustomerPortal_DownloadFile extends CustomerPortal_API_Abstract { //Construct array with all attachment details $fileDetails = array(); $fileDetails['fileid'] = $fileid; - $fileDetails['filename'] = $filename; + $fileDetails['filename'] = $realfilename; $fileDetails['filetype'] = $filetype; $fileDetails['filesize'] = $filesize; $fileDetails['filecontents'] = base64_encode(file_get_contents($filenamewithpath)); diff --git a/pkg/vtiger/modules/Import/modules/Import/actions/Data.php b/pkg/vtiger/modules/Import/modules/Import/actions/Data.php index 5e0e36a0965a1fb656cef858d5b7f9335295c0cc..718a828e58dbebadeb1cd1ca03c32269412baf74 100644 --- a/pkg/vtiger/modules/Import/modules/Import/actions/Data.php +++ b/pkg/vtiger/modules/Import/modules/Import/actions/Data.php @@ -896,7 +896,7 @@ class Import_Data_Action extends Vtiger_Action_Controller { $emailData = getTranslatedString('LBL_IMPORT_COMPLETED', 'Import').' '.$importResult.getTranslatedString('LBL_CHECK_IMPORT_STATUS', 'Import'); - $userName = getFullNameFromArray('Users', $importDataController->user->column_fields); + $userName = $importDataController->user->column_fields['userlabel']; $userEmail = $importDataController->user->email1; $vtigerMailer->AddAddress($userEmail, $userName); $vtigerMailer->Subject = $emailSubject; diff --git a/pkg/vtiger/modules/MailManager/modules/MailManager/views/Mail.php b/pkg/vtiger/modules/MailManager/modules/MailManager/views/Mail.php index 7d03ebff4cc62ac61393f14a575f7c473c66ece4..996f53ff1c8b3498fd3e8c385922d2c325ea877c 100755 --- a/pkg/vtiger/modules/MailManager/modules/MailManager/views/Mail.php +++ b/pkg/vtiger/modules/MailManager/modules/MailManager/views/Mail.php @@ -164,7 +164,7 @@ class MailManager_Mail_View extends MailManager_Abstract_View { } $fromEmail = $connector->getFromEmailAddress(); - $userFullName = getFullNameFromArray('Users', $currentUserModel->getData()); + $userFullName = $currentUserModel->get('userlabel'); $userId = $currentUserModel->getId(); $mailer = new Vtiger_Mailer(); diff --git a/pkg/vtiger/modules/Mobile/modules/Mobile/api/ws/FetchModuleOwners.php b/pkg/vtiger/modules/Mobile/modules/Mobile/api/ws/FetchModuleOwners.php index 381a9b41863e5942271a87d1784f181db25cc993..2d0a8800087eff2124af6fb19f5be2d19c3cc9a0 100644 --- a/pkg/vtiger/modules/Mobile/modules/Mobile/api/ws/FetchModuleOwners.php +++ b/pkg/vtiger/modules/Mobile/modules/Mobile/api/ws/FetchModuleOwners.php @@ -38,7 +38,7 @@ class Mobile_WS_FetchModuleOwners extends Mobile_WS_Controller { $userRecord = Users_Record_Model::getInstanceById($userId, 'Users'); $usersList[] = array( 'value' => $usersWSId . 'x' . $userId, - 'label' => decode_html($userRecord->get("first_name") . ' ' . $userRecord->get('last_name')) + 'label' => decode_html($userRecord->get("userlabel")) ); } return $usersList; diff --git a/pkg/vtiger/modules/ModComments/modules/ModComments/models/Record.php b/pkg/vtiger/modules/ModComments/modules/ModComments/models/Record.php index 6d8807df47e7beeca77944c5be875adc1c410da6..6616d6a6e80c69fd01b85d7757c38aadebd4f667 100644 --- a/pkg/vtiger/modules/ModComments/modules/ModComments/models/Record.php +++ b/pkg/vtiger/modules/ModComments/modules/ModComments/models/Record.php @@ -412,14 +412,27 @@ class ModComments_Record_Model extends Vtiger_Record_Model { $attachmentsList = array(); if($numOfRows) { for($i=0; $i<$numOfRows; $i++) { - $attachmentsList[$i]['fileid'] = $db->query_result($attachmentRes, $i, 'attachmentsid'); - $attachmentsList[$i]['attachment'] = decode_html($db->query_result($attachmentRes, $i, 'name')); - $path = $db->query_result($attachmentRes, $i, 'path'); + $attachmentId = $db->query_result($attachmentRes, $i, 'attachmentsid'); + $rawFileName = $db->query_result($attachmentRes, $i, 'name'); + $storedName = $db->query_result($attachmentRes, $i, 'storedname'); + $path = $db->query_result($attachmentRes, $i, 'path'); + if($storedName) { + $filename = $storedName; + } else { + $filename = $rawFileName; + } + $attachmentsList[$i]['attachment'] = decode_html($rawFileName); + $attachmentsList[$i]['fileid'] = $attachmentId; + $attachmentsList[$i]['storedname'] = decode_html($storedName); $attachmentsList[$i]['path'] = $path; - $attachmentsList[$i]['size'] = filesize($path.$attachmentsList[$i]['fileid'].'_'.$attachmentsList[$i]['attachment']); + $saved_filename = $attachmentId."_".$filename; + $filenamewithpath = $path.$saved_filename; + $filesize = filesize($filenamewithpath); + $attachmentsList[$i]['filenamewithpath'] = $filenamewithpath; + $attachmentsList[$i]['size'] = $filesize; $attachmentsList[$i]['type'] = $db->query_result($attachmentRes, $i, 'type'); $attachmentsList[$i]['cid'] = $db->query_result($attachmentRes, $i, 'cid'); - } + } } return $attachmentsList; } diff --git a/pkg/vtiger/modules/ModTracker/modules/ModTracker/ModTrackerHandler.php b/pkg/vtiger/modules/ModTracker/modules/ModTracker/ModTrackerHandler.php index dc9849a147b34cbff74ce2a68028bcf4d17aec55..abe1bc3cb282746c492ef10001216065d7432100 100644 --- a/pkg/vtiger/modules/ModTracker/modules/ModTracker/ModTrackerHandler.php +++ b/pkg/vtiger/modules/ModTracker/modules/ModTracker/ModTrackerHandler.php @@ -14,16 +14,21 @@ class ModTrackerHandler extends VTEventHandler { function handleEvent($eventName, $data) { global $adb; - $current_user_id=$_SESSION["authenticated_user_id"]; - $current_user = Users_Record_Model::getInstanceById($current_user_id, 'Users'); - $curid=$current_user->get('id'); - global $current_user; + if (isset($_SESSION["authenticated_user_id"])) { + $current_user_id = $_SESSION["authenticated_user_id"]; + $current_user = Users_Record_Model::getInstanceById($current_user_id, 'Users'); + $curid = $current_user->get('id'); + } else { + //$_SESSION["authenticated_user_id"] is not set when creating/updating via Webservice + global $current_user; + $curid = $current_user->id; + } $moduleName = $data->getModuleName(); $isTrackingEnabled = ModTracker::isTrackingEnabledForModule($moduleName); - if(!$isTrackingEnabled) { + if (!$isTrackingEnabled) { return; } - if($eventName == 'vtiger.entity.aftersave.final') { + if ($eventName == 'vtiger.entity.aftersave.final') { $recordId = $data->getId(); $columnFields = $data->getData(); $vtEntityDelta = new VTEntityDelta(); diff --git a/pkg/vtiger/modules/PBXManager/settings/models/Record.php b/pkg/vtiger/modules/PBXManager/settings/models/Record.php index f17988f3559bd58d0c779b7c837a97f9f3217ba6..2520ff18b6ea6bd61a7e927fa4457bdbf37e3ffa 100644 --- a/pkg/vtiger/modules/PBXManager/settings/models/Record.php +++ b/pkg/vtiger/modules/PBXManager/settings/models/Record.php @@ -11,29 +11,29 @@ class Settings_PBXManager_Record_Model extends Settings_Vtiger_Record_Model { const tableName = 'vtiger_pbxmanager_gateway'; - + public function getId() { return $this->get('id'); } public function getName() { } - + public function getModule(){ return new Settings_PBXManager_Module_Model; } - + static function getCleanInstance(){ return new self; } - + public static function getInstance(){ $serverModel = new self(); $db = PearDatabase::getInstance(); $query = 'SELECT * FROM '.self::tableName; $gatewatResult = $db->pquery($query, array()); $gatewatResultCount = $db->num_rows($gatewatResult); - + if($gatewatResultCount > 0) { $rowData = $db->query_result_rowdata($gatewatResult, 0); $serverModel->set('gateway',$rowData['gateway']); @@ -46,7 +46,7 @@ class Settings_PBXManager_Record_Model extends Settings_Vtiger_Record_Model { } return $serverModel; } - + public static function getInstanceById($recordId, $qualifiedModuleName) { $db = PearDatabase::getInstance(); $result = $db->pquery('SELECT * FROM '.self::tableName.' WHERE id = ?', array($recordId)); @@ -66,20 +66,20 @@ class Settings_PBXManager_Record_Model extends Settings_Vtiger_Record_Model { } return false; } - + public function save() { $db = PearDatabase::getInstance(); - $parameters = ''; + $parameters = array(); $selectedGateway = $this->get('gateway'); $connector = new PBXManager_PBXManager_Connector; - + foreach ($connector->getSettingsParameters() as $field => $type) { $parameters[$field] = $this->get($field); } $this->set('parameters', Zend_Json::encode($parameters)); $params = array($selectedGateway,$this->get('parameters')); $id = $this->getId(); - + if ($id) { $query = 'UPDATE '.self::tableName.' SET gateway=?, parameters = ? WHERE id = ?'; array_push($params, $id); diff --git a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/manifest.xml b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/manifest.xml index a477fd2e9ae58d3cff55b03c25591939927f8f09..28e8c17ae1013ff52406bad1b281f8ffaacd36e7 100644 --- a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/manifest.xml +++ b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/manifest.xml @@ -4,7 +4,7 @@ <name>Mexican Spanish</name> <label>ES Mexico</label> <prefix>es_mx</prefix> - <version>7.2.1</version> + <version>1.0</version> <dependencies> <vtiger_version>7.2</vtiger_version> <vtiger_max_version>7.*</vtiger_max_version> @@ -19,7 +19,7 @@ * the specific language governing rights and limitations under the License. ******************************************************************************** * Language : Español es_mx - * Version : 7.2.1 + * Version : 1.0 * Author : Aimée Valckx - simplesistemas.com (Simple - Sistemas e Implementos Empresariales SA de CV) * Author : Rubén Estrada - simplesistemas.com (Simple - Sistemas e Implementos Empresariales SA de CV) * Author : Francisco Hernandez Odin Consultores S de RL de CV diff --git a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Leads.php b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Leads.php index ca8227f0eb91e557024df14d8e847e2d1438578f..831d7f61cd41496f4d439a4eb5902682a73d021b 100644 --- a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Leads.php +++ b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Leads.php @@ -37,7 +37,7 @@ $languageStrings = array( 'Pre Qualified' => 'Pre calificado' , 'Qualified' => 'Calificado' , 'Warm' => 'Tibio' , - 'LBL_CONVERT_LEAD' => 'Convertir Prospecto:' , + 'LBL_CONVERT_LEAD' => 'Convertir Prospecto' , 'LBL_TRANSFER_RELATED_RECORD' => 'Transferir registro relacionado a', 'LBL_CONVERT_LEAD_ERROR' => 'Necesita tener habilitado el módulo de Cuentas o Contactos para convertir el Prospecto', 'LBL_LEADS_FIELD_MAPPING_INCOMPLETE' => 'No se han vinculado todos los campos obligatorios', diff --git a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Reports.php b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Reports.php index 33b047ffadf765e5039945961c30a39ee7673297..2823c74f6edfebf7c4b8670dcbd5bb86a7382ace 100644 --- a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Reports.php +++ b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Reports.php @@ -8,17 +8,17 @@ * All Rights Reserved. ************************************************************************************/ $languageStrings = array( - 'Reports' => 'Informes' , // TODO: Review - 'SINGLE_Reports' => 'Informe' , // TODO: Review + 'Reports' => 'Reportes' , + 'SINGLE_Reports' => 'Reporte' , 'LBL_FOLDER_NOT_EMPTY' => 'La carpeta no está vacÃa' , - 'LBL_MOVE_REPORT' => 'Mover Informe' , + 'LBL_MOVE_REPORT' => 'Mover Reporte' , 'LBL_CUSTOMIZE' => 'Personalizar' , 'LBL_REPORT_EXPORT_EXCEL' => 'Exportar a Excel' , 'LBL_REPORT_PRINT' => 'Imprimir' , 'LBL_STEP_1' => 'Paso 1' , 'LBL_STEP_2' => 'Paso 2' , 'LBL_STEP_3' => 'Paso 3' , - 'LBL_REPORT_DETAILS' => 'Detalles del Informe' , + 'LBL_REPORT_DETAILS' => 'Detalles del Reporte' , 'LBL_SELECT_COLUMNS' => 'Seleccionar columnas' , 'LBL_FILTERS' => 'Filtros' , 'LBL_FOLDERS' => 'Carpetas' , @@ -28,22 +28,22 @@ $languageStrings = array( 'LBL_WRITE_YOUR_DESCRIPTION_HERE' => 'Agregue la descripción aquÃ', 'LBL_DUPLICATES_EXIST' => 'Existe un duplicado' , 'LBL_FOLDERS_LIST' => 'Lista de carpetas' , - 'LBL_DENIED_REPORTS' => 'Informes denegados' , + 'LBL_DENIED_REPORTS' => 'Reportes denegados' , 'LBL_NO_OF_RECORDS' => 'Registros en total : ' , 'LBL_MORE_RECORDS_TXT' => 'Sólo se muestran 500 registros. Por favor, exporte para ver todos los registros', - 'LBL_ADD_RECORD' => 'Agregar Informe' , + 'LBL_ADD_RECORD' => 'Agregar Reporte' , 'LBL_ADD_FOLDER' => 'Agregar carpeta' , - 'LBL_REPORT_DELETE_DENIED' => 'No cuenta con los permisos para eliminar este informe', + 'LBL_REPORT_DELETE_DENIED' => 'No cuenta con los permisos para eliminar este Reporte', 'LBL_FOLDER_CAN_NOT_BE_DELETED' => 'Esta carpeta no puede ser eliminada', - 'LBL_REPORTS_LIST' => 'Lista de Informes' , - 'LBL_REPORT_NAME' => 'Nombre del Informe' , - 'LBL_REPORT_FOLDER' => 'Carpeta del Informe' , + 'LBL_REPORTS_LIST' => 'Lista de Reportes' , + 'LBL_REPORT_NAME' => 'Nombre del Reporte' , + 'LBL_REPORT_FOLDER' => 'Carpeta del Reporte' , 'LBL_DESCRIPTION' => 'Descripción' , 'PRIMARY_MODULE' => 'Módulo principal' , 'LBL_SELECT_RELATED_MODULES' => 'Seleccionar módulos relacionados', 'LBL_MAX' => 'MAX' , 'LBL_NEXT' => 'Siguiente' , - 'LBL_REPORTS' => 'Lista de Informes' , + 'LBL_REPORTS' => 'Lista de Reportes' , 'LBL_GROUP_BY' => 'Agrupar por' , 'LBL_SORT_ORDER' => 'Ordenar' , 'LBL_ASCENDING' => 'Ascendente' , @@ -54,7 +54,7 @@ $languageStrings = array( 'LBL_AVERAGE' => 'Promedio' , 'LBL_LOWEST_VALUE' => 'El valor más bajo' , 'LBL_HIGHEST_VALUE' => 'El valor más alto' , - 'LBL_GENERATE_REPORT' => 'Guardar y generar Informe' , + 'LBL_GENERATE_REPORT' => 'Guardar y generar Reporte' , 'LBL_SUM' => 'SUM' , 'LBL_AVG' => 'PROM' , 'LBL_MIN' => 'MIN' , @@ -62,18 +62,23 @@ $languageStrings = array( 'LBL_REPORT_CSV' => 'Exportar a CSV' , 'LBL_VIEW_DETAILS' => 'Ver Detalles' , 'LBL_GENERATE_NOW' => 'Generar ahora' , - 'Report Name' => 'Nombre del Informe' , - 'Account and Contact Reports' => 'Informes de Cuenta y Contactos', - 'Lead Reports' => 'Informes de Prospectos' , - 'Potential Reports' => 'Informes de Oportunidades' , - 'Activity Reports' => 'Informes de Tareas' , - 'HelpDesk Reports' => 'Informes de Casos' , - 'Product Reports' => 'Informes de Productos' , - 'Quote Reports' => 'Informes de Cotizaciones' , - 'PurchaseOrder Reports' => 'Informes de Órdenes de Compra', - 'SalesOrder Reports' => 'Informes de Pedidos de Venta', - 'Invoice Reports' => 'Informes de Facturas' , - 'Campaign Reports' => 'Informes de Campañas' , + 'Report Name' => 'Nombre del Reporte' , + 'Account and Contact Reports' => 'Reportes de Cuenta y Contactos', + 'Organization and Contact Reports' => 'Reportes de Cuentas y Contactos', + 'Lead Reports' => 'Reportes de Prospectos' , + 'Potential Reports' => 'Reportes de Oportunidades' , + 'Opportunity Reports' => 'Reportes de Oportunidades', + 'Activity Reports' => 'Reportes de Tareas' , + 'Sales Order Reports' => 'Reportes de Pedidos', + 'HelpDesk Reports' => 'Reportes de Casos' , + 'Tickets Reports' => 'Reportes de Casos' , + 'Product Reports' => 'Reportes de Productos' , + 'Quote Reports' => 'Reportes de Cotizaciones' , + 'PurchaseOrder Reports' => 'Reportes de Órdenes de Compra', + 'Purchase Order Reports' => 'Reportes de Órdenes de Compra', + 'SalesOrder Reports' => 'Reportes de Pedidos de Venta', + 'Invoice Reports' => 'Reportes de Facturas' , + 'Campaign Reports' => 'Reportes de Campañas' , 'Contacts by Accounts' => 'Contactos por Cuenta' , 'Contacts without Accounts' => 'Contactos sin Cuenta' , 'Contacts by Potentials' => 'Contactos por Oportunidades' , @@ -81,7 +86,7 @@ $languageStrings = array( 'Contacts not related to Accounts' => 'Contactos sin Cuenta' , 'Contacts related to Potentials' => 'Contactos relacionados con Oportunidades', 'Lead by Source' => 'Prospectos por Origen' , - 'Lead Status Report' => 'Informe de estado de los Prospectos', + 'Lead Status Report' => 'Reporte de estado de los Prospectos', 'Potential Pipeline' => 'Gráfica de Oportunidades' , 'Closed Potentials' => 'Oportunidades Cerradas' , 'Potential that have Won' => 'Oportunidades Exitosas' , @@ -92,38 +97,38 @@ $languageStrings = array( 'Tickets that are Open' => 'Casos que están abiertos' , 'Product Details' => 'Detalles de Productos' , 'Products by Contacts' => 'Productos por Contacto' , - 'Product Detailed Report' => 'Informe detallado de Productos', + 'Product Detailed Report' => 'Reporte detallado de Productos', 'Products related to Contacts' => 'Productos relacionados con Contactos', 'Open Quotes' => 'Cotizaciones pendientes' , - 'Quotes Detailed Report' => 'Informe detallado de Cotizaciones', + 'Quotes Detailed Report' => 'Reporte detallado de Cotizaciones', 'Quotes that are Open' => 'Cotizaciones pendientes' , 'PurchaseOrder by Contacts' => 'Órdenes de Compra por Contactos', - 'PurchaseOrder Detailed Report' => 'Informe detallado de Órdenes de Compra', + 'PurchaseOrder Detailed Report' => 'Reporte detallado de Órdenes de Compra', 'PurchaseOrder related to Contacts' => 'Órdenes de Compra relacionadas con Contactos', - 'Invoice Detailed Report' => 'Informe detallado de Facturas ', + 'Invoice Detailed Report' => 'Reporte detallado de Facturas ', 'Last Month Activities' => 'Tareas del mes pasado' , 'This Month Activities' => 'Tareas de este mes' , 'Campaign Expectations and Actuals' => 'Expectativa y realidad de campaña', - 'SalesOrder Detailed Report' => 'Informe detallado de Pedidos', - 'Email Reports' => 'Informe de Correos' , - 'Contacts Email Report' => 'Informe de correos de Contactos', - 'Accounts Email Report' => 'Informe de correos de Cuentas', - 'Leads Email Report' => 'Informe de correos de Prospectos', - 'Vendors Email Report' => 'Informe de correos de Proveedores', + 'SalesOrder Detailed Report' => 'Reporte detallado de Pedidos', + 'Email Reports' => 'Reporte de Correos' , + 'Contacts Email Report' => 'Reporte de correos de Contactos', + 'Accounts Email Report' => 'Reporte de correos de Cuentas', + 'Leads Email Report' => 'Reporte de correos de Prospectos', + 'Vendors Email Report' => 'Reporte de correos de Proveedores', 'Emails sent to Contacts' => 'Correos enviados a Contactos', 'Emails sent to Organizations' => 'Correos enviados a Cuentas' , 'Emails sent to Leads' => 'Correos enviados a Prospectos', 'Emails sent to Vendors' => 'Correos enviados a Proveedores', - 'LBL_PRINT_REPORT' => 'Imprimir Informe' , + 'LBL_PRINT_REPORT' => 'Imprimir Reporte' , 'LBL_RECORDS' => 'Registros' , 'LBL_LIMIT_EXCEEDED' => 'Solo se muestran 1000 + registros. Utilice CSV o Excel Export para ver todos los registros', 'LBL_TOP' => 'Top' , - 'LBL_ALL_REPORTS' => 'Todos los Informes' , + 'LBL_ALL_REPORTS' => 'Todos los Reportes' , 'LBL_CALCULATION_CONVERSION_MESSAGE' => 'El cálculo se basa en la moneda establecida en Mis Preferencias de su CRM', //Scedule Reports - 'LBL_CREATING_REPORT' => 'Creando Informe', - 'LBL_EDITING_REPORT' => 'Editando Informe', - 'LBL_SCHEDULE_REPORTS' => 'Programar Informe', + 'LBL_CREATING_REPORT' => 'Creando Reporte', + 'LBL_EDITING_REPORT' => 'Editando Reporte', + 'LBL_SCHEDULE_REPORTS' => 'Programar Reporte', 'LBL_AT_TIME' => 'A la hora', 'LBL_DAILY' => 'Diario', 'LBL_WEEKLY' => 'Semanal', @@ -136,13 +141,13 @@ $languageStrings = array( 'LBL_SELECTED_DATES' => 'Fechas seleccionadas', 'LBL_EXCEEDING_MAXIMUM_LIMIT' => 'Superando el lÃmite máximo', 'LBL_NEXT_TRIGGER_TIME' => 'Tiempo de activación siguiente', - 'LBL_RUN_REPORT' => 'Ejecutar informe', + 'LBL_RUN_REPORT' => 'Ejecutar Reporte', 'LBL_SELECT_RECIEPIENTS' => 'Seleccionar destinatarios', 'LBL_SPECIFIC_EMAIL_ADDRESS' => 'Enviar al correo electrónico especÃfico', //Summary/Pivot Reports - 'LBL_CREATING_PIVOT_REPORT' => 'Creando informe dinámico', - 'LBL_EDITING_PIVOT_REPORT' => 'Editando informe dinámico', + 'LBL_CREATING_PIVOT_REPORT' => 'Creando Reporte dinámico', + 'LBL_EDITING_PIVOT_REPORT' => 'Editando Reporte dinámico', 'LBL_SELECT_PIVOT_FIELDS' => 'Seleccione los campos dinámicos', 'LBL_SELECT_ROWS' => 'Seleccione las filas', 'LBL_SELECT_DATA_FIELDS' => 'Seleccione los campos de datos', @@ -154,7 +159,7 @@ $languageStrings = array( 'LBL_SELECT_PIVOT_FIELDS_WARNING' => 'Advertencia: Por favor, seleccione al menos un campo de fila, un campo columna y un campo de datos', 'LBL_PIVOT_DATA_FIELDS_WARNING' => 'Advertencia: En la columna de datos las funciones de agregación (suma, promedio, mÃnimo y máximo) no se deben repetir.', 'LBL_MODIFY_CONDITIONS' => 'Modificar condiciones', - 'LBL_PIVOT_PREVIEW_EX' => 'Vista previa del informe dinámico (Ejemplo)', + 'LBL_PIVOT_PREVIEW_EX' => 'Vista previa del Reporte dinámico (Ejemplo)', //charts labels 'LBL_SELECT_CHART_TYPE' => 'Seleccionar tipo de gráfico', @@ -177,20 +182,20 @@ $languageStrings = array( 'LBL_MODIFY_CONDITION' => 'Modificar condiciones', 'LBL_PLEASE_SELECT_ATLEAST_ONE_GROUP_FIELD_AND_DATA_FIELD' => 'Por favor, seleccione al menos un grupo de campos y un campo de datos', 'LBL_FOR_BAR_GRAPH_AND_LINE_GRAPH_SELECT_3_MAX_DATA_FIELDS' => 'Para la gráfica de barras y de lÃneas, puede seleccionar un máximo de 3 campos de datos', - 'LBL_DETAIL_REPORT' => 'Informe detallado', + 'LBL_DETAIL_REPORT' => 'Reporte detallado', 'LBL_PIVOT_REPORT' => 'Reporte dinámico', 'LBL_CHARTS' => 'Gráficas', //Schedule Reports - Mail Content - 'LBL_AUTO_GENERATED_REPORT_EMAIL' => 'Este es un mensaje generado automáticamente enviado en nombre de un informe programado', + 'LBL_AUTO_GENERATED_REPORT_EMAIL' => 'Este es un mensaje generado automáticamente enviado en nombre de un Reporte programado', 'LBL_PIN_CHART_TO_DASHBOARD' => 'Anclar la gráfica en el tablero', 'LBL_FILE_FORMAT' => 'Formato de archivo', - 'Report Type' => 'Tipo de informe', + 'Report Type' => 'Tipo de Reporte', 'tabular' => 'Detalle', 'summary' => 'Detalle', 'pivot' => 'Dinámico', 'chart' => 'Gráfica', - 'LBL_REPORTS_MOVED_SUCCESSFULLY'=>'Informes movidos exitosamente.', + 'LBL_REPORTS_MOVED_SUCCESSFULLY'=>'Reportes movidos exitosamente.', 'LBL_SAME_SOURCE_AND_TARGET_FOLDER'=>'La carpeta de destino es igual que la carpeta de origen', 'LBL_SEARCH_FOR_FOLDERS' => 'Buscar carpetas', 'LBL_CHART_REPORT' => 'Reporte de gráficas', @@ -198,26 +203,26 @@ $languageStrings = array( 'LBL_SAME_LEVEL_ROLES' => 'Mismo nivel de funciones', 'LBL_SUBORDINATE_ROLES' => 'Funciones subordinadas', - 'LBL_SHARE_REPORT' => 'Informe de acciones', - 'LBL_SHARED_REPORTS' => 'Informes compartidos', + 'LBL_SHARE_REPORT' => 'Compartir Reporte con', + 'LBL_SHARED_REPORTS' => 'Reportes compartidos', 'LBL_PINNED' => 'Anclada', 'LBL_UNPINNED' => 'Desanclada', - 'LBL_REPORTS_DELETED_SUCCESSFULLY' => 'Informes eliminados sorrectamente', + 'LBL_REPORTS_DELETED_SUCCESSFULLY' => 'Reportes eliminados sorrectamente', ); $jsLanguageStrings = array( - 'JS_DUPLICATE_RECORD' => 'Duplicar reporte' , - 'JS_CALCULATION_LINE_ITEM_FIELDS_SELECTION_LIMITATION' => 'Limitación: Los campos de artÃculos de lÃnea (precio de lista, descuento & cantidad) solo pueden usarse cuando otros campos de cálculo no están seleccionados', - 'JS_CHART_PINNED_TO_DASHBOARD' => 'Gráfica anclada en el tablero', - 'JS_CHART_ALREADY_PINNED_TO_DASHBOARD' => 'La gráfica ya está anclada en el tablero', - 'JS_MOVE_REPORTS'=>'Mover Informes', + 'JS_DUPLICATE_RECORD' => 'Duplicar Reporte' , + 'JS_CALCULATION_LINE_ITEM_FIELDS_SELECTION_LIMITATION' => 'Limitación: Los campos de partidas (precio de lista, descuento & cantidad) solo pueden usarse cuando otros campos de cálculo no están seleccionados', + 'JS_CHART_PINNED_TO_DASHBOARD' => 'Gráfica agregada al tablero', + 'JS_CHART_ALREADY_PINNED_TO_DASHBOARD' => 'La gráfica ya está agregada en el tablero', + 'JS_MOVE_REPORTS' => 'Mover Reportes', 'JS_SCHEDULED_DATE_TIME_ERROR' => 'La fecha y hora programada deben ser mayores que la fecha y hora actuales', - 'JSLBL_PIN_CHART_TO_DASHBOARD' => 'Anclar la gráfica en el tablero', - 'JSLBL_UNPIN_CHART_FROM_DASHBOARD' => 'Desanclar la gráfica del tablero', + 'JSLBL_PIN_CHART_TO_DASHBOARD' => 'Agregar la gráfica al tablero', + 'JSLBL_UNPIN_CHART_FROM_DASHBOARD' => 'Retirar la gráfica del tablero', 'JS_CHART_REMOVED_FROM_DASHBOARD' => 'Gráfica retirada del tablero', - 'JS_NO_CHART_DATA_AVAILABLE' => 'No se dispone de datos, por favor revise los campos seleccionados', + 'JS_NO_CHART_DATA_AVAILABLE' => 'No se dispone de datos, por favor revise los campos seleccionados', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Vtiger.php b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Vtiger.php index 9081820013f05f29ff5d6f5352640aa49b8d9832..d1b33bdf609e6d7b470771b01cbd279bfb20763d 100644 --- a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Vtiger.php +++ b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/Vtiger.php @@ -211,7 +211,7 @@ $languageStrings = array( 'LBL_SEARCH_RESULTS' => 'Buscar resultados' , 'LBL_SAVE_AS_FILTER' => 'Guardar como lista' , 'LBL_NOT_ACCESSIBLE' => 'No accesible' , - 'LBL_ITEM_DETAILS' => 'Detalles del elemento' , + 'LBL_ITEM_DETAILS' => 'Partidas' , 'LBL_CURRENCY' => 'Moneda' , 'LBL_TAX_MODE' => 'Tipo de impuestos' , 'LBL_INDIVIDUAL' => 'Individual' , @@ -491,13 +491,13 @@ $languageStrings = array( 'In Progress' => 'En progreso' , 'Subject' => 'Asunto' , 'Terms & Conditions' => 'Términos y condiciones' , - 'Item Name' => 'Nombre del elemento' , + 'Item Name' => 'Nombre del artÃculo' , 'Quantity' => 'Cantidad' , 'List Price' => 'Precio de venta' , 'Image' => 'Imagen' , 'Purchase Cost' => 'Costo de compra' , 'Margin' => 'Margen' , - 'Item Comment' => 'Comentario al elemento' , + 'Item Comment' => 'Comentario del artÃculo' , 'Tax1' => 'Impuesto1' , 'Tax2' => 'Impuesto2' , 'Tax3' => 'Impuesto3' , @@ -788,8 +788,9 @@ $languageStrings = array( 'Workflows' => 'Flujos de trabajo', 'Primary Twitter' => 'Twitter principal', - 'Engagement Score' => 'Tasa de interacción' , - 'Click Count' => 'Contador de clics' , + 'Engagement Score' => 'Tasa de interacción', + 'Click Count' => 'Contador de clics', + 'Access Count' => 'Contador de accesos', // Extensions 'LBL_SYNC_LOG' => 'Registro de sincronización', 'LBL_SYNC_SETTINGS' => 'Configuración de sincronización', @@ -865,7 +866,7 @@ $languageStrings = array( 'LBL_IS_REMOVED' => 'se elimina', 'LBL_REASON_FOR_CHANGING_COMMENT' => 'Razón para cambiar el comentario', 'LBL_APPLBL_NONEROVE' => 'Aprobar', - 'LBL_ITEM' => 'Elemento', + 'LBL_ITEM' => 'ArtÃculo', 'Add Note' => 'Agregar documento', 'LBL_form' => 'de', 'LBL_to' => 'a', @@ -1076,8 +1077,7 @@ fuera de Vtiger.', 'LBL_HOUR(S)' => 'hora(s)', 'LBL_SLA_INFORMATION' => 'Información SLA', 'LBL_TO_USE_SWITCH_TO_NEW_LOOK' => 'Para utilizar <b>%s</b>, le recomendamos cambiar al nuevo aspecto visual.', - 'LBL_DELETE_USER_CONFIRMATION' => 'Cuando se elimina un usuario, el usuario será marcado como "inactivo" y no se le podrán asignar nuevos registros ni tampoco podrá iniciar sesión. ¿Estás seguro que quieres borrarlo?', - + 'LBL_DUPLICATES' => 'duplicados', 'LBL_DUPLICATES_DETECTED' => 'Duplicado(s) detectado(s)', 'LBL_DUPLICATES_FOUND_MESSAGE' => 'Esto%s no se pueden guardar ya que existe o existen duplicado%s en VTiger con los mismos valores para %s.', @@ -1110,9 +1110,11 @@ $jsLanguageStrings = array( 'OVERWRITE_EXISTING_MSG2' => 'Detalles de la dirección' , 'SINGLE_Accounts' => 'Cuenta' , 'SINGLE_Contacts' => 'Contacto' , - 'LBL_DELETE_CONFIRMATION' => '¿Seguro que lo quiere borrar?', - 'LBL_MASS_DELETE_CONFIRMATION' => '¿Seguro que quiere borrar los siguientes registros?', - 'JS_LBL_SAVE' => 'Guardar' , + 'LBL_DELETE_CONFIRMATION' => '¿Estás seguro que lo quieres borrar?', + 'LBL_MASS_DELETE_CONFIRMATION' => '¿Estás seguro que quieres borrar los registros seleccionados?', + 'LBL_DELETE_USER_CONFIRMATION' => 'Cuando se elimina un usuario, el usuario será marcado como "inactivo" y no se le podrán asignar nuevos registros ni tampoco podrá iniciar sesión. ¿Estás seguro que quieres borrarlo?', + 'LBL_UNLINK_CONFIRMATION' => '¿Estás seguro que quieres desvincluar?', + 'JS_LBL_SAVE' => 'Guardar' , 'JS_LBL_CANCEL' => 'Cancelar' , 'SHOULD_BE_LESS_THAN_TODAY' => 'Debe suceder antes de hoy' , 'JS_PLEASE_SELECT_ATLEAST_ONE_OPTION' => 'Seleccione al menos una opción', @@ -1298,7 +1300,7 @@ $jsLanguageStrings = array( 'JS_INVALID_EMAILS' => 'Los correos electrónicos no válidos', 'JS_INTERNAL_COMMENT' => 'Comentario interno', 'JS_INTERNAL_COMMENT_INFO' => 'El comentario sólo será visto por los usuarios de CRM si la casilla de verificación comentario interno está activada. Para notificar a los clientes (a través de flujos de trabajo configurables o del portal de clientes) deje esta casilla deshabilitada', - 'JS_NO_LINE_ITEM' =>'Los artÃculos de lÃnea no pueden estar vacÃos.', + 'JS_NO_LINE_ITEM' =>'Los artÃculos no pueden estar vacÃos.', //More currencies message 'JS_BASE_CURRENCY_CHANGED_TO_DISABLE_CURRENCY' => 'La moneda base tiene que cambiarse para desactivar ', diff --git a/schema/DatabaseSchema.xml b/schema/DatabaseSchema.xml index bc7399e40bbc004148d11b1b665473ff63de33c0..7d0b001407ed5e9efc2e52aefca5d167e148b14b 100644 --- a/schema/DatabaseSchema.xml +++ b/schema/DatabaseSchema.xml @@ -101,7 +101,7 @@ <field name="currency_decimal_separator" type="C" size="2" /> <field name="currency_grouping_separator" type="C" size="2" /> <field name="currency_symbol_placement" type="C" size="20" /> - + <field name="userlabel" type="C" size="255" /> <index name="user_user_name_idx"> <col>user_name</col> </index> diff --git a/vtigerversion.php b/vtigerversion.php index 7a7ade6383272ef11256d683a03c6f8f0c031b03..83a0f13b2d1a2031527763983cd6e0b2be9d2de6 100644 --- a/vtigerversion.php +++ b/vtigerversion.php @@ -8,9 +8,9 @@ * All Rights Reserved. ************************************************************************************/ -$patch_version = '20201013'; // -ve timestamp before release, +ve timestamp after release. +$patch_version = '-20201019'; // -ve timestamp before release, +ve timestamp after release. $modified_database = ''; -$vtiger_current_version = '7.3.0'; +$vtiger_current_version = '7.4.0'; $_SESSION['vtiger_version'] = $vtiger_current_version; ?> diff --git a/vtlib/Vtiger/Deprecated.php b/vtlib/Vtiger/Deprecated.php index 7a5288679a7f44f0d6ea6841a1b72423e20746a9..a23a02a49264d560d6463a3ed1bdcad09cc63cb6 100644 --- a/vtlib/Vtiger/Deprecated.php +++ b/vtlib/Vtiger/Deprecated.php @@ -544,6 +544,23 @@ class Vtiger_Deprecated { } static function getSqlForNameInDisplayFormat($input, $module, $glue = ' ') { + if ($module == 'Users') { + if (is_string($input)) { + $input = array($input); + } + + $tableName = ''; + foreach ($input as $fieldTableColumn) { + if ($fieldTableColumn) { + list($tableName, $columnName) = explode('.', $fieldTableColumn); + break; + } + } + if ($tableName) { + return "$tableName.userlabel"; + } + } + $entity_field_info = Vtiger_Functions::getEntityModuleInfoFieldsFormatted($module); $fieldsName = $entity_field_info['fieldname']; if(is_array($fieldsName)) {