diff options
Diffstat (limited to 'management-interface/lib/smtp.php')
-rw-r--r-- | management-interface/lib/smtp.php | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/management-interface/lib/smtp.php b/management-interface/lib/smtp.php new file mode 100644 index 0000000..d9041b5 --- /dev/null +++ b/management-interface/lib/smtp.php @@ -0,0 +1,274 @@ +<?php + +/* + Copyright (c) 2009-2014 F3::Factory/Bong Cosca, All rights reserved. + + This file is part of the Fat-Free Framework (http://fatfree.sf.net). + + THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF + ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + Please see the license.txt file for more information. +*/ + +//! SMTP plug-in +class SMTP extends Magic { + + //@{ Locale-specific error/exception messages + const + E_Header='%s: header is required', + E_Blank='Message must not be blank', + E_Attach='Attachment %s not found'; + //@} + + protected + //! Message properties + $headers, + //! E-mail attachments + $attachments, + //! SMTP host + $host, + //! SMTP port + $port, + //! TLS/SSL + $scheme, + //! User ID + $user, + //! Password + $pw, + //! TCP/IP socket + $socket, + //! Server-client conversation + $log; + + /** + * Fix header + * @return string + * @param $key string + **/ + protected function fixheader($key) { + return str_replace(' ','-', + ucwords(preg_replace('/[_-]/',' ',strtolower($key)))); + } + + /** + * Return TRUE if header exists + * @return bool + * @param $key + **/ + function exists($key) { + $key=$this->fixheader($key); + return isset($this->headers[$key]); + } + + /** + * Bind value to e-mail header + * @return string + * @param $key string + * @param $val string + **/ + function set($key,$val) { + $key=$this->fixheader($key); + return $this->headers[$key]=$val; + } + + /** + * Return value of e-mail header + * @return string|NULL + * @param $key string + **/ + function get($key) { + $key=$this->fixheader($key); + return isset($this->headers[$key])?$this->headers[$key]:NULL; + } + + /** + * Remove header + * @return NULL + * @param $key string + **/ + function clear($key) { + $key=$this->fixheader($key); + unset($this->headers[$key]); + } + + /** + * Return client-server conversation history + * @return string + **/ + function log() { + return str_replace("\n",PHP_EOL,$this->log); + } + + /** + * Send SMTP command and record server response + * @return string + * @param $cmd string + * @param $log bool + **/ + protected function dialog($cmd=NULL,$log=TRUE) { + $socket=&$this->socket; + if (!is_null($cmd)) + fputs($socket,$cmd."\r\n"); + $reply=''; + while (!feof($socket) && ($info=stream_get_meta_data($socket)) && + !$info['timed_out'] && $str=fgets($socket,4096)) { + $reply.=$str; + if (preg_match('/(?:^|\n)\d{3} .+?\r\n/s',$reply)) + break; + } + if ($log) { + $this->log.=$cmd."\n"; + $this->log.=str_replace("\r",'',$reply); + } + return $reply; + } + + /** + * Add e-mail attachment + * @return NULL + * @param $file + **/ + function attach($file) { + if (!is_file($file)) + user_error(sprintf(self::E_Attach,$file)); + $this->attachments[]=$file; + } + + /** + * Transmit message + * @return bool + * @param $message string + * @param $log bool + **/ + function send($message,$log=TRUE) { + if ($this->scheme=='ssl' && !extension_loaded('openssl')) + return FALSE; + // Message should not be blank + if (!$message) + user_error(self::E_Blank); + $fw=Base::instance(); + // Retrieve headers + $headers=$this->headers; + // Connect to the server + $socket=&$this->socket; + $socket=@fsockopen($this->host,$this->port); + if (!$socket) + return FALSE; + stream_set_blocking($socket,TRUE); + // Get server's initial response + $this->dialog(NULL,FALSE); + // Announce presence + $reply=$this->dialog('EHLO '.$fw->get('HOST'),$log); + if (strtolower($this->scheme)=='tls') { + $this->dialog('STARTTLS',$log); + stream_socket_enable_crypto( + $socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT); + $reply=$this->dialog('EHLO '.$fw->get('HOST'),$log); + if (preg_match('/8BITMIME/',$reply)) + $headers['Content-Transfer-Encoding']='8bit'; + else { + $headers['Content-Transfer-Encoding']='quoted-printable'; + $message=quoted_printable_encode($message); + } + } + if ($this->user && $this->pw && preg_match('/AUTH/',$reply)) { + // Authenticate + $this->dialog('AUTH LOGIN',$log); + $this->dialog(base64_encode($this->user),$log); + $this->dialog(base64_encode($this->pw),$log); + } + // Required headers + $reqd=array('From','To','Subject'); + foreach ($reqd as $id) + if (empty($headers[$id])) + user_error(sprintf(self::E_Header,$id)); + $eol="\r\n"; + $str=''; + // Stringify headers + foreach ($headers as $key=>$val) + if (!in_array($key,$reqd)) + $str.=$key.': '.$val.$eol; + // Start message dialog + $this->dialog('MAIL FROM: '.strstr($headers['From'],'<'),$log); + foreach ($fw->split($headers['To']. + (isset($headers['Cc'])?(';'.$headers['Cc']):''). + (isset($headers['Bcc'])?(';'.$headers['Bcc']):'')) as $dst) + $this->dialog('RCPT TO: '.strstr($dst,'<'),$log); + $this->dialog('DATA',$log); + if ($this->attachments) { + // Replace Content-Type + $hash=uniqid(NULL,TRUE); + $type=$headers['Content-Type']; + $headers['Content-Type']='multipart/mixed; '. + 'boundary="'.$hash.'"'; + // Send mail headers + $out=''; + foreach ($headers as $key=>$val) + if ($key!='Bcc') + $out.=$key.': '.$val.$eol; + $out.=$eol; + $out.='This is a multi-part message in MIME format'.$eol; + $out.=$eol; + $out.='--'.$hash.$eol; + $out.='Content-Type: '.$type.$eol; + $out.=$eol; + $out.=$message.$eol; + foreach ($this->attachments as $attachment) { + $out.='--'.$hash.$eol; + $out.='Content-Type: application/octet-stream'.$eol; + $out.='Content-Transfer-Encoding: base64'.$eol; + $out.='Content-Disposition: attachment; '. + 'filename="'.basename($attachment).'"'.$eol; + $out.=$eol; + $out.=chunk_split( + base64_encode(file_get_contents($attachment))).$eol; + } + $out.=$eol; + $out.='--'.$hash.'--'.$eol; + $out.='.'; + $this->dialog($out,FALSE); + } + else { + // Send mail headers + $out=''; + foreach ($headers as $key=>$val) + if ($key!='Bcc') + $out.=$key.': '.$val.$eol; + $out.=$eol; + $out.=$message.$eol; + $out.='.'; + // Send message + $this->dialog($out); + } + $this->dialog('QUIT',$log); + if ($socket) + fclose($socket); + return TRUE; + } + + /** + * Instantiate class + * @param $host string + * @param $port int + * @param $scheme string + * @param $user string + * @param $pw string + **/ + function __construct($host,$port,$scheme,$user,$pw) { + $this->headers=array( + 'MIME-Version'=>'1.0', + 'Content-Type'=>'text/plain; '. + 'charset='.Base::instance()->get('ENCODING') + ); + $this->host=$host; + if (strtolower($this->scheme=strtolower($scheme))=='ssl') + $this->host='ssl://'.$host; + $this->port=$port; + $this->user=$user; + $this->pw=$pw; + } + +} |