Your IP : 172.28.240.42


Current Path : /var/www/html/clients/old.e-nkama.ru/e-nkama_bitrix/bitrix/modules/mail/classes/general/
Upload File :
Current File : /var/www/html/clients/old.e-nkama.ru/e-nkama_bitrix/bitrix/modules/mail/classes/general/smtp.php

<?
class CSMTPServer
{
	var $arServers = Array();
	
	var $logFile;
	var $logFileName = "/bitrix/modules/smtpd.log";
	var $logLevel = 10;
	var $logMaxSize = 2000000;
	var $startPeriodTimeTruncate;
	var $startTime;

	function WriteToLog($txt, $level)
	{
		$this->logLevel = IntVal(COption::GetOptionString("mail", "smtp_log_level", "4"));

		if ($this->logLevel < $level)
			return;

		if (MicroTime(true) - $this->startPeriodTimeTruncate > 600)
		{
			if ($this->logFile)
				FClose($this->logFile);

			$this->logFile = null;

			if (File_Exists($_SERVER["DOCUMENT_ROOT"].$this->logFileName))
			{
				$logSize = @FileSize($_SERVER["DOCUMENT_ROOT"].$this->logFileName);
				$logSize = IntVal($logSize);

				if ($logSize > $this->logMaxSize)
				{
					if (($fp = @FOpen($_SERVER["DOCUMENT_ROOT"].$this->logFileName, "rb"))
						&& ($fp1 = @FOpen($_SERVER["DOCUMENT_ROOT"].$this->logFileName."_", "wb")))
					{
						$iSeekLen = IntVal($logSize - $this->logMaxSize / 2.0);
						FSeek($fp, $iSeekLen);

						@FWrite($fp1, "Truncated ".Date("Y-m-d H:i:s")."\n---------------------------------\n");
						do
						{
							$data = FRead($fp, 8192);
							if (StrLen($data) == 0)
								break;

							@FWrite($fp1, $data);
						}
						while (true);

						@FClose($fp);
						@FClose($fp1);

						@Copy($_SERVER["DOCUMENT_ROOT"].$this->logFileName."_", $_SERVER["DOCUMENT_ROOT"].$this->logFileName);
						@UnLink($_SERVER["DOCUMENT_ROOT"].$this->logFileName."_");
					}
				}
				ClearStatCache();
			}

			$this->startPeriodTimeTruncate = MicroTime(true);
		}

		if (!$this->logFile || $this->logFile == null)
			$this->logFile = FOpen($_SERVER["DOCUMENT_ROOT"].$this->logFileName, "a");

		if (!$this->logFile)
		{
			echo "Can't write to log\n---------------------------------\n";
			return;
		}

		FWrite($this->logFile, Date("Y-m-d H:i:s")."\t".trim($txt)."\n");
		FFlush($this->logFile);

		//if ($level > 4)
			echo trim($txt)."\n---------------------------------\n";
	}

	function Run()
	{
		$var = new CSMTPServer();
		$var->startTime = time();
		$var->Start(); 
		$var->Listen(); 
	}
	
	function Start()
	{
		if(file_exists($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stop.php"))
			@unlink($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stop.php");
		if(file_exists($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd.php"))
			@unlink($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd.php");

		ini_set('max_execution_time', 0);
		set_time_limit(0);
		ob_implicit_flush(true);

		while(@ob_end_clean());

		$dbr = CMailBox::GetList(array(), array("ACTIVE"=>"Y", "SERVER_TYPE"=>"smtp"));	
		while($arr = $dbr->Fetch())
		{
			$server = new CSMTPServerHost($this, $arr);
			$server->Start();
			$this->arServers[] = $server;
		}
	}
	
	function ReloadServers()
	{
		global $BX_MAIL_FILTER_CACHE;
		$BX_MAIL_FILTER_CACHE = Array();

		$rnd = uniqid();
		$dbr = CMailBox::GetList(array(), array("ACTIVE"=>"Y", "SERVER_TYPE"=>"smtp"));
		$arFounded = Array();	
		while($arr = $dbr->Fetch())
		{
			$bFound = false;
			foreach($this->arServers as $server)
			{
				if(
					$server->arFields["PORT"] == $arr["PORT"]
					&& $server->arFields["SERVER"] == ($arr["SERVER"]=="*"?"0.0.0.0":$arr["SERVER"])
					)
					{
						$server->arFields = $arr;
						$server->rnd = $rnd;
						$bFound = true;
						break;
					}
			}

			if(!$bFound)
			{
				$server = new CSMTPServerHost($this, $arr);
				$server->rnd = $rnd;
				$server->Start();
				$this->arServers[] = $server;
			}
		}

		$arServers = $this->arServers;
		foreach($arServers as $k=>$server)
		{
			if($server->rnd!=$rnd)
				$server->Stop($k);
		}
	}

	function Listen()
	{
		global $DB;
		$cnt = 100;
		while (true)
		{
			$cnt++;
			if($cnt>5)
			{
				$cnt = 0; 
				$stats = Array(
						'started'=>$this->startTime,
						'uptime'=>time() - $this->startTime,
						'messages'=>0,
						'connections'=>0,
						'connections_now'=>0,
						'servers'=>Array()
						);

				foreach($this->arServers as $arServer)
				{
					$stats["servers"][] = Array(
							'id'=>$arServer->arFields["ID"],
							'server'=>$arServer->arFields["SERVER"],
							'port'=>$arServer->arFields["PORT"],
							'started'=>$arServer->startTime
						);
					$stats["messages"] += $arServer->msgCount;
					$stats["connections"] += $arServer->conCount;
					$stats["connections_now"] += count($arServer->arClients);
				}

				$f = fopen($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stats.php", "w+");
				fwrite($f, '<?return unserialize("'.addslashes(serialize($stats)).'");?>');
				fclose($f);

				
				if(file_exists($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd.php"))
				{
					$this->ReloadServers();
					@unlink($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd.php");
				}

				if(file_exists($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stop.php"))
				{
					@unlink($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stop.php");
					if(file_exists($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stats.php"))
						@unlink($_SERVER["DOCUMENT_ROOT"]."/bitrix/cache/smtpd_stats.php");
					die();
				}
				
				$DB->Query("SELECT 'x' FROM b_user WHERE 1=0"); // nop
			}
			
			$arReadSockets = Array();

			foreach($this->arServers as $server)
				$arReadSockets = array_merge($arReadSockets, $server->GetSockets());

			if(count($arReadSockets)<=0)
				sleep(1);
			else
			{
				$n = @stream_select($arReadSockets, $w = null, $e = null, 3);
				if($n > 0)
				{
					foreach($arReadSockets as $r)
					{
						if(($server = $this->FindServerSocket($r))!==false)
						{
							$server->AddConnection();
						}
						else
						{
							if(($conn = $this->FindServerConnection($r))!==false)
							{
								$conn->Receive();
							}
						}
					}
				}
			}

			$arServers = $this->arServers;
			foreach($arServers as $server)
				$server->CheckTimeout(600);
		}
	}

	function FindServerSocket($s)
	{
		$arServers = $this->arServers;
		foreach($arServers as $server)
			if($s == $server->sockServer)
				return $server;

		return false;
	}

	function FindServerConnection($s)
	{
		$arServers = $this->arServers;
		foreach($arServers as $server)
			if(($conn = $server->FindConnection($s))!==false)
				return $conn;
		return false;
	}

	function Stop()
	{		
		if ($this->logFile)
			FClose($this->logFile);
	}
	
	function RemoveHost($i)
	{
		unset($this->arServers[$i]);
	}
}

class CSMTPServerHost
{
	var $sockServer;
	var $server;
	var $initialized;

	var $arClients = array();
	var $arClientsIndex = array();
	var $lastClientId;

	var $arSockets = array();

	var $startPeriodTime;
	var $arFields = Array();
	var $msgCount = 0;
	var $conCount = 0;

	function FindConnection($s)
	{
		$id = array_search($s, $this->arSockets);
		if($id !== false)
			return $this->arClients[$id];
		return false;
	}

	function GetSockets()
	{
		if($this->sockServer)
			return array_merge(array($this->sockServer), $this->arSockets);

		return array();
	}

	function CSMTPServerHost($server, $arFields)
	{
		$this->server = $server;
		$this->arFields = $arFields;

		$this->arClients = array();
		$this->arClientsIndex = array();
		$this->lastClientId = -1;
	}
	
	function AddConnection()
	{
		if(Is_Resource($sock = stream_socket_accept($this->sockServer, 0, $ip)))
		{
			$this->lastClientId++;
			$id = $this->lastClientId;

			$this->WriteToLog("Client connected (".$id.", ".$ip.", ".$sock.")", 5);

			stream_set_timeout($sock, 5);
			$this->arClients[$id] = new CSMTPConnection($id, $sock, $this);
			$this->arClients[$id]->ip = $ip;

			$this->arSockets[$id] = $sock;
			$this->conCount++;

			return true;
		}
		return false;
	}
	
	function RemoveConnection($id)
	{
		unset($this->arClients[$id]);
		unset($this->arSockets[$id]);
		$this->WriteToLog("Connection removed (".$id.", ".$this->arClients[$id]->ip.", ".$this->arClients[$id]->sock.")", 3);
		if($this->_stopAfterDisconnect && count($this->arClients)<=0)
			$this->_Stop();
	}
	
	function WriteToLog($txt, $level)
	{
		$this->server->WriteToLog($txt, $level);
	}

	function Start()
	{
		$this->startPeriodTime = microtime(true);
		$this->startPeriodTimeTruncate = microtime(true);

		$this->sockServer = stream_socket_server("tcp://".($this->arFields["SERVER"]=="*" ? "0.0.0.0" : $this->arFields["SERVER"]).":".$this->arFields["PORT"], $errno, $errstr);

		if (!$this->sockServer)
		{
			$this->WriteToLog("Create socket error: $errstr ($errno)", 1);
			return false;
		}

		$this->WriteToLog("Server #".$this->arFields["ID"]." started: ".($this->arFields["SERVER"]=="*"?"0.0.0.0":$this->arFields["SERVER"]).":".$this->arFields["PORT"], 1);
		return true;
	}

	function Stop($num)
	{
		$this->num = $num;
		if(count($this->arClients)<=0)
			$this->_Stop();
		else
			$this->_stopAfterDisconnect = true;
	}

	function _Stop()
	{
		if($this->sockServer)
		{
			@FClose($this->sockServer);
			$this->WriteToLog("Server #".$this->arFields["ID"]." stopped: ".($this->arFields["SERVER"]=="*"?"0.0.0.0":$this->arFields["SERVER"]).":".$this->arFields["PORT"], 1);
		}

		$this->server->RemoveHost($this->num);
	}
	
	function CheckTimeout($timeout)
	{
		$arConns = $this->arClients;
		foreach($arConns as $k=>$c)
			if(time() - $c->lastRecieve > $timeout)
				$this->RemoveConnection($k);
	}
}


class CSMTPConnection 
{
	var $id;
	var $sock;
	var $connected = false;
	var $authenticated = false;
	var $readBuffer = "";
	var $__listenFunc = false;
	var $arMsg = Array();
	var $server;
	var $lastRecieve;
	var $auth_user_id = 0;
	var $msgCount = 0;

	function CSMTPConnection($id, $sock, $serv)
	{
		$this->id = $id;
		$this->sock = $sock;
		$this->connected = true;
		$this->authenticated = false;
		$this->server = $serv;
		$this->lastRecieve = time();
		$this->uid = md5(uniqid());
		$this->arMsg = array('LOCAL_ID'=>md5(uniqid()));		
		$this->Send('220');
	}

	function WriteToLog($txt, $level)
	{
		$this->server->WriteToLog($txt." (C:".$this->uid.")", $level);
	}

	function Receive()
	{
		$this->readBuffer .= FRead($this->sock, 8192);
		$this->WriteToLog("C<- (".$this->id.")\t".$this->readBuffer, 10);
		$res = $this->__ParseBuffer();
		$this->lastRecieve = time();

		if($this->sock && feof($this->sock)) 
			$this->Disconnect();

		return $res;
	}

	function __ParseBuffer()
	{
		if(StrLen($this->readBuffer) <= 0)
			return false;
			
		if($this->__listenFunc == '__AuthLoginHandler')
			return $this->__AuthLoginHandler();

		if($this->__listenFunc == '__AuthPlainHandler')
			return $this->__AuthPlainHandler();

		if($this->__listenFunc == '__DataHandler')
			return $this->__DataHandler();

		if(strpos($this->readBuffer, "\r\n")===false)
			return false;
		
		$this->readBuffer = Trim($this->readBuffer);

		$res = false;
		if(($p = strpos($this->readBuffer, " "))!==false)
		{
			$command = substr($this->readBuffer, 0, $p);
			$res = $this->__ProcessCommand($command, substr($this->readBuffer, $p+1));
		}
		else
		{
			$res = $this->__ProcessCommand($this->readBuffer);
		}

		if($res)
			$this->readBuffer = "";

		return true;
	}

	function Send($code, $text = "")
	{
		if (!$this->connected)
			return false;

		if (intval($code) <= 0)
			return false;

		if($text=='')
		{
			$results = Array(
				'211'=>'System status, or system help reply',
				'214'=>'Help message', //[Information on how to use the receiver or the meaning of a particular non-standard command; this reply is useful only to the human user]
				'220'=>'<domain> Service ready',
				'221'=>'<domain> Service closing transmission channel',
				'250'=>'Requested mail action okay, completed',
				'251'=>'User not local; will forward to <forward-path>',
				'354'=>'Start mail input; end with <CRLF>.<CRLF>',
				'421'=>'<domain> Service not available,', //closing transmission channel [This may be a reply to any command if the service knows it must shut down]
				'450'=>'Requested mail action not taken: mailbox unavailable', //[E.g., mailbox busy]
				'451'=>'Requested action aborted: local error in processing',
				'452'=>'Requested action not taken: insufficient system storage',
				'500'=>'Syntax error, command unrecognized', //[This may include errors such as command line too long]
				'501'=>'Syntax error in parameters or arguments',
				'502'=>'Command not implemented',
				'503'=>'Bad sequence of commands',
				'504'=>'Command parameter not implemented',
				'550'=>'Requested action not taken: mailbox unavailable', //[E.g., mailbox not found, no access]
				'551'=>'User not local; please try <forward-path>',
				'552'=>'Requested mail action aborted: exceeded storage allocation',
				'553'=>'Requested action not taken: mailbox name not allowed', //[E.g., mailbox syntax incorrect]
				'554'=>'Transaction failed',
				);
			$text = $results[$code];
		}

		return $this->__Send($code." ".$text."\r\n");
	}

	function __Send($message)
	{
		if (StrLen($message) <= 0)
			return false;

		$this->WriteToLog("S-> (".$this->id.")\t".$message, 10);

		$r = FWrite($this->sock, $message);

		return ($r !== false);
	}

	function Disconnect()
	{
		@FClose($this->sock);
		$this->sock = false;

		$this->WriteToLog("Client disconnected (".$this->id.", ".$this->ip.")", 5);
		$this->server->RemoveConnection($this->id);
	}
	
	function CheckRelaying($email)
	{
		$domains = preg_split('/[\s]+/', strtolower($this->server->arFields['DOMAINS']), -1, PREG_SPLIT_NO_EMPTY);
		if(count($domains)<=0)
			return true;

		if(!is_array($this->arMsg["FOR_RELAY"]))
			$this->arMsg["FOR_RELAY"] = array();

		$p = strpos($email, "@");
		$email_domain = substr($email, $p+1);
		
		if(in_array($email_domain, $domains))
		{
			$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] Accepted for relaying '.$email, 8);
			return true;
		}

		if($this->server->arFields['RELAY']!='Y')
			return false;

		if($this->server->arFields['AUTH_RELAY']=='Y' && $this->auth_user_id<=0)
			return false;

		$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] Accepted for relaying '.$email, 8);
		$this->arMsg["FOR_RELAY"][]	= $email;
		return true;
	}
	
	//îáðàáîò÷èê êîìàíä
	function __ProcessCommand($command, $arg = '')
	{
		switch(strtoupper($command))
		{
		case "HELO":
			$this->Send('250', 'domain name should be qualified');
			if(trim($arg)=='')
				$this->host = $this->ip;
			else
				$this->host = $arg;
			//500, 501, 504, 421
			break;
		case "SEND":
		case "SOML":
		case "SAML":
		case "MAIL":
			if(!preg_match('#FROM[ ]*:[ ]*(.+)#i', $arg, $arMatches))
				$this->Send('501', 'Unrecognized parameter '.$arg);
			elseif($this->arMsg["FROM"])
					$this->Send('503', 'Sender already specified');
			else
			{
				$email = $arMatches[1];
				$email = CMailUtil::ExtractMailAddress($email);
				if($email=='' || !check_email($email))
					$this->Send('501', '<'.$email.'> Invalid Address');
				else
				{
					$this->arMsg["FROM"] = $email;
					$this->arMsg["TO"] = array();

					$this->Send('250', '<'.$email.'> Sender ok');
				}
			}
			//F: 552, 451, 452
			//E: 500, 501, 421
			break;
		case "RCPT":
			if(!preg_match('#TO[ ]*:[ ]*(.+)#i', $arg, $arMatches))
				$this->Send('501', 'Unrecognized parameter '.$arg);
			else
			{
				$email = $arMatches[1];
				$email = CMailUtil::ExtractMailAddress($email);
				if($email=='' || !check_email($email))
					$this->Send('501', '<'.$email.'> Invalid Address');
				elseif(false)
					$this->Send('550', '<'.$email.'> User unknown');
				elseif(!$this->CheckRelaying($email))
					$this->Send('550', '<'.$email.'>... Relaying denied.');
				elseif(!$this->arMsg["FROM"])
					$this->Send('503', 'Sender is not specified');
				else
				{
					$this->arMsg["TO"][] = $email;
					$this->Send('250', '<'.$email.'> ok');

	               //S: 250, 251
	               //F: 550, 551, 552, 553, 450, 451, 452
	               //E: 500, 501, 503, 421
				}
			}
			break;
		case "DATA":
			if(!$this->arMsg["FROM"] || !$this->arMsg["TO"] || count($this->arMsg["TO"])==0)
				$this->Send('503');
			else
			{
				$this->Send('354');
				$this->__listenFunc = '__DataHandler';
			}
            // I: 354 -> data -> S: 250
            //                      F: 552, 554, 451, 452
            //   F: 451, 554
            //   E: 500, 501, 503, 421
			break;
		case "RSET":
			$this->Send('250', 'Resetting');
			$this->arMsg = array('LOCAL_ID'=>md5(uniqid()));
			//E: 500, 501, 504, 421
			break;
		case "QUIT":
			$this->Send('221');
			$this->Disconnect();
            //E: 500
			break;
		case "EHLO":
			if(trim($arg)=='')
				$this->host = $this->ip;
			else
				$this->host = $arg;

			$this->Send('250-ehlo', '');
			$this->Send('250-AUTH LOGIN PLAIN', '');
			//$this->Send('250-SIZE', '');
			$this->Send('250-HELP', '');
			$this->Send('250', 'EHLO');
			/*
			250-mail.company2.tld is pleased to meet you
			250-DSN
			250-SIZE
			250-STARTTLS
			250-AUTH LOGIN PLAIN CRAM-MD5 DIGEST-MD5 GSSAPI MSN NTLM
			250-ETRN
			250-TURN
			250-ATRN
			250-NO-SOLICITING
			250-HELP
			250-PIPELINING
			250 EHLO
			*/
			break;
		case "AUTH":
			if($this->authorized)
				$this->Send('503', 'Already authorized');
			elseif(count($this->arMsg)>1)
				$this->Send('503', 'Mail transaction is active');
			elseif(!preg_match('#^([A-Z0-9-_]+)[ ]*(\S*)$#i', $arg, $arMatches))
				$this->Send('501', 'Unrecognized parameter '.$arg);
			else
			{
				switch(strtoupper($arMatches[1]))
				{
				case "LOGIN":
					$this->Send('334', 'VXNlcm5hbWU6'); 
					$this->__listenFunc = '__AuthLoginHandler';
					$this->__login = false;
					break;
				case "PLAIN":
					if($arMatches[2] && trim($arMatches[2])!='')
					{
						$pwd = base64_decode($arMatches[2]);
						$this->Authorize($pwd, $pwd);
					}
					else
					{
						$this->Send('334', ''); 
						$this->__listenFunc = '__AuthPlainHandler';
					}
					break;
				default:
					$this->Send('504', 'Unrecognized authentication type.');
				}
			}

			break;
		case "NOOP":
	        $this->Send('250');
	        //E: 500, 421
			break;
		case "HELP":
	        //       S: 211, 214
	        //       E: 500, 501, 502, 504, 421
			break;
		case "EXPN": 
			//<string>
	        //       S: 250
	        //       F: 550
	        //       E: 500, 501, 502, 504, 421
			break;
		case "VRFY":
	        //       S: 250, 251
	        //       F: 550, 551, 553
	        //       E: 500, 501, 502, 504, 421
			break;
		default:
			$this->Send('500', $command.' command unrecognized');
		}
		return true;
	}
	
	function Authorize($login, $password)
	{
		$authResult = $GLOBALS["USER"]->Login($login, $password, "N");

		if($authResult === true)
		{
			$this->Send("235", "Authentication successful");
			$this->auth_user_id = $GLOBALS["USER"]->GetID();
			$this->authorized = true;
			$this->WriteToLog('Authentication successful '.$this->auth_user_id, 7);
			return true;
		}

		$this->Send("535", "authorization failed");

		return false;
	}

	function __AuthLoginHandler()
	{
		if(strpos($this->readBuffer, "\r\n")===false)
			return false;

		$this->readBuffer = trim($this->readBuffer);
		if($this->readBuffer=="*")
			$this->Send('501', 'AUTH aborted');
		else
		{
			$pwd = base64_decode($this->readBuffer);
			if($this->__login === false)
			{
				$this->__login = $pwd;
				$this->Send('334', 'UGFzc3dvcmQ6'); 
				$this->readBuffer = "";
				return false;
			}
			else
			{
				$this->Authorize($this->__login, $pwd);
			}
		}

		$this->__login = false;
		$this->readBuffer = "";
		$this->__listenFunc = false;
		return true;
	}

	function __AuthPlainHandler()
	{
		if(strpos($this->readBuffer, "\r\n")===false)
			return false;
		$this->readBuffer = trim($this->readBuffer);
		if($this->readBuffer=="*")
			$this->Send('501', 'AUTH aborted');
		else
		{
			$pwd = base64_decode($this->readBuffer);
			if($pwd == '')
				$this->Send('501', 'Base64 decode error');
			else
			{
				$pwd = ltrim($pwd, chr(0));
				$this->Authorize(substr($pwd, 0, strpos($pwd, chr(0))), substr($pwd, strpos($pwd, chr(0))+1));
			}				
		}

		$this->readBuffer = "";
		$this->__listenFunc = false;
		return true;
	}

	function __DataHandler()
	{
		if(strpos($this->readBuffer, "\r\n.\r\n")===false)
			return false;

		$this->readBuffer = substr($this->readBuffer, 0, -5);

		$this->readBuffer = str_replace("\r\n..", "\r\n.", $this->readBuffer);

		// Äîáàâëåíèå ñîîáùåíèÿ êóäà íàäî
		$message = $this->readBuffer;
		$this->arMsg["MSG"] = $message;

		$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] Start processing mail...', 7);

		$p = strpos($message, "\r\n\r\n");
		if($p>0)
		{
			$message_header = substr($message, 0, $p);
			$message_text = substr($message, $p+2);

			$arLocalTo = Array();
			foreach($this->arMsg["TO"] as $to)
			{
				if(is_array($this->arMsg["FOR_RELAY"]) && in_array($to, $this->arMsg["FOR_RELAY"]))
				{
					$message_header_add = 
						"Received: from ".$this->host." by ".$this->server->arFields["SERVER"]." with Bitrix SMTP Server \r\n".
						"\t".date("r")."\r\n".
						"\tfor <".$to.">; \r\n".
						"Return-Path: <".$this->arMsg["FROM"].">\r\n";

					$subject = "";
					$message_header_new = $message_header;
					if(preg_match('/(Subject:\s*([^\r\n]*\r\n(\t[^\r\n]*\r\n)*))\S/is', $message_header_new."\r\nx", $reg))
					{
						$message_header_new = trim(str_replace($reg[1], "", $message_header_new."\r\n"));
						$subject = trim($reg[2]);
					}

					$r = bxmail($to, $subject, $message_text, $message_header_add.$message_header_new);
					$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] Relay message to '.$to.' ('.($r?'OK':'FAILED').')', 7);
				}
				else
					$arLocalTo[] = $to;
			}
	
			if(count($arLocalTo)>0)
			{
				$message_header_add = 
					"Received: from ".$this->host." by ".$this->server->arFields["SERVER"]." with Bitrix SMTP Server \r\n".
					"\t".date("r")."\r\n".
					"Return-Path: <".$this->arMsg["FROM"].">\r\n".
					"X-Original-Rcpt-to: ".implode(", ", $arLocalTo)."\r\n";

				$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] Message add: '.$message_header_add.$message, 9);

				if($this->server->arFields["CHARSET"]!='')
					$charset = $this->server->arFields["CHARSET"];
				else
					$charset = $this->server->arFields["LANG_CHARSET"];

				$message_id = CMailMessage::AddMessage($this->server->arFields["ID"], $message_header_add.$message, $charset);

				$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] Message sent to '.implode(", ", $arLocalTo).' ('.$message_id.')', 7);
			}			
			$this->Send('250', $message_id.' Message accepted for delivery');
		}
		else
			$this->Send('554', ' Bad message format');

		$this->WriteToLog('['.$this->arMsg["LOCAL_ID"].'] End processing mail...', 7);

		$this->readBuffer = "";
		$this->__listenFunc = false;
		$this->arMsg = array('LOCAL_ID'=>md5(uniqid()));

		$this->msgCount++;
		$this->server->msgCount++;
		return true;
	}
}
?>