. */ class MailOut { private $smtphost; private $smtpport; private $domain; private $from; private $rcpt; private $messages; private $boundary; private $dlv_msg; private $dlv_ret_nr; const MAIL_FAIL = 0; const MAIL_OK = 1; const MAIL_QUEUE = 2; public function __construct($from, $rcpt, $domain="", $smtphost="localhost", $smtpport=25) { global $setup; if( $this->check_email($from) ) $this->from = $from; else { debug("Invalid from: ".$from); return false; } if( $setup['Debug'] ) { $this->rcpt = $setup['WebmasterEmail']; }else { if( is_array($rcpt) ) { for( $i=0; $icheck_email($rcpt[$i]) ) { debug("Invalid rcpt: ".$rcpt[$i]); return false; } } $this->rcpt = $rcpt; }else { if( $this->check_email($rcpt) ) $this->rcpt = $rcpt; else { debug("Invalid rcpt: ".$rcpt); return false; } } } $this->smtphost = $smtphost; $this->smtpport = $smtpport; if( strlen($domain)>0 ) $this->domain = $domain; elseif( strlen($_SERVER['SERVER_NAME'])>0 ) $this->domain = $_SERVER['SERVER_NAME']; else $this->domain = DEFAULTDOMAIN; $this->messages = array(); $this->boundary = date('YmdHis') . '_' . mt_rand(10000, 99999) . "/" . $this->domain; $this->dlv_msg = ""; $this->dlv_ret_nr = 0; return true; } public function __destruct() { } public function set_message($text, $msg=0) { $this->messages[$msg]['body'] = $text; } public function add_header($header, $msg=0) { if( preg_match("/^(.*?):(.*)$/i", $header, $matches) ) { $title = $matches[1]; $value = $matches[2]; if( $this->allow_duplicateheader($title) ) { // duplicates allowed $this->messages[$msg]['header'][] = $header; return; }else { // no duplicates allowed, check if header already is present for( $i=0; $imessages[$msg]['header']); $i++ ) { if( preg_match("/^".$title.":(.*)$/i", $this->messages[$msg]['header'][$i]) ) { // duplicate found $this->messages[$msg]['header'][$i] = $header; return; } } // Seems this is not a duplicate $this->messages[$msg]['header'][] = $header; return; } }else { debug("invalid header: ".$header); } } public function set_from($from) { $this->add_header("From: ".$from); } public function set_subject($subject) { $this->add_header("Subject: ".$subject); } public function send($sendLaterOnError=false) { $this->checkHeader(); if( is_array($this->rcpt) ) { $to = $this->rcpt[0]; for( $i=1; $ircpt); $i++ ) { $to .= " ,".$this->rcpt[$i]; } }elseif( strlen($this->rcpt)>0 ) { $to = $this->rcpt; }else { return self::MAIL_FAIL; } if( $stream=$this->initStream($this->from, $to) ) { // headers $this->sendHeaders($stream, 0); // message $this->sendMessage($stream, 0); // attachment for( $i=1; $imessages); $i++ ) { // boundary $this->writeToStream($stream, "\n--".$this->boundary."\n"); // headers $this->sendHeaders($stream, $i); // message $this->sendMessage($stream, $i); } if( count($this->messages)>1 ) { // final boundary $this->writeToStream($stream, "\n--".$this->boundary."--\n"); } // close mail if( $this->finalizeStream($stream) ) return self::MAIL_OK; else { if( $sendLaterOnError ) { if( $this->queue() ) return self::MAIL_QUEUE; else return self::MAIL_FAIL; }else return self::MAIL_FAIL; } }else { // failed debug("Failed initStream()"); debug("Error ".$this->dlv_ret_nr.": ".$this->dlv_msg); if( $sendLaterOnError ) { if( $this->queue() ) return self::MAIL_QUEUE; else return self::MAIL_FAIL; } return self::MAIL_FAIL; } } public function get_delivery_msg() { return $this->dlv_msg; } public function get_delivery_ret_nr() { return $this->dlv_ret_nr; } private function check_email($Email) { // Check if the supplied e-mail address is a valid e-mail address. If yes, return true, else return false $re="/(^(\w|\.|-|\+)+@(\w|-)+(\.(\w|-)+)*\.[a-zA-Z]{2,4}$)/"; if( preg_match($re,$Email) ) { //Regex matches, now check MX $host = substr($Email, strpos($Email, "@") + 1); if( getmxrr($host, $mxhosts) OR checkdnsrr($host, "A") OR checkdnsrr($host, "AAAA") ) { // Accept MX, A and AAAA records return true; }else { return false; } }else { return false; } } private function checkHeader() { $k = array(); // Store all header names we use for( $i=0; $imessages[0]['header']); $i++ ) { if( preg_match("/^(.*?):(.*)$/i", $this->messages[0]['header'][$i], $matches) ) { $k[] = $matches[1]; } } if( !in_array("From", $k) ) $this->add_header("From: ".$this->from); if( !in_array("To", $k) ) { if( is_array($this->rcpt) ) { $to = $this->rcpt[0]; for( $i=1; $ircpt); $i++ ) { $to .= $this->rcpt[$i]; } $this->add_header("To: ".$to); }else { $this->add_header("To: ".$this->rcpt); } } if( !in_array("Date", $k) ) $this->add_header("Date: ".date("r")); if( !in_array("Message-ID", $k) ) $this->add_header("Message-ID: <" . date('YmdHis') . '.' . mt_rand(10000, 99999) . "@" . $this->domain . ">"); if( !in_array("Content-Type", $k) ) { if( count($this->messages)>1 ) $this->add_header("Content-Type: multipart/mixed; boundary=\"".$this->boundary."\""); else $this->add_header("Content-Type: text/plain; charset=UTF-8"); }else { if( count($this->messages)>1 ) { // Make sure we have a multipart/* message and a boundary $ok = false; for( $i=0; $imessages[0]['header']); $i++ ) { if( preg_match("/^Content-Type:(.*)$/i", $this->messages[0]['header'][$i], $matches) ) { if( preg_match("/^\s*multipart\/[a-z-]+;.*boundary=\"".$this->boundary."\".*$/i", $matches[1]) ) { $ok = true; } } } if( !$ok ) { // header does not mention multipart message, override... $this->add_header("Content-Type: multipart/mixed; boundary=\"".$this->boundary."\""); } } } $this->add_header("X-Mailer: AEGEE Helpdesk"); if( strlen($_SERVER['REMOTE_ADDR'])>0 ) $this->add_header("X-Posting-Host: ".$_SERVER['REMOTE_ADDR']); } private function queue() { global $sql; $query = "INSERT INTO `mailsqueued` (`date`, `to`, `content`, `mail`) VALUES ('".addslashes(date("Y-m-d H:i:s"))."', '"; if( is_array($this->rcpt) ) { $query .= addslashes($this->rcpt[0]); for( $i=1; $ircpt); $i++ ) { $query .= addslashes(", ".$this->rcpt[$i]); } }else { $query .= addslashes($this->rcpt); } $query .= "', '"; for( $i=0; $imessages[0]['header']); $i++ ) { if( preg_match("/^X-Content:\s*(.*)$/i", $this->messages[0]['header'][$i], $matches) ) { $query .= addslashes($matches[1]); } } $query .= "', '".addslashes(serialize($this))."')"; if( $sql->query($query) AND $sql->getaffected()==1 ) { return true; }else { return false; } } private function sendHeaders($stream, $msg=0) { $headerorder = array("Message-ID", "Date", "From", "To", "Cc", "Subject", "Content-Type"); $headers = $this->messages[$msg]['header']; for( $i=0; $iwriteToStream($stream, $this->headerLimit($headers[$j]."\n")); unset($headers[$j]); $headers = array_merge($headers); } } } for( $i=0; $iwriteToStream($stream, $this->headerLimit($headers[$i]."\n")); } $this->writeToStream($stream, "\n"); } private function sendMessage($stream, $msg=0) { $this->writeToStream($stream, $this->messageLimit($this->messages[$msg]['body'])); $this->writeToStream($stream, "\n"); } private function headerLimit($header) { ########## return $header; } private function messageLimit($message) { ########## return $message; } private function allow_duplicateheader($header) { return in_array(strtolower($header), array('received')); } private function preWriteToStream(&$s) { if( $s ) { if( $s{0} == '.' ) $s = '.' . $s; $s = str_replace("\n.","\n..",$s); } } private function initStream($from, $to) { $to.=","; $to_list=array(); $a=0; $b=strpos($to, ",", $a); while( !($b===false) ) { $to_list[]=substr($to, $a, $b-$a); $a=$b+1; $b=strpos($to, ",", $a); } $stream = @fsockopen($this->smtphost, $this->smtpport, $errorNumber, $errorString); if( !$stream ) { $this->dlv_msg = $errorString; $this->dlv_ret_nr = $errorNumber; return false; } $tmp = fgets($stream, 1024); if( $this->errorCheck($tmp, $stream) ) { return false; } if( $this->smtphost=="localhost" ) $helodomain = "localhost"; else $helodomain = $this->domain; /* Lets introduce ourselves */ fputs($stream, "EHLO ".$helodomain."\r\n"); $tmp = fgets($stream,1024); if( $this->errorCheck($tmp,$stream) ) { // fall back to HELO if EHLO is not supported if( $this->dlv_ret_nr == '500' ) { fputs($stream, "HELO ".$helodomain."\r\n"); $tmp = fgets($stream,1024); if( $this->errorCheck($tmp,$stream) ) { return false; } }else { return false; } } /* Ok, who is sending the message? */ fputs($stream, "MAIL FROM: <".$from.">\r\n"); $tmp = fgets($stream, 1024); if( $this->errorCheck($tmp, $stream) ) { return false; } /* send who the recipients are */ for( $i = 0, $cnt = count($to_list); $i < $cnt; $i++ ) { fputs($stream, "RCPT TO: <".$to_list[$i].">\r\n"); $tmp = fgets($stream, 1024); if( $this->errorCheck($tmp, $stream) ) { return false; } } /* Lets start sending the actual message */ fputs($stream, "DATA\r\n"); $tmp = fgets($stream, 1024); if( $this->errorCheck($tmp, $stream) ) { return false; } return $stream; } private function writeToStream($stream, $data) { $this->preWriteToStream($data); fputs($stream, $data); } private function finalizeStream($stream) { fputs($stream, "\r\n.\r\n"); /* end the DATA part */ $tmp = fgets($stream, 1024); $this->errorCheck($tmp, $stream); if( $this->dlv_ret_nr != 250 ) { return false; } fputs($stream, "QUIT\r\n"); /* log off */ fclose($stream); return true; } /* check if an SMTP reply is an error and set an error message) */ private function errorCheck($line, $smtpConnection) { $err_num = substr($line, 0, 3); $this->dlv_ret_nr = $err_num; $server_msg = substr($line, 4); while( substr($line, 0, 4) == ($err_num.'-') ) { $line = fgets($smtpConnection, 1024); $server_msg .= substr($line, 4); } if( ((int) $err_num{0}) < 4 ) { return false; } switch( $err_num ) { case '421': $message = _("Service not available, closing channel"); break; case '432': $message = _("A password transition is needed"); break; case '450': $message = _("Requested mail action not taken: mailbox unavailable"); break; case '451': $message = _("Requested action aborted: error in processing"); break; case '452': $message = _("Requested action not taken: insufficient system storage"); break; case '454': $message = _("Temporary authentication failure"); break; case '500': $message = _("Syntax error; command not recognized"); break; case '501': $message = _("Syntax error in parameters or arguments"); break; case '502': $message = _("Command not implemented"); break; case '503': $message = _("Bad sequence of commands"); break; case '504': $message = _("Command parameter not implemented"); break; case '530': $message = _("Authentication required"); break; case '534': $message = _("Authentication mechanism is too weak"); break; case '535': $message = _("Authentication failed"); break; case '538': $message = _("Encryption required for requested authentication mechanism"); break; case '550': $message = _("Requested action not taken: mailbox unavailable"); break; case '551': $message = _("User not local; please try forwarding"); break; case '552': $message = _("Requested mail action aborted: exceeding storage allocation"); break; case '553': $message = _("Requested action not taken: mailbox name not allowed"); break; case '554': $message = _("Transaction failed"); break; default: $message = _("Unknown response"); break; } $this->dlv_msg = $message; $this->dlv_server_msg = nl2br(htmlspecialchars($server_msg)); return true; } } ?>