- Added possibility to encrypt received header, option 'http_received_header_encrypt',
added some more logic in encrypt/decrypt functions for security
1 files added
13 files modified
| | |
| | | CHANGELOG RoundCube Webmail |
| | | =========================== |
| | | |
| | | - Added possibility to encrypt received header, option 'http_received_header_encrypt', |
| | | added some more logic in encrypt/decrypt functions for security |
| | | - Fix Answered/Forwarded flag setting for messages in subfolders |
| | | - Fix autocomplete problem with capital letters (#1485792) |
| | | - Support UUencode content encoding (#1485839) |
New file |
| | |
| | | #!/usr/bin/env php |
| | | <?php |
| | | /* |
| | | |
| | | +-----------------------------------------------------------------------+ |
| | | | bin/decrypt.php | |
| | | | | |
| | | | This file is part of the RoundCube Webmail client | |
| | | | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | | PURPOSE: | |
| | | | Decrypt the encrypted parts of the HTTP Received: headers | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Tomas Tevesz <ice@extreme.hu> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | */ |
| | | |
| | | /*- |
| | | * If http_received_header_encrypt is configured, the IP address and the |
| | | * host name of the added Received: header is encrypted with 3DES, to |
| | | * protect information that some could consider sensitve, yet their |
| | | * availability is a must in some circumstances. |
| | | * |
| | | * Such an encrypted Received: header might look like: |
| | | * |
| | | * Received: from DzgkvJBO5+bw+oje5JACeNIa/uSI4mRw2cy5YoPBba73eyBmjtyHnQ== |
| | | * [my0nUbjZXKtl7KVBZcsvWOxxtyVFxza4] |
| | | * with HTTP/1.1 (POST); Thu, 14 May 2009 19:17:28 +0200 |
| | | * |
| | | * In this example, the two encrypted components are the sender host name |
| | | * (DzgkvJBO5+bw+oje5JACeNIa/uSI4mRw2cy5YoPBba73eyBmjtyHnQ==) and the IP |
| | | * address (my0nUbjZXKtl7KVBZcsvWOxxtyVFxza4). |
| | | * |
| | | * Using this tool, they can be decrypted into plain text: |
| | | * |
| | | * $ bin/decrypt_received.php 'my0nUbjZXKtl7KVBZcsvWOxxtyVFxza4' \ |
| | | * > 'DzgkvJBO5+bw+oje5JACeNIa/uSI4mRw2cy5YoPBba73eyBmjtyHnQ==' |
| | | * 84.3.187.208 |
| | | * 5403BBD0.catv.pool.telekom.hu |
| | | * $ |
| | | * |
| | | * Thus it is known that this particular message was sent by 84.3.187.208, |
| | | * having, at the time of sending, the name of 5403BBD0.catv.pool.telekom.hu. |
| | | * |
| | | * If (most likely binary) junk is shown, then |
| | | * - either the encryption password has, between the time the mail was sent |
| | | * and `now', changed, or |
| | | * - you are dealing with counterfeit header data. |
| | | */ |
| | | |
| | | if (php_sapi_name() != 'cli') { |
| | | die("Not on the 'shell' (php-cli).\n"); |
| | | } |
| | | |
| | | define('INSTALL_PATH', realpath(dirname(__FILE__).'/..') . '/'); |
| | | require INSTALL_PATH . 'program/include/iniset.php'; |
| | | |
| | | $config = new rcube_config(); |
| | | if (!$config->get('http_received_header_encrypt')) { |
| | | die("http_received_header_encrypt is not configured\n"); |
| | | } |
| | | |
| | | if ($argc < 2) { |
| | | die("Usage: " . basename($argv[0]) . " encrypted-hdr-part [encrypted-hdr-part ...]\n"); |
| | | } |
| | | |
| | | $RCMAIL = rcmail::get_instance(); |
| | | |
| | | for ($i = 1; $i < $argc; $i++) { |
| | | printf("%s\n", $RCMAIL->decrypt($argv[$i])); |
| | | }; |
| | |
| | | // add a received header to outgoing mails containing the creators IP and hostname |
| | | $rcmail_config['http_received_header'] = false; |
| | | |
| | | // Whether or not to encrypt the IP address and the host name |
| | | // these could, in some circles, be considered as sensitive information; |
| | | // however, for the administrator, these could be invaluable help |
| | | // when tracking down issues. |
| | | $rcmail_config['http_received_header_encrypt'] = false; |
| | | |
| | | // this string is used as a delimiter for message headers when sending |
| | | // leave empty for auto-detection |
| | | $rcmail_config['mail_header_delimiter'] = NULL; |
| | |
| | | * (Settings -> Password tab) |
| | | * |
| | | * @version 1.1 |
| | | * @author Aleksander 'A.L.E.C' Machniak |
| | | * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl> |
| | | * @editor Daniel Black |
| | | * |
| | | * Configuration Items (config/main.inc.php): |
| | |
| | | $curpwd = get_input_value('_curpasswd', RCUBE_INPUT_POST); |
| | | $newpwd = get_input_value('_newpasswd', RCUBE_INPUT_POST); |
| | | |
| | | if ($confirm && $_SESSION['password'] != $rcmail->encrypt_passwd($curpwd)) |
| | | if ($confirm && $rcmail->decrypt($_SESSION['password']) != $curpwd) |
| | | $rcmail->output->command('display_message', $this->gettext('passwordincorrect'), 'error'); |
| | | else if (!($res = $this->_save($curpwd,$newpwd))) { |
| | | $rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation'); |
| | | $_SESSION['password'] = $rcmail->encrypt_passwd($newpwd); |
| | | $_SESSION['password'] = $rcmail->encrypt($newpwd); |
| | | } else |
| | | $rcmail->output->command('display_message', $res, 'error'); |
| | | } |
| | |
| | | // return the complete edit form as table |
| | | $out = '<table' . $attrib_str . ">\n\n"; |
| | | |
| | | $a_show_cols = array('newpasswd' => array('type' => 'text'), |
| | | 'confpasswd' => array('type' => 'text')); |
| | | |
| | | if ($confirm) { |
| | | $a_show_cols['curpasswd'] = array('type' => 'text'); |
| | | // show current password selection |
| | | $field_id = 'curpasswd'; |
| | | $input_newpasswd = new html_passwordfield(array('name' => '_curpasswd', 'id' => $field_id, 'size' => 20)); |
| | | $input_newpasswd = new html_passwordfield(array('name' => '_curpasswd', 'id' => $field_id, |
| | | 'size' => 20, 'autocomplete' => 'off')); |
| | | |
| | | $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", |
| | | $field_id, |
| | |
| | | |
| | | // show new password selection |
| | | $field_id = 'newpasswd'; |
| | | $input_newpasswd = new html_passwordfield(array('name' => '_newpasswd', 'id' => $field_id, 'size' => 20)); |
| | | $input_newpasswd = new html_passwordfield(array('name' => '_newpasswd', 'id' => $field_id, |
| | | 'size' => 20, 'autocomplete' => 'off')); |
| | | |
| | | $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", |
| | | $field_id, |
| | |
| | | |
| | | // show confirm password selection |
| | | $field_id = 'confpasswd'; |
| | | $input_confpasswd = new html_passwordfield(array('name' => '_confpasswd', 'id' => $field_id, 'size' => 20)); |
| | | $input_confpasswd = new html_passwordfield(array('name' => '_confpasswd', 'id' => $field_id, |
| | | 'size' => 20, 'autocomplete' => 'off')); |
| | | |
| | | $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", |
| | | $field_id, |
| | |
| | | $curpwd = get_input_value('_curpasswd', RCUBE_INPUT_POST); |
| | | $newpwd = get_input_value('_newpasswd', RCUBE_INPUT_POST); |
| | | |
| | | if ($_SESSION['password'] != $rcmail->encrypt_passwd($curpwd)) { |
| | | if ($rcmail->decrypt($_SESSION['password']) != $curpwd) { |
| | | $rcmail->output->command('display_message', $this->gettext('passwordincorrect'), 'error'); |
| | | } |
| | | else if ($this->_save($newpwd)) { |
| | | $rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation'); |
| | | $_SESSION['password'] = $rcmail->encrypt_passwd($newpwd); |
| | | $_SESSION['password'] = $rcmail->encrypt($newpwd); |
| | | } |
| | | else { |
| | | $rcmail->output->command('display_message', $this->gettext('errorsaving'), 'error'); |
| | |
| | | $conn = false; |
| | | |
| | | if ($_SESSION['imap_host'] && !$this->imap->conn) { |
| | | if (!($conn = $this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) { |
| | | if (!($conn = $this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) { |
| | | if ($this->output) |
| | | $this->output->show_message($this->imap->error_code == -1 ? 'imaperror' : 'sessionerror', 'error'); |
| | | } |
| | |
| | | $_SESSION['imap_host'] = $host; |
| | | $_SESSION['imap_port'] = $imap_port; |
| | | $_SESSION['imap_ssl'] = $imap_ssl; |
| | | $_SESSION['password'] = $this->encrypt_passwd($pass); |
| | | $_SESSION['password'] = $this->encrypt($pass); |
| | | $_SESSION['login_time'] = mktime(); |
| | | |
| | | if ($_REQUEST['_timezone'] != '_default_') |
| | |
| | | return md5($auth_string); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypt IMAP password using DES encryption |
| | | * Encrypt using 3DES |
| | | * |
| | | * @param string Password to encrypt |
| | | * @return string Encryprted string |
| | | * @param string $clear clear text input |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not to base64_encode() the result before returning |
| | | * |
| | | * @return string encrypted text |
| | | */ |
| | | public function encrypt_passwd($pass) |
| | | public function encrypt($clear, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) { |
| | | /*- |
| | | * Add a single canary byte to the end of the clear text, which |
| | | * will help find out how much of padding will need to be removed |
| | | * upon decryption; see http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = pack("a*H2", $clear, "80"); |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) |
| | | { |
| | | $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); |
| | | mcrypt_generic_init($td, $this->config->get_des_key(), $iv); |
| | | $cypher = mcrypt_generic($td, $pass); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $cipher = $iv . mcrypt_generic($td, $clear); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else if (function_exists('des')) { |
| | | $cypher = des($this->config->get_des_key(), $pass, 1, 0, NULL); |
| | | else if (function_exists('des')) |
| | | { |
| | | define('DES_IV_SIZE', 8); |
| | | $iv = ''; |
| | | for ($i = 0; $i < constant('DES_IV_SIZE'); $i++) |
| | | $iv .= sprintf("%c", mt_rand(0, 255)); |
| | | $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); |
| | | } |
| | | else { |
| | | $cypher = $pass; |
| | | |
| | | else |
| | | { |
| | | raise_error(array( |
| | | 'code' => 500, |
| | | 'type' => 'php', |
| | | 'file' => __FILE__, |
| | | 'message' => "Could not convert encrypt password. Make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, false); |
| | | 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | |
| | | return base64_encode($cypher); |
| | | |
| | | return $base64 ? base64_encode($cipher) : $cipher; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decrypt IMAP password using DES encryption |
| | | * Decrypt 3DES-encrypted string |
| | | * |
| | | * @param string Encrypted password |
| | | * @return string Plain password |
| | | * @param string $cipher encrypted text |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not input is base64-encoded |
| | | * |
| | | * @return string decrypted text |
| | | */ |
| | | public function decrypt_passwd($cypher) |
| | | public function decrypt($cipher, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) { |
| | | $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); |
| | | mcrypt_generic_init($td, $this->config->get_des_key(), $iv); |
| | | $pass = mdecrypt_generic($td, base64_decode($cypher)); |
| | | $cipher = $base64 ? base64_decode($cipher) : $cipher; |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) |
| | | { |
| | | $iv = substr($cipher, 0, mcrypt_enc_get_iv_size($td)); |
| | | $cipher = substr($cipher, mcrypt_enc_get_iv_size($td)); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $clear = mdecrypt_generic($td, $cipher); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else if (function_exists('des')) { |
| | | $pass = des($this->config->get_des_key(), base64_decode($cypher), 0, 0, NULL); |
| | | else if (function_exists('des')) |
| | | { |
| | | define('DES_IV_SIZE', 8); |
| | | $iv = substr($cipher, 0, constant('DES_IV_SIZE')); |
| | | $cipher = substr($cipher, constant('DES_IV_SIZE')); |
| | | $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); |
| | | } |
| | | else { |
| | | $pass = base64_decode($cypher); |
| | | else |
| | | { |
| | | raise_error(array( |
| | | 'code' => 500, |
| | | 'type' => 'php', |
| | | 'file' => __FILE__, |
| | | 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | |
| | | return preg_replace('/\x00/', '', $pass); |
| | | |
| | | /*- |
| | | * Trim PHP's padding and the canary byte; see note in |
| | | * rcmail::encrypt() and http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = substr(rtrim($clear, "\0"), 0, -1); |
| | | |
| | | return $clear; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Build a valid URL to this instance of RoundCube |
| | |
| | | { |
| | | return $this->prop; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return a 24 byte key for the DES encryption |
| | | * |
| | | * @return string DES encryption key |
| | | */ |
| | | public function get_des_key() |
| | | { |
| | | $key = !empty($this->prop['des_key']) ? $this->prop['des_key'] : 'rcmail?24BitPwDkeyF**ECB'; |
| | | $len = strlen($key); |
| | | |
| | | // make sure the key is exactly 24 chars long |
| | | if ($len<24) |
| | | $key .= str_repeat('_', 24-$len); |
| | | else if ($len>24) |
| | | substr($key, 0, 24); |
| | | /** |
| | | * Return requested DES crypto key. |
| | | * |
| | | * @param string Crypto key name |
| | | * @return string Crypto key |
| | | */ |
| | | public function get_crypto_key($key) |
| | | { |
| | | // Bomb out if the requested key does not exist |
| | | if (!array_key_exists($key, $this->prop)) |
| | | { |
| | | raise_error(array( |
| | | 'code' => 500, |
| | | 'type' => 'php', |
| | | 'file' => __FILE__, |
| | | 'message' => "Request for unconfigured crypto key \"$key\"" |
| | | ), true, true); |
| | | } |
| | | |
| | | $key = $this->prop[$key]; |
| | | |
| | | // Bomb out if the configured key is not exactly 24 bytes long |
| | | if (strlen($key) != 24) |
| | | { |
| | | raise_error(array( |
| | | 'code' => 500, |
| | | 'type' => 'php', |
| | | 'file' => __FILE__, |
| | | 'message' => "Configured crypto key \"$key\" is not exactly 24 bytes long" |
| | | ), true, true); |
| | | } |
| | | |
| | | return $key; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Try to autodetect operating system and find the correct line endings |
| | | * |
| | |
| | | if ($this->prop["user_specific"]) { |
| | | // No password set, use the session password |
| | | if (empty($this->prop['bind_pass'])) { |
| | | $this->prop['bind_pass'] = $RCMAIL->decrypt_passwd($_SESSION["password"]); |
| | | $this->prop['bind_pass'] = $RCMAIL->decrypt($_SESSION['password']); |
| | | } |
| | | |
| | | // Get the pieces needed for variable replacement. |
| | |
| | | $smtp_user = $CONFIG['smtp_user']; |
| | | |
| | | if (strstr($CONFIG['smtp_pass'], '%p')) |
| | | $smtp_pass = str_replace('%p', $RCMAIL->decrypt_passwd($_SESSION['password']), $CONFIG['smtp_pass']); |
| | | $smtp_pass = str_replace('%p', $RCMAIL->decrypt($_SESSION['password']), $CONFIG['smtp_pass']); |
| | | else |
| | | $smtp_pass = $CONFIG['smtp_pass']; |
| | | |
| | |
| | | $messages['opnotpermitted'] = 'Operation not permitted!'; |
| | | $messages['nofromaddress'] = 'Missing e-mail address in selected identity'; |
| | | $messages['editorwarning'] = 'Switching to the plain text editor will cause all text formatting to be lost. Do you wish to continue?'; |
| | | $messages['httpreceivedencrypterror'] = 'A fatal configuration error occurred. Contact your administrator immediately. <b>Your message can not be sent.</b>'; |
| | | |
| | | ?> |
| | |
| | | $messages['opnotpermitted'] = 'Operation not permitted!'; |
| | | $messages['nofromaddress'] = 'Missing e-mail address in selected identity'; |
| | | $messages['editorwarning'] = 'Switching to the plain text editor will cause all text formatting to be lost. Do you wish to continue?'; |
| | | $messages['httpreceivedencrypterror'] = 'A fatal configuration error occurred. Contact your administrator immediately. <b>Your message can not be sent.</b>'; |
| | | |
| | | ?> |
| | |
| | | $messages['opnotpermitted'] = 'A művelet nem megengedett!'; |
| | | $messages['nofromaddress'] = 'Hiányzó email cím a kiválasztott feladónál'; |
| | | $messages['editorwarning'] = 'Az egyszerű szöveges formátumra való váltás az összes formázás elvesztésével jár. Biztosan folytatja?'; |
| | | $messages['httpreceivedencrypterror'] = 'Végzetes konfigurációs hiba történt, azonnal lépjen kapcsolatba az üzemeltetővel. <b>Az üzenet nem küldhető el.</b>'; |
| | | |
| | | ?> |
| | |
| | | $messages['opnotpermitted'] = 'Niedozwolona operacja!'; |
| | | $messages['nofromaddress'] = 'Brak adresu e-mail w wybranej tożsamości'; |
| | | $messages['editorwarning'] = 'Zmiana edytora spowoduje utratę formatowania tekstu. Czy jesteś pewien, że chcesz to zrobić?'; |
| | | $messages['httpreceivedencrypterror'] = 'Wystąpił błąd systemu. Skontaktuj się z administratorem. <b>Nie można wysłać wiadomości.</b>'; |
| | | |
| | | ?> |
| | |
| | | |
| | | /****** message sending functions ********/ |
| | | |
| | | // encrypt parts of the header |
| | | function rcmail_encrypt_header($what) |
| | | { |
| | | global $CONFIG, $RCMAIL; |
| | | if (!$CONFIG['http_received_header_encrypt']) |
| | | { |
| | | return $what; |
| | | } |
| | | return $RCMAIL->encrypt($what); |
| | | } |
| | | |
| | | // get identity record |
| | | function rcmail_get_identity($id) |
| | | { |
| | |
| | | $identity_arr['string'] = $from; |
| | | |
| | | // compose headers array |
| | | $headers = array('Date' => date('r'), |
| | | 'From' => rcube_charset_convert($identity_arr['string'], RCMAIL_CHARSET, $message_charset), |
| | | 'To' => $mailto); |
| | | $headers = array(); |
| | | |
| | | // if configured, the Received headers goes to top, for good measure |
| | | if ($CONFIG['http_received_header']) |
| | | { |
| | | $nldlm = $RCMAIL->config->header_delimiter() . "\t"; |
| | | $http_header = 'from '; |
| | | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { |
| | | $http_header .= rcmail_encrypt_header(gethostbyaddr($_SERVER['HTTP_X_FORWARDED_FOR'])) . |
| | | ' [' . rcmail_encrypt_header($_SERVER['HTTP_X_FORWARDED_FOR']) . ']'; |
| | | $http_header .= $nldlm . ' via '; |
| | | } |
| | | $http_header .= rcmail_encrypt_header(gethostbyaddr($_SERVER['REMOTE_ADDR'])) . |
| | | ' [' . rcmail_encrypt_header($_SERVER['REMOTE_ADDR']) .']'; |
| | | $http_header .= $nldlm . 'with ' . $_SERVER['SERVER_PROTOCOL'] . |
| | | ' ('.$_SERVER['REQUEST_METHOD'] . '); ' . date('r'); |
| | | $http_header = wordwrap($http_header, 69, $nldlm); |
| | | $headers['Received'] = $http_header; |
| | | } |
| | | |
| | | $headers['Date'] = date('r'); |
| | | $headers['From'] = rcube_charset_convert($identity_arr['string'], RCMAIL_CHARSET, $message_charset); |
| | | $headers['To'] = $mailto; |
| | | |
| | | // additional recipients |
| | | if (!empty($mailcc)) |
| | |
| | | } |
| | | |
| | | // additional headers |
| | | if ($CONFIG['http_received_header']) |
| | | { |
| | | $nldlm = $RCMAIL->config->header_delimiter() . "\t"; |
| | | $headers['Received'] = wordwrap('from ' . (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? |
| | | gethostbyaddr($_SERVER['HTTP_X_FORWARDED_FOR']).' ['.$_SERVER['HTTP_X_FORWARDED_FOR'].']'.$nldlm.' via ' : '') . |
| | | gethostbyaddr($_SERVER['REMOTE_ADDR']).' ['.$_SERVER['REMOTE_ADDR'].']'.$nldlm.'with ' . |
| | | $_SERVER['SERVER_PROTOCOL'].' ('.$_SERVER['REQUEST_METHOD'].'); ' . date('r'), |
| | | 69, $nldlm); |
| | | } |
| | | |
| | | $headers['Message-ID'] = $message_id; |
| | | $headers['X-Sender'] = $from; |
| | | |