123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- <?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;
- }
- }
|