Changeset 27385 for trunk/src/wp-includes/class-smtp.php
- Timestamp:
- 03/03/2014 08:24:31 PM (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-smtp.php
r23522 r27385 1 1 <?php 2 /*~ class.smtp.php3 .---------------------------------------------------------------------------.4 | Software: PHPMailer - PHP email class |5 | Version: 5.2.4 |6 | Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ |7 | ------------------------------------------------------------------------- |8 | Admin: Jim Jagielski (project admininistrator) |9 | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |10 | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net |11 | : Jim Jagielski (jimjag) jimjag@gmail.com |12 | Founder: Brent R. Matzelle (original founder) |13 | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. |14 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. |15 | Copyright (c) 2001-2003, Brent R. Matzelle |16 | ------------------------------------------------------------------------- |17 | License: Distributed under the Lesser General Public License (LGPL) |18 | http://www.gnu.org/copyleft/lesser.html |19 | This program is distributed in the hope that it will be useful - WITHOUT |20 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |21 | FITNESS FOR A PARTICULAR PURPOSE. |22 '---------------------------------------------------------------------------'23 */24 25 2 /** 26 * PHPMailer - PHP SMTP email transport class 27 * NOTE: Designed for use with PHP version 5 and up 28 * @package PHPMailer 29 * @author Andy Prevost 30 * @author Marcus Bointon 3 * PHPMailer RFC821 SMTP email transport class. 4 * Version 5.2.7 5 * PHP version 5.0.0 6 * @category PHP 7 * @package PHPMailer 8 * @link https://github.com/PHPMailer/PHPMailer/ 9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> 10 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 11 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 12 * @copyright 2013 Marcus Bointon 31 13 * @copyright 2004 - 2008 Andy Prevost 32 * @author Jim Jagielski33 14 * @copyright 2010 - 2012 Jim Jagielski 34 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)15 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 35 16 */ 36 17 37 18 /** 38 * PHP RFC821 SMTP client19 * PHPMailer RFC821 SMTP email transport class. 39 20 * 40 * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error. 41 * SMTP also provides some utility methods for sending mail to an SMTP server. 42 * @author Chris Ryan 43 * @package PHPMailer 21 * Implements RFC 821 SMTP commands 22 * and provides some utility methods for sending mail to an SMTP server. 23 * 24 * PHP Version 5.0.0 25 * 26 * @category PHP 27 * @package PHPMailer 28 * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php 29 * @author Chris Ryan <unknown@example.com> 30 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 31 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 44 32 */ 45 33 46 class SMTP { 47 /** 48 * SMTP server port 49 * @var int 50 */ 51 public $SMTP_PORT = 25; 52 53 /** 54 * SMTP reply line ending (don't change) 55 * @var string 56 */ 57 public $CRLF = "\r\n"; 58 59 /** 60 * Sets whether debugging is turned on 61 * @var bool 62 */ 63 public $do_debug; // the level of debug to perform 64 65 /** 66 * Sets the function/method to use for debugging output. 67 * Right now we only honor "echo" or "error_log" 68 * @var string 69 */ 70 public $Debugoutput = "echo"; 71 72 /** 73 * Sets VERP use on/off (default is off) 74 * @var bool 75 */ 76 public $do_verp = false; 77 78 /** 79 * Sets the SMTP timeout value for reads, in seconds 80 * @var int 81 */ 82 public $Timeout = 15; 83 84 /** 85 * Sets the SMTP timelimit value for reads, in seconds 86 * @var int 87 */ 88 public $Timelimit = 30; 89 90 /** 91 * Sets the SMTP PHPMailer Version number 92 * @var string 93 */ 94 public $Version = '5.2.4'; 95 96 ///////////////////////////////////////////////// 97 // PROPERTIES, PRIVATE AND PROTECTED 98 ///////////////////////////////////////////////// 99 100 /** 101 * @var resource The socket to the server 102 */ 103 private $smtp_conn; 104 /** 105 * @var string Error message, if any, for the last call 106 */ 107 private $error; 108 /** 109 * @var string The reply the server sent to us for HELO 110 */ 111 private $helo_rply; 112 113 /** 114 * Outputs debugging info via user-defined method 115 * @param string $str 116 */ 117 private function edebug($str) { 118 if ($this->Debugoutput == "error_log") { 119 error_log($str); 120 } else { 121 echo $str; 122 } 123 } 124 125 /** 126 * Initialize the class so that the data is in a known state. 127 * @access public 128 * @return SMTP 129 */ 130 public function __construct() { 131 $this->smtp_conn = 0; 132 $this->error = null; 133 $this->helo_rply = null; 134 135 $this->do_debug = 0; 136 } 137 138 ///////////////////////////////////////////////// 139 // CONNECTION FUNCTIONS 140 ///////////////////////////////////////////////// 141 142 /** 143 * Connect to the server specified on the port specified. 144 * If the port is not specified use the default SMTP_PORT. 145 * If tval is specified then a connection will try and be 146 * established with the server for that number of seconds. 147 * If tval is not specified the default is 30 seconds to 148 * try on the connection. 149 * 150 * SMTP CODE SUCCESS: 220 151 * SMTP CODE FAILURE: 421 152 * @access public 153 * @param string $host 154 * @param int $port 155 * @param int $tval 156 * @return bool 157 */ 158 public function Connect($host, $port = 0, $tval = 30) { 159 // set the error val to null so there is no confusion 160 $this->error = null; 161 162 // make sure we are __not__ connected 163 if($this->connected()) { 164 // already connected, generate error 165 $this->error = array("error" => "Already connected to a server"); 166 return false; 167 } 168 169 if(empty($port)) { 170 $port = $this->SMTP_PORT; 171 } 172 173 // connect to the smtp server 174 $this->smtp_conn = @fsockopen($host, // the host of the server 175 $port, // the port to use 176 $errno, // error number if any 177 $errstr, // error message if any 178 $tval); // give up after ? secs 179 // verify we connected properly 180 if(empty($this->smtp_conn)) { 181 $this->error = array("error" => "Failed to connect to server", 182 "errno" => $errno, 183 "errstr" => $errstr); 184 if($this->do_debug >= 1) { 185 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />'); 186 } 187 return false; 188 } 189 190 // SMTP server can take longer to respond, give longer timeout for first read 191 // Windows does not have support for this timeout function 192 if(substr(PHP_OS, 0, 3) != "WIN") { 193 $max = ini_get('max_execution_time'); 194 if ($max != 0 && $tval > $max) { // don't bother if unlimited 195 @set_time_limit($tval); 196 } 197 stream_set_timeout($this->smtp_conn, $tval, 0); 198 } 199 200 // get any announcement 201 $announce = $this->get_lines(); 202 203 if($this->do_debug >= 2) { 204 $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />'); 205 } 206 207 return true; 208 } 209 210 /** 211 * Initiate a TLS communication with the server. 212 * 213 * SMTP CODE 220 Ready to start TLS 214 * SMTP CODE 501 Syntax error (no parameters allowed) 215 * SMTP CODE 454 TLS not available due to temporary reason 216 * @access public 217 * @return bool success 218 */ 219 public function StartTLS() { 220 $this->error = null; # to avoid confusion 221 222 if(!$this->connected()) { 223 $this->error = array("error" => "Called StartTLS() without being connected"); 224 return false; 225 } 226 227 fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); 228 229 $rply = $this->get_lines(); 230 $code = substr($rply,0,3); 231 232 if($this->do_debug >= 2) { 233 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 234 } 235 236 if($code != 220) { 237 $this->error = 238 array("error" => "STARTTLS not accepted from server", 239 "smtp_code" => $code, 240 "smtp_msg" => substr($rply,4)); 241 if($this->do_debug >= 1) { 242 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 243 } 244 return false; 245 } 246 247 // Begin encrypted connection 248 if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { 249 return false; 250 } 251 252 return true; 253 } 254 255 /** 256 * Performs SMTP authentication. Must be run after running the 257 * Hello() method. Returns true if successfully authenticated. 258 * @access public 259 * @param string $username 260 * @param string $password 261 * @param string $authtype 262 * @param string $realm 263 * @param string $workstation 264 * @return bool 265 */ 266 public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') { 267 if (empty($authtype)) { 268 $authtype = 'LOGIN'; 269 } 270 271 switch ($authtype) { 272 case 'PLAIN': 273 // Start authentication 274 fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF); 275 276 $rply = $this->get_lines(); 277 $code = substr($rply,0,3); 278 279 if($code != 334) { 280 $this->error = 281 array("error" => "AUTH not accepted from server", 282 "smtp_code" => $code, 283 "smtp_msg" => substr($rply,4)); 284 if($this->do_debug >= 1) { 285 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 286 } 287 return false; 288 } 289 // Send encoded username and password 290 fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF); 291 292 $rply = $this->get_lines(); 293 $code = substr($rply,0,3); 294 295 if($code != 235) { 296 $this->error = 297 array("error" => "Authentication not accepted from server", 298 "smtp_code" => $code, 299 "smtp_msg" => substr($rply,4)); 300 if($this->do_debug >= 1) { 301 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 302 } 303 return false; 304 } 305 break; 306 case 'LOGIN': 307 // Start authentication 308 fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); 309 310 $rply = $this->get_lines(); 311 $code = substr($rply,0,3); 312 313 if($code != 334) { 314 $this->error = 315 array("error" => "AUTH not accepted from server", 316 "smtp_code" => $code, 317 "smtp_msg" => substr($rply,4)); 318 if($this->do_debug >= 1) { 319 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 320 } 321 return false; 322 } 323 324 // Send encoded username 325 fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); 326 327 $rply = $this->get_lines(); 328 $code = substr($rply,0,3); 329 330 if($code != 334) { 331 $this->error = 332 array("error" => "Username not accepted from server", 333 "smtp_code" => $code, 334 "smtp_msg" => substr($rply,4)); 335 if($this->do_debug >= 1) { 336 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 337 } 338 return false; 339 } 340 341 // Send encoded password 342 fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); 343 344 $rply = $this->get_lines(); 345 $code = substr($rply,0,3); 346 347 if($code != 235) { 348 $this->error = 349 array("error" => "Password not accepted from server", 350 "smtp_code" => $code, 351 "smtp_msg" => substr($rply,4)); 352 if($this->do_debug >= 1) { 353 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 354 } 355 return false; 356 } 357 break; 358 case 'NTLM': 359 /* 360 * ntlm_sasl_client.php 361 ** Bundled with Permission 362 ** 363 ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 364 ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 34 class SMTP 35 { 36 /** 37 * The PHPMailer SMTP Version number. 38 */ 39 const VERSION = '5.2.7'; 40 41 /** 42 * SMTP line break constant. 43 */ 44 const CRLF = "\r\n"; 45 46 /** 47 * The SMTP port to use if one is not specified. 48 */ 49 const DEFAULT_SMTP_PORT = 25; 50 51 /** 52 * The PHPMailer SMTP Version number. 53 * @type string 54 * @deprecated This should be a constant 55 * @see SMTP::VERSION 56 */ 57 public $Version = '5.2.7'; 58 59 /** 60 * SMTP server port number. 61 * @type int 62 * @deprecated This is only ever ued as default value, so should be a constant 63 * @see SMTP::DEFAULT_SMTP_PORT 64 */ 65 public $SMTP_PORT = 25; 66 67 /** 68 * SMTP reply line ending 69 * @type string 70 * @deprecated Use the class constant instead 71 * @see SMTP::CRLF 72 */ 73 public $CRLF = "\r\n"; 74 75 /** 76 * Debug output level. 77 * Options: 0 for no output, 1 for commands, 2 for data and commands 78 * @type int 79 */ 80 public $do_debug = 0; 81 82 /** 83 * The function/method to use for debugging output. 84 * Options: 'echo', 'html' or 'error_log' 85 * @type string 86 */ 87 public $Debugoutput = 'echo'; 88 89 /** 90 * Whether to use VERP. 91 * @type bool 92 */ 93 public $do_verp = false; 94 95 /** 96 * The SMTP timeout value for reads, in seconds. 97 * @type int 98 */ 99 public $Timeout = 15; 100 101 /** 102 * The SMTP timelimit value for reads, in seconds. 103 * @type int 104 */ 105 public $Timelimit = 30; 106 107 /** 108 * The socket for the server connection. 109 * @type resource 110 */ 111 protected $smtp_conn; 112 113 /** 114 * Error message, if any, for the last call. 115 * @type string 116 */ 117 protected $error = ''; 118 119 /** 120 * The reply the server sent to us for HELO. 121 * @type string 122 */ 123 protected $helo_rply = ''; 124 125 /** 126 * The most recent reply received from the server. 127 * @type string 128 */ 129 protected $last_reply = ''; 130 131 /** 132 * Constructor. 133 * @access public 134 */ 135 public function __construct() 136 { 137 $this->smtp_conn = 0; 138 $this->error = null; 139 $this->helo_rply = null; 140 141 $this->do_debug = 0; 142 } 143 144 /** 145 * Output debugging info via a user-selected method. 146 * @param string $str Debug string to output 147 * @return void 148 */ 149 protected function edebug($str) 150 { 151 switch ($this->Debugoutput) { 152 case 'error_log': 153 //Don't output, just log 154 error_log($str); 155 break; 156 case 'html': 157 //Cleans up output a bit for a better looking, HTML-safe output 158 echo htmlentities( 159 preg_replace('/[\r\n]+/', '', $str), 160 ENT_QUOTES, 161 'UTF-8' 162 ) 163 . "<br>\n"; 164 break; 165 case 'echo': 166 default: 167 //Just echoes whatever was received 168 echo $str; 169 } 170 } 171 172 /** 173 * Connect to an SMTP server. 174 * @param string $host SMTP server IP or host name 175 * @param int $port The port number to connect to 176 * @param int $timeout How long to wait for the connection to open 177 * @param array $options An array of options for stream_context_create() 178 * @access public 179 * @return bool 180 */ 181 public function connect($host, $port = null, $timeout = 30, $options = array()) 182 { 183 // Clear errors to avoid confusion 184 $this->error = null; 185 186 // Make sure we are __not__ connected 187 if ($this->connected()) { 188 // Already connected, generate error 189 $this->error = array('error' => 'Already connected to a server'); 190 return false; 191 } 192 193 if (empty($port)) { 194 $port = self::DEFAULT_SMTP_PORT; 195 } 196 197 // Connect to the SMTP server 198 $errno = 0; 199 $errstr = ''; 200 $socket_context = stream_context_create($options); 201 //Suppress errors; connection failures are handled at a higher level 202 $this->smtp_conn = @stream_socket_client( 203 $host . ":" . $port, 204 $errno, 205 $errstr, 206 $timeout, 207 STREAM_CLIENT_CONNECT, 208 $socket_context 209 ); 210 211 // Verify we connected properly 212 if (empty($this->smtp_conn)) { 213 $this->error = array( 214 'error' => 'Failed to connect to server', 215 'errno' => $errno, 216 'errstr' => $errstr 217 ); 218 if ($this->do_debug >= 1) { 219 $this->edebug( 220 'SMTP -> ERROR: ' . $this->error['error'] 221 . ": $errstr ($errno)" 222 ); 223 } 224 return false; 225 } 226 227 // SMTP server can take longer to respond, give longer timeout for first read 228 // Windows does not have support for this timeout function 229 if (substr(PHP_OS, 0, 3) != 'WIN') { 230 $max = ini_get('max_execution_time'); 231 if ($max != 0 && $timeout > $max) { // Don't bother if unlimited 232 @set_time_limit($timeout); 233 } 234 stream_set_timeout($this->smtp_conn, $timeout, 0); 235 } 236 237 // Get any announcement 238 $announce = $this->get_lines(); 239 240 if ($this->do_debug >= 2) { 241 $this->edebug('SMTP -> FROM SERVER:' . $announce); 242 } 243 244 return true; 245 } 246 247 /** 248 * Initiate a TLS (encrypted) session. 249 * @access public 250 * @return bool 251 */ 252 public function startTLS() 253 { 254 if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) { 255 return false; 256 } 257 // Begin encrypted connection 258 if (!stream_socket_enable_crypto( 259 $this->smtp_conn, 260 true, 261 STREAM_CRYPTO_METHOD_TLS_CLIENT 262 ) 263 ) { 264 return false; 265 } 266 return true; 267 } 268 269 /** 270 * Perform SMTP authentication. 271 * Must be run after hello(). 272 * @see hello() 273 * @param string $username The user name 274 * @param string $password The password 275 * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5) 276 * @param string $realm The auth realm for NTLM 277 * @param string $workstation The auth workstation for NTLM 278 * @access public 279 * @return bool True if successfully authenticated. 280 */ 281 public function authenticate( 282 $username, 283 $password, 284 $authtype = 'LOGIN', 285 $realm = '', 286 $workstation = '' 287 ) { 288 if (empty($authtype)) { 289 $authtype = 'LOGIN'; 290 } 291 292 switch ($authtype) { 293 case 'PLAIN': 294 // Start authentication 295 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { 296 return false; 297 } 298 // Send encoded username and password 299 if (!$this->sendCommand( 300 'User & Password', 301 base64_encode("\0" . $username . "\0" . $password), 302 235 303 ) 304 ) { 305 return false; 306 } 307 break; 308 case 'LOGIN': 309 // Start authentication 310 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { 311 return false; 312 } 313 if (!$this->sendCommand("Username", base64_encode($username), 334)) { 314 return false; 315 } 316 if (!$this->sendCommand("Password", base64_encode($password), 235)) { 317 return false; 318 } 319 break; 320 case 'NTLM': 321 /* 322 * ntlm_sasl_client.php 323 * Bundled with Permission 324 * 325 * How to telnet in windows: 326 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 327 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 328 */ 329 require_once 'extras/ntlm_sasl_client.php'; 330 $temp = new stdClass(); 331 $ntlm_client = new ntlm_sasl_client_class; 332 //Check that functions are available 333 if (!$ntlm_client->Initialize($temp)) { 334 $this->error = array('error' => $temp->error); 335 if ($this->do_debug >= 1) { 336 $this->edebug( 337 'You need to enable some modules in your php.ini file: ' 338 . $this->error['error'] 339 ); 340 } 341 return false; 342 } 343 //msg1 344 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1 345 346 if (!$this->sendCommand( 347 'AUTH NTLM', 348 'AUTH NTLM ' . base64_encode($msg1), 349 334 350 ) 351 ) { 352 return false; 353 } 354 355 //Though 0 based, there is a white space after the 3 digit number 356 //msg2 357 $challenge = substr($this->last_reply, 3); 358 $challenge = base64_decode($challenge); 359 $ntlm_res = $ntlm_client->NTLMResponse( 360 substr($challenge, 24, 8), 361 $password 362 ); 363 //msg3 364 $msg3 = $ntlm_client->TypeMsg3( 365 $ntlm_res, 366 $username, 367 $realm, 368 $workstation 369 ); 370 // send encoded username 371 return $this->sendCommand('Username', base64_encode($msg3), 235); 372 break; 373 case 'CRAM-MD5': 374 // Start authentication 375 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { 376 return false; 377 } 378 // Get the challenge 379 $challenge = base64_decode(substr($this->last_reply, 4)); 380 381 // Build the response 382 $response = $username . ' ' . $this->hmac($challenge, $password); 383 384 // send encoded credentials 385 return $this->sendCommand('Username', base64_encode($response), 235); 386 break; 387 } 388 return true; 389 } 390 391 /** 392 * Calculate an MD5 HMAC hash. 393 * Works like hash_hmac('md5', $data, $key) 394 * in case that function is not available 395 * @param string $data The data to hash 396 * @param string $key The key to hash with 397 * @access protected 398 * @return string 399 */ 400 protected function hmac($data, $key) 401 { 402 if (function_exists('hash_hmac')) { 403 return hash_hmac('md5', $data, $key); 404 } 405 406 // The following borrowed from 407 // http://php.net/manual/en/function.mhash.php#27225 408 409 // RFC 2104 HMAC implementation for php. 410 // Creates an md5 HMAC. 411 // Eliminates the need to install mhash to compute a HMAC 412 // Hacked by Lance Rushing 413 414 $b = 64; // byte length for md5 415 if (strlen($key) > $b) { 416 $key = pack('H*', md5($key)); 417 } 418 $key = str_pad($key, $b, chr(0x00)); 419 $ipad = str_pad('', $b, chr(0x36)); 420 $opad = str_pad('', $b, chr(0x5c)); 421 $k_ipad = $key ^ $ipad; 422 $k_opad = $key ^ $opad; 423 424 return md5($k_opad . pack('H*', md5($k_ipad . $data))); 425 } 426 427 /** 428 * Check connection state. 429 * @access public 430 * @return bool True if connected. 431 */ 432 public function connected() 433 { 434 if (!empty($this->smtp_conn)) { 435 $sock_status = stream_get_meta_data($this->smtp_conn); 436 if ($sock_status['eof']) { 437 // the socket is valid but we are not connected 438 if ($this->do_debug >= 1) { 439 $this->edebug( 440 'SMTP -> NOTICE: EOF caught while checking if connected' 441 ); 442 } 443 $this->close(); 444 return false; 445 } 446 return true; // everything looks good 447 } 448 return false; 449 } 450 451 /** 452 * Close the socket and clean up the state of the class. 453 * Don't use this function without first trying to use QUIT. 454 * @see quit() 455 * @access public 456 * @return void 457 */ 458 public function close() 459 { 460 $this->error = null; // so there is no confusion 461 $this->helo_rply = null; 462 if (!empty($this->smtp_conn)) { 463 // close the connection and cleanup 464 fclose($this->smtp_conn); 465 $this->smtp_conn = 0; 466 } 467 } 468 469 /** 470 * Send an SMTP DATA command. 471 * Issues a data command and sends the msg_data to the server, 472 * finializing the mail transaction. $msg_data is the message 473 * that is to be send with the headers. Each header needs to be 474 * on a single line followed by a <CRLF> with the message headers 475 * and the message body being separated by and additional <CRLF>. 476 * Implements rfc 821: DATA <CRLF> 477 * @param string $msg_data Message data to send 478 * @access public 479 * @return bool 480 */ 481 public function data($msg_data) 482 { 483 if (!$this->sendCommand('DATA', 'DATA', 354)) { 484 return false; 485 } 486 487 /* The server is ready to accept data! 488 * according to rfc821 we should not send more than 1000 489 * including the CRLF 490 * characters on a single line so we will break the data up 491 * into lines by \r and/or \n then if needed we will break 492 * each of those into smaller lines to fit within the limit. 493 * in addition we will be looking for lines that start with 494 * a period '.' and append and additional period '.' to that 495 * line. NOTE: this does not count towards limit. 365 496 */ 366 require_once('ntlm_sasl_client.php'); 367 $temp = new stdClass(); 368 $ntlm_client = new ntlm_sasl_client_class; 369 if(! $ntlm_client->Initialize($temp)){//let's test if every function its available 370 $this->error = array("error" => $temp->error); 371 if($this->do_debug >= 1) { 372 $this->edebug("You need to enable some modules in your php.ini file: " . $this->error["error"] . $this->CRLF); 373 } 497 498 // Normalize the line breaks before exploding 499 $msg_data = str_replace("\r\n", "\n", $msg_data); 500 $msg_data = str_replace("\r", "\n", $msg_data); 501 $lines = explode("\n", $msg_data); 502 503 /* We need to find a good way to determine if headers are 504 * in the msg_data or if it is a straight msg body 505 * currently I am assuming rfc822 definitions of msg headers 506 * and if the first field of the first line (':' separated) 507 * does not contain a space then it _should_ be a header 508 * and we can process all lines before a blank "" line as 509 * headers. 510 */ 511 512 $field = substr($lines[0], 0, strpos($lines[0], ':')); 513 $in_headers = false; 514 if (!empty($field) && !strstr($field, ' ')) { 515 $in_headers = true; 516 } 517 518 //RFC 2822 section 2.1.1 limit 519 $max_line_length = 998; 520 521 foreach ($lines as $line) { 522 $lines_out = null; 523 if ($line == '' && $in_headers) { 524 $in_headers = false; 525 } 526 // ok we need to break this line up into several smaller lines 527 while (strlen($line) > $max_line_length) { 528 $pos = strrpos(substr($line, 0, $max_line_length), ' '); 529 530 // Patch to fix DOS attack 531 if (!$pos) { 532 $pos = $max_line_length - 1; 533 $lines_out[] = substr($line, 0, $pos); 534 $line = substr($line, $pos); 535 } else { 536 $lines_out[] = substr($line, 0, $pos); 537 $line = substr($line, $pos + 1); 538 } 539 540 /* If processing headers add a LWSP-char to the front of new line 541 * rfc822 on long msg headers 542 */ 543 if ($in_headers) { 544 $line = "\t" . $line; 545 } 546 } 547 $lines_out[] = $line; 548 549 // send the lines to the server 550 while (list(, $line_out) = @each($lines_out)) { 551 if (strlen($line_out) > 0) { 552 if (substr($line_out, 0, 1) == '.') { 553 $line_out = '.' . $line_out; 554 } 555 } 556 $this->client_send($line_out . self::CRLF); 557 } 558 } 559 560 // Message data has been sent, complete the command 561 return $this->sendCommand('DATA END', '.', 250); 562 } 563 564 /** 565 * Send an SMTP HELO or EHLO command. 566 * Used to identify the sending server to the receiving server. 567 * This makes sure that client and server are in a known state. 568 * Implements from RFC 821: HELO <SP> <domain> <CRLF> 569 * and RFC 2821 EHLO. 570 * @param string $host The host name or IP to connect to 571 * @access public 572 * @return bool 573 */ 574 public function hello($host = '') 575 { 576 // Try extended hello first (RFC 2821) 577 if (!$this->sendHello('EHLO', $host)) { 578 if (!$this->sendHello('HELO', $host)) { 579 return false; 580 } 581 } 582 583 return true; 584 } 585 586 /** 587 * Send an SMTP HELO or EHLO command. 588 * Low-level implementation used by hello() 589 * @see hello() 590 * @param string $hello The HELO string 591 * @param string $host The hostname to say we are 592 * @access protected 593 * @return bool 594 */ 595 protected function sendHello($hello, $host) 596 { 597 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); 598 $this->helo_rply = $this->last_reply; 599 return $noerror; 600 } 601 602 /** 603 * Send an SMTP MAIL command. 604 * Starts a mail transaction from the email address specified in 605 * $from. Returns true if successful or false otherwise. If True 606 * the mail transaction is started and then one or more recipient 607 * commands may be called followed by a data command. 608 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 609 * @param string $from Source address of this message 610 * @access public 611 * @return bool 612 */ 613 public function mail($from) 614 { 615 $useVerp = ($this->do_verp ? ' XVERP' : ''); 616 return $this->sendCommand( 617 'MAIL FROM', 618 'MAIL FROM:<' . $from . '>' . $useVerp, 619 250 620 ); 621 } 622 623 /** 624 * Send an SMTP QUIT command. 625 * Closes the socket if there is no error or the $close_on_error argument is true. 626 * Implements from rfc 821: QUIT <CRLF> 627 * @param bool $close_on_error Should the connection close if an error occurs? 628 * @access public 629 * @return bool 630 */ 631 public function quit($close_on_error = true) 632 { 633 $noerror = $this->sendCommand('QUIT', 'QUIT', 221); 634 $e = $this->error; //Save any error 635 if ($noerror or $close_on_error) { 636 $this->close(); 637 $this->error = $e; //Restore any error from the quit command 638 } 639 return $noerror; 640 } 641 642 /** 643 * Send an SMTP RCPT command. 644 * Sets the TO argument to $to. 645 * Returns true if the recipient was accepted false if it was rejected. 646 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 647 * @param string $to The address the message is being sent to 648 * @access public 649 * @return bool 650 */ 651 public function recipient($to) 652 { 653 return $this->sendCommand( 654 'RCPT TO ', 655 'RCPT TO:<' . $to . '>', 656 array(250, 251) 657 ); 658 } 659 660 /** 661 * Send an SMTP RSET command. 662 * Abort any transaction that is currently in progress. 663 * Implements rfc 821: RSET <CRLF> 664 * @access public 665 * @return bool True on success. 666 */ 667 public function reset() 668 { 669 return $this->sendCommand('RSET', 'RSET', 250); 670 } 671 672 /** 673 * Send a command to an SMTP server and check its return code. 674 * @param string $command The command name - not sent to the server 675 * @param string $commandstring The actual command to send 676 * @param int|array $expect One or more expected integer success codes 677 * @access protected 678 * @return bool True on success. 679 */ 680 protected function sendCommand($command, $commandstring, $expect) 681 { 682 if (!$this->connected()) { 683 $this->error = array( 684 "error" => "Called $command without being connected" 685 ); 374 686 return false; 375 687 } 376 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1 377 378 fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF); 379 380 $rply = $this->get_lines(); 381 $code = substr($rply,0,3); 382 383 384 if($code != 334) { 385 $this->error = 386 array("error" => "AUTH not accepted from server", 387 "smtp_code" => $code, 388 "smtp_msg" => substr($rply,4)); 389 if($this->do_debug >= 1) { 390 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); 688 $this->client_send($commandstring . self::CRLF); 689 690 $reply = $this->get_lines(); 691 $code = substr($reply, 0, 3); 692 693 if ($this->do_debug >= 2) { 694 $this->edebug('SMTP -> FROM SERVER:' . $reply); 695 } 696 697 if (!in_array($code, (array)$expect)) { 698 $this->last_reply = null; 699 $this->error = array( 700 "error" => "$command command failed", 701 "smtp_code" => $code, 702 "detail" => substr($reply, 4) 703 ); 704 if ($this->do_debug >= 1) { 705 $this->edebug( 706 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply 707 ); 391 708 } 392 709 return false; 393 710 } 394 395 $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2 396 $challange = base64_decode($challange); 397 $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password); 398 $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3 399 // Send encoded username 400 fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF); 401 402 $rply = $this->get_lines(); 403 $code = substr($rply,0,3); 404 405 if($code != 235) { 406 $this->error = 407 array("error" => "Could not authenticate", 408 "smtp_code" => $code, 409 "smtp_msg" => substr($rply,4)); 410 if($this->do_debug >= 1) { 411 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); 412 } 413 return false; 414 } 415 break; 416 } 417 return true; 418 } 419 420 /** 421 * Returns true if connected to a server otherwise false 422 * @access public 423 * @return bool 424 */ 425 public function Connected() { 426 if(!empty($this->smtp_conn)) { 427 $sock_status = socket_get_status($this->smtp_conn); 428 if($sock_status["eof"]) { 429 // the socket is valid but we are not connected 430 if($this->do_debug >= 1) { 431 $this->edebug("SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"); 432 } 433 $this->Close(); 711 712 $this->last_reply = $reply; 713 $this->error = null; 714 return true; 715 } 716 717 /** 718 * Send an SMTP SAML command. 719 * Starts a mail transaction from the email address specified in $from. 720 * Returns true if successful or false otherwise. If True 721 * the mail transaction is started and then one or more recipient 722 * commands may be called followed by a data command. This command 723 * will send the message to the users terminal if they are logged 724 * in and send them an email. 725 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 726 * @param string $from The address the message is from 727 * @access public 728 * @return bool 729 */ 730 public function sendAndMail($from) 731 { 732 return $this->sendCommand("SAML", "SAML FROM:$from", 250); 733 } 734 735 /** 736 * Send an SMTP VRFY command. 737 * @param string $name The name to verify 738 * @access public 739 * @return bool 740 */ 741 public function verify($name) 742 { 743 return $this->sendCommand("VRFY", "VRFY $name", array(250, 251)); 744 } 745 746 /** 747 * Send an SMTP NOOP command. 748 * Used to keep keep-alives alive, doesn't actually do anything 749 * @access public 750 * @return bool 751 */ 752 public function noop() 753 { 754 return $this->sendCommand("NOOP", "NOOP", 250); 755 } 756 757 /** 758 * Send an SMTP TURN command. 759 * This is an optional command for SMTP that this class does not support. 760 * This method is here to make the RFC821 Definition 761 * complete for this class and __may__ be implemented in future 762 * Implements from rfc 821: TURN <CRLF> 763 * @access public 764 * @return bool 765 */ 766 public function turn() 767 { 768 $this->error = array( 769 'error' => 'The SMTP TURN command is not implemented' 770 ); 771 if ($this->do_debug >= 1) { 772 $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); 773 } 434 774 return false; 435 } 436 return true; // everything looks good 437 } 438 return false; 439 } 440 441 /** 442 * Closes the socket and cleans up the state of the class. 443 * It is not considered good to use this function without 444 * first trying to use QUIT. 445 * @access public 446 * @return void 447 */ 448 public function Close() { 449 $this->error = null; // so there is no confusion 450 $this->helo_rply = null; 451 if(!empty($this->smtp_conn)) { 452 // close the connection and cleanup 453 fclose($this->smtp_conn); 454 $this->smtp_conn = 0; 455 } 456 } 457 458 ///////////////////////////////////////////////// 459 // SMTP COMMANDS 460 ///////////////////////////////////////////////// 461 462 /** 463 * Issues a data command and sends the msg_data to the server 464 * finializing the mail transaction. $msg_data is the message 465 * that is to be send with the headers. Each header needs to be 466 * on a single line followed by a <CRLF> with the message headers 467 * and the message body being seperated by and additional <CRLF>. 468 * 469 * Implements rfc 821: DATA <CRLF> 470 * 471 * SMTP CODE INTERMEDIATE: 354 472 * [data] 473 * <CRLF>.<CRLF> 474 * SMTP CODE SUCCESS: 250 475 * SMTP CODE FAILURE: 552,554,451,452 476 * SMTP CODE FAILURE: 451,554 477 * SMTP CODE ERROR : 500,501,503,421 478 * @access public 479 * @param string $msg_data 480 * @return bool 481 */ 482 public function Data($msg_data) { 483 $this->error = null; // so no confusion is caused 484 485 if(!$this->connected()) { 486 $this->error = array( 487 "error" => "Called Data() without being connected"); 488 return false; 489 } 490 491 fputs($this->smtp_conn,"DATA" . $this->CRLF); 492 493 $rply = $this->get_lines(); 494 $code = substr($rply,0,3); 495 496 if($this->do_debug >= 2) { 497 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 498 } 499 500 if($code != 354) { 501 $this->error = 502 array("error" => "DATA command not accepted from server", 503 "smtp_code" => $code, 504 "smtp_msg" => substr($rply,4)); 505 if($this->do_debug >= 1) { 506 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 507 } 508 return false; 509 } 510 511 /* the server is ready to accept data! 512 * according to rfc 821 we should not send more than 1000 513 * including the CRLF 514 * characters on a single line so we will break the data up 515 * into lines by \r and/or \n then if needed we will break 516 * each of those into smaller lines to fit within the limit. 517 * in addition we will be looking for lines that start with 518 * a period '.' and append and additional period '.' to that 519 * line. NOTE: this does not count towards limit. 520 */ 521 522 // normalize the line breaks so we know the explode works 523 $msg_data = str_replace("\r\n","\n",$msg_data); 524 $msg_data = str_replace("\r","\n",$msg_data); 525 $lines = explode("\n",$msg_data); 526 527 /* we need to find a good way to determine is headers are 528 * in the msg_data or if it is a straight msg body 529 * currently I am assuming rfc 822 definitions of msg headers 530 * and if the first field of the first line (':' sperated) 531 * does not contain a space then it _should_ be a header 532 * and we can process all lines before a blank "" line as 533 * headers. 534 */ 535 536 $field = substr($lines[0],0,strpos($lines[0],":")); 537 $in_headers = false; 538 if(!empty($field) && !strstr($field," ")) { 539 $in_headers = true; 540 } 541 542 $max_line_length = 998; // used below; set here for ease in change 543 544 while(list(,$line) = @each($lines)) { 545 $lines_out = null; 546 if($line == "" && $in_headers) { 547 $in_headers = false; 548 } 549 // ok we need to break this line up into several smaller lines 550 while(strlen($line) > $max_line_length) { 551 $pos = strrpos(substr($line,0,$max_line_length)," "); 552 553 // Patch to fix DOS attack 554 if(!$pos) { 555 $pos = $max_line_length - 1; 556 $lines_out[] = substr($line,0,$pos); 557 $line = substr($line,$pos); 558 } else { 559 $lines_out[] = substr($line,0,$pos); 560 $line = substr($line,$pos + 1); 561 } 562 563 /* if processing headers add a LWSP-char to the front of new line 564 * rfc 822 on long msg headers 565 */ 566 if($in_headers) { 567 $line = "\t" . $line; 568 } 569 } 570 $lines_out[] = $line; 571 572 // send the lines to the server 573 while(list(,$line_out) = @each($lines_out)) { 574 if(strlen($line_out) > 0) 575 { 576 if(substr($line_out, 0, 1) == ".") { 577 $line_out = "." . $line_out; 578 } 579 } 580 fputs($this->smtp_conn,$line_out . $this->CRLF); 581 } 582 } 583 584 // message data has been sent 585 fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); 586 587 $rply = $this->get_lines(); 588 $code = substr($rply,0,3); 589 590 if($this->do_debug >= 2) { 591 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 592 } 593 594 if($code != 250) { 595 $this->error = 596 array("error" => "DATA not accepted from server", 597 "smtp_code" => $code, 598 "smtp_msg" => substr($rply,4)); 599 if($this->do_debug >= 1) { 600 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 601 } 602 return false; 603 } 604 return true; 605 } 606 607 /** 608 * Sends the HELO command to the smtp server. 609 * This makes sure that we and the server are in 610 * the same known state. 611 * 612 * Implements from rfc 821: HELO <SP> <domain> <CRLF> 613 * 614 * SMTP CODE SUCCESS: 250 615 * SMTP CODE ERROR : 500, 501, 504, 421 616 * @access public 617 * @param string $host 618 * @return bool 619 */ 620 public function Hello($host = '') { 621 $this->error = null; // so no confusion is caused 622 623 if(!$this->connected()) { 624 $this->error = array( 625 "error" => "Called Hello() without being connected"); 626 return false; 627 } 628 629 // if hostname for HELO was not specified send default 630 if(empty($host)) { 631 // determine appropriate default to send to server 632 $host = "localhost"; 633 } 634 635 // Send extended hello first (RFC 2821) 636 if(!$this->SendHello("EHLO", $host)) { 637 if(!$this->SendHello("HELO", $host)) { 638 return false; 639 } 640 } 641 642 return true; 643 } 644 645 /** 646 * Sends a HELO/EHLO command. 647 * @access private 648 * @param string $hello 649 * @param string $host 650 * @return bool 651 */ 652 private function SendHello($hello, $host) { 653 fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); 654 655 $rply = $this->get_lines(); 656 $code = substr($rply,0,3); 657 658 if($this->do_debug >= 2) { 659 $this->edebug("SMTP -> FROM SERVER: " . $rply . $this->CRLF . '<br />'); 660 } 661 662 if($code != 250) { 663 $this->error = 664 array("error" => $hello . " not accepted from server", 665 "smtp_code" => $code, 666 "smtp_msg" => substr($rply,4)); 667 if($this->do_debug >= 1) { 668 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 669 } 670 return false; 671 } 672 673 $this->helo_rply = $rply; 674 675 return true; 676 } 677 678 /** 679 * Starts a mail transaction from the email address specified in 680 * $from. Returns true if successful or false otherwise. If True 681 * the mail transaction is started and then one or more Recipient 682 * commands may be called followed by a Data command. 683 * 684 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 685 * 686 * SMTP CODE SUCCESS: 250 687 * SMTP CODE SUCCESS: 552,451,452 688 * SMTP CODE SUCCESS: 500,501,421 689 * @access public 690 * @param string $from 691 * @return bool 692 */ 693 public function Mail($from) { 694 $this->error = null; // so no confusion is caused 695 696 if(!$this->connected()) { 697 $this->error = array( 698 "error" => "Called Mail() without being connected"); 699 return false; 700 } 701 702 $useVerp = ($this->do_verp ? " XVERP" : ""); 703 fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); 704 705 $rply = $this->get_lines(); 706 $code = substr($rply,0,3); 707 708 if($this->do_debug >= 2) { 709 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 710 } 711 712 if($code != 250) { 713 $this->error = 714 array("error" => "MAIL not accepted from server", 715 "smtp_code" => $code, 716 "smtp_msg" => substr($rply,4)); 717 if($this->do_debug >= 1) { 718 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 719 } 720 return false; 721 } 722 return true; 723 } 724 725 /** 726 * Sends the quit command to the server and then closes the socket 727 * if there is no error or the $close_on_error argument is true. 728 * 729 * Implements from rfc 821: QUIT <CRLF> 730 * 731 * SMTP CODE SUCCESS: 221 732 * SMTP CODE ERROR : 500 733 * @access public 734 * @param bool $close_on_error 735 * @return bool 736 */ 737 public function Quit($close_on_error = true) { 738 $this->error = null; // so there is no confusion 739 740 if(!$this->connected()) { 741 $this->error = array( 742 "error" => "Called Quit() without being connected"); 743 return false; 744 } 745 746 // send the quit command to the server 747 fputs($this->smtp_conn,"quit" . $this->CRLF); 748 749 // get any good-bye messages 750 $byemsg = $this->get_lines(); 751 752 if($this->do_debug >= 2) { 753 $this->edebug("SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '<br />'); 754 } 755 756 $rval = true; 757 $e = null; 758 759 $code = substr($byemsg,0,3); 760 if($code != 221) { 761 // use e as a tmp var cause Close will overwrite $this->error 762 $e = array("error" => "SMTP server rejected quit command", 763 "smtp_code" => $code, 764 "smtp_rply" => substr($byemsg,4)); 765 $rval = false; 766 if($this->do_debug >= 1) { 767 $this->edebug("SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '<br />'); 768 } 769 } 770 771 if(empty($e) || $close_on_error) { 772 $this->Close(); 773 } 774 775 return $rval; 776 } 777 778 /** 779 * Sends the command RCPT to the SMTP server with the TO: argument of $to. 780 * Returns true if the recipient was accepted false if it was rejected. 781 * 782 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 783 * 784 * SMTP CODE SUCCESS: 250,251 785 * SMTP CODE FAILURE: 550,551,552,553,450,451,452 786 * SMTP CODE ERROR : 500,501,503,421 787 * @access public 788 * @param string $to 789 * @return bool 790 */ 791 public function Recipient($to) { 792 $this->error = null; // so no confusion is caused 793 794 if(!$this->connected()) { 795 $this->error = array( 796 "error" => "Called Recipient() without being connected"); 797 return false; 798 } 799 800 fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); 801 802 $rply = $this->get_lines(); 803 $code = substr($rply,0,3); 804 805 if($this->do_debug >= 2) { 806 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 807 } 808 809 if($code != 250 && $code != 251) { 810 $this->error = 811 array("error" => "RCPT not accepted from server", 812 "smtp_code" => $code, 813 "smtp_msg" => substr($rply,4)); 814 if($this->do_debug >= 1) { 815 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 816 } 817 return false; 818 } 819 return true; 820 } 821 822 /** 823 * Sends the RSET command to abort and transaction that is 824 * currently in progress. Returns true if successful false 825 * otherwise. 826 * 827 * Implements rfc 821: RSET <CRLF> 828 * 829 * SMTP CODE SUCCESS: 250 830 * SMTP CODE ERROR : 500,501,504,421 831 * @access public 832 * @return bool 833 */ 834 public function Reset() { 835 $this->error = null; // so no confusion is caused 836 837 if(!$this->connected()) { 838 $this->error = array( 839 "error" => "Called Reset() without being connected"); 840 return false; 841 } 842 843 fputs($this->smtp_conn,"RSET" . $this->CRLF); 844 845 $rply = $this->get_lines(); 846 $code = substr($rply,0,3); 847 848 if($this->do_debug >= 2) { 849 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 850 } 851 852 if($code != 250) { 853 $this->error = 854 array("error" => "RSET failed", 855 "smtp_code" => $code, 856 "smtp_msg" => substr($rply,4)); 857 if($this->do_debug >= 1) { 858 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 859 } 860 return false; 861 } 862 863 return true; 864 } 865 866 /** 867 * Starts a mail transaction from the email address specified in 868 * $from. Returns true if successful or false otherwise. If True 869 * the mail transaction is started and then one or more Recipient 870 * commands may be called followed by a Data command. This command 871 * will send the message to the users terminal if they are logged 872 * in and send them an email. 873 * 874 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 875 * 876 * SMTP CODE SUCCESS: 250 877 * SMTP CODE SUCCESS: 552,451,452 878 * SMTP CODE SUCCESS: 500,501,502,421 879 * @access public 880 * @param string $from 881 * @return bool 882 */ 883 public function SendAndMail($from) { 884 $this->error = null; // so no confusion is caused 885 886 if(!$this->connected()) { 887 $this->error = array( 888 "error" => "Called SendAndMail() without being connected"); 889 return false; 890 } 891 892 fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); 893 894 $rply = $this->get_lines(); 895 $code = substr($rply,0,3); 896 897 if($this->do_debug >= 2) { 898 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 899 } 900 901 if($code != 250) { 902 $this->error = 903 array("error" => "SAML not accepted from server", 904 "smtp_code" => $code, 905 "smtp_msg" => substr($rply,4)); 906 if($this->do_debug >= 1) { 907 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 908 } 909 return false; 910 } 911 return true; 912 } 913 914 /** 915 * This is an optional command for SMTP that this class does not 916 * support. This method is here to make the RFC821 Definition 917 * complete for this class and __may__ be implimented in the future 918 * 919 * Implements from rfc 821: TURN <CRLF> 920 * 921 * SMTP CODE SUCCESS: 250 922 * SMTP CODE FAILURE: 502 923 * SMTP CODE ERROR : 500, 503 924 * @access public 925 * @return bool 926 */ 927 public function Turn() { 928 $this->error = array("error" => "This method, TURN, of the SMTP ". 929 "is not implemented"); 930 if($this->do_debug >= 1) { 931 $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '<br />'); 932 } 933 return false; 934 } 935 936 /** 937 * Get the current error 938 * @access public 939 * @return array 940 */ 941 public function getError() { 942 return $this->error; 943 } 944 945 ///////////////////////////////////////////////// 946 // INTERNAL FUNCTIONS 947 ///////////////////////////////////////////////// 948 949 /** 950 * Read in as many lines as possible 951 * either before eof or socket timeout occurs on the operation. 952 * With SMTP we can tell if we have more lines to read if the 953 * 4th character is '-' symbol. If it is a space then we don't 954 * need to read anything else. 955 * @access private 956 * @return string 957 */ 958 private function get_lines() { 959 $data = ""; 960 $endtime = 0; 961 /* If for some reason the fp is bad, don't inf loop */ 962 if (!is_resource($this->smtp_conn)) { 963 return $data; 964 } 965 stream_set_timeout($this->smtp_conn, $this->Timeout); 966 if ($this->Timelimit > 0) { 967 $endtime = time() + $this->Timelimit; 968 } 969 while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 970 $str = @fgets($this->smtp_conn,515); 971 if($this->do_debug >= 4) { 972 $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '<br />'); 973 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '<br />'); 974 } 975 $data .= $str; 976 if($this->do_debug >= 4) { 977 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '<br />'); 978 } 979 // if 4th character is a space, we are done reading, break the loop 980 if(substr($str,3,1) == " ") { break; } 981 // Timed-out? Log and break 982 $info = stream_get_meta_data($this->smtp_conn); 983 if ($info['timed_out']) { 984 if($this->do_debug >= 4) { 985 $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds) <br />"); 986 } 987 break; 988 } 989 // Now check if reads took too long 990 if ($endtime) { 991 if (time() > $endtime) { 992 if($this->do_debug >= 4) { 993 $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds) <br />"); 994 } 995 break; 996 } 997 } 998 } 999 return $data; 1000 } 1001 775 } 776 777 /** 778 * Send raw data to the server. 779 * @param string $data The data to send 780 * @access public 781 * @return int|bool The number of bytes sent to the server or FALSE on error 782 */ 783 public function client_send($data) 784 { 785 if ($this->do_debug >= 1) { 786 $this->edebug("CLIENT -> SMTP: $data"); 787 } 788 return fwrite($this->smtp_conn, $data); 789 } 790 791 /** 792 * Get the latest error. 793 * @access public 794 * @return array 795 */ 796 public function getError() 797 { 798 return $this->error; 799 } 800 801 /** 802 * Get the last reply from the server. 803 * @access public 804 * @return string 805 */ 806 public function getLastReply() 807 { 808 return $this->last_reply; 809 } 810 811 /** 812 * Read the SMTP server's response. 813 * Either before eof or socket timeout occurs on the operation. 814 * With SMTP we can tell if we have more lines to read if the 815 * 4th character is '-' symbol. If it is a space then we don't 816 * need to read anything else. 817 * @access protected 818 * @return string 819 */ 820 protected function get_lines() 821 { 822 $data = ''; 823 $endtime = 0; 824 // If the connection is bad, give up now 825 if (!is_resource($this->smtp_conn)) { 826 return $data; 827 } 828 stream_set_timeout($this->smtp_conn, $this->Timeout); 829 if ($this->Timelimit > 0) { 830 $endtime = time() + $this->Timelimit; 831 } 832 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 833 $str = @fgets($this->smtp_conn, 515); 834 if ($this->do_debug >= 4) { 835 $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); 836 $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); 837 } 838 $data .= $str; 839 if ($this->do_debug >= 4) { 840 $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); 841 } 842 // if 4th character is a space, we are done reading, break the loop 843 if (substr($str, 3, 1) == ' ') { 844 break; 845 } 846 // Timed-out? Log and break 847 $info = stream_get_meta_data($this->smtp_conn); 848 if ($info['timed_out']) { 849 if ($this->do_debug >= 4) { 850 $this->edebug( 851 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' 852 ); 853 } 854 break; 855 } 856 // Now check if reads took too long 857 if ($endtime) { 858 if (time() > $endtime) { 859 if ($this->do_debug >= 4) { 860 $this->edebug( 861 'SMTP -> get_lines(): timelimit reached (' 862 . $this->Timelimit . ' sec)' 863 ); 864 } 865 break; 866 } 867 } 868 } 869 return $data; 870 } 871 872 /** 873 * Enable or disable VERP address generation. 874 * @param bool $enabled 875 */ 876 public function setVerp($enabled = false) 877 { 878 $this->do_verp = $enabled; 879 } 880 881 /** 882 * Get VERP address generation mode. 883 * @return bool 884 */ 885 public function getVerp() 886 { 887 return $this->do_verp; 888 } 889 890 /** 891 * Set debug output method. 892 * @param string $method The function/method to use for debugging output. 893 */ 894 public function setDebugOutput($method = 'echo') 895 { 896 $this->Debugoutput = $method; 897 } 898 899 /** 900 * Get debug output method. 901 * @return string 902 */ 903 public function getDebugOutput() 904 { 905 return $this->Debugoutput; 906 } 907 908 /** 909 * Set debug output level. 910 * @param int $level 911 */ 912 public function setDebugLevel($level = 0) 913 { 914 $this->do_debug = $level; 915 } 916 917 /** 918 * Get debug output level. 919 * @return int 920 */ 921 public function getDebugLevel() 922 { 923 return $this->do_debug; 924 } 925 926 /** 927 * Set SMTP timeout. 928 * @param int $timeout 929 */ 930 public function setTimeout($timeout = 0) 931 { 932 $this->Timeout = $timeout; 933 } 934 935 /** 936 * Get SMTP timeout. 937 * @return int 938 */ 939 public function getTimeout() 940 { 941 return $this->Timeout; 942 } 1002 943 } 1003 ?>
Note: See TracChangeset
for help on using the changeset viewer.