/fuel/packages/email/classes/email/driver/smtp.php

https://github.com/hendrik-weiler/Portal-CMS · PHP · 269 lines · 152 code · 50 blank · 67 comment · 12 complexity · e61c47f8eccf7e132f5538e71c7c314e MD5 · raw file

  1. <?php
  2. /**
  3. * Fuel
  4. *
  5. * Fuel is a fast, lightweight, community driven PHP5 framework.
  6. *
  7. * @package Fuel
  8. * @version 1.6
  9. * @author Fuel Development Team
  10. * @license MIT License
  11. * @copyright 2010 - 2013 Fuel Development Team
  12. * @link http://fuelphp.com
  13. */
  14. namespace Email;
  15. class SmtpConnectionException extends \FuelException {}
  16. class SmtpCommandFailureException extends \EmailSendingFailedException {}
  17. class SmtpTimeoutException extends \EmailSendingFailedException {}
  18. class SmtpAuthenticationFailedException extends \FuelException {}
  19. class Email_Driver_Smtp extends \Email_Driver
  20. {
  21. /**
  22. * Class destructor
  23. */
  24. function __destruct()
  25. {
  26. // makes sure any open connections will be closed
  27. if ( ! empty($this->smtp_connection))
  28. {
  29. $this->smtp_disconnect();
  30. }
  31. }
  32. /**
  33. * The SMTP connection
  34. */
  35. protected $smtp_connection = 0;
  36. /**
  37. * Initalted all needed for SMTP mailing.
  38. *
  39. * @return bool success boolean
  40. */
  41. protected function _send()
  42. {
  43. $message = $this->build_message(true);
  44. if(empty($this->config['smtp']['host']) or empty($this->config['smtp']['port']))
  45. {
  46. throw new \FuelException('Must supply a SMTP host and port, none given.');
  47. }
  48. // Use authentication?
  49. $authenticate = ! empty($this->config['smtp']['username']) and ! empty($this->config['smtp']['password']);
  50. // Connect
  51. $this->smtp_connect();
  52. // Authenticate when needed
  53. $authenticate and $this->smtp_authenticate();
  54. // Set from
  55. $this->smtp_send('MAIL FROM:<'.$this->config['from']['email'].'>', 250);
  56. foreach(array('to', 'cc', 'bcc') as $list)
  57. {
  58. foreach($this->{$list} as $recipient)
  59. {
  60. $this->smtp_send('RCPT TO:<'.$recipient['email'].'>', array(250, 251));
  61. }
  62. }
  63. // Prepare for data sending
  64. $this->smtp_send('DATA', 354);
  65. $lines = explode($this->config['newline'], $message['header'].$this->config['newline'].preg_replace('/^\./m', '..$1', $message['body']));
  66. foreach($lines as $line)
  67. {
  68. if(substr($line, 0, 1) === '.')
  69. {
  70. $line = '.'.$line;
  71. }
  72. fputs($this->smtp_connection, $line.$this->config['newline']);
  73. }
  74. // Finish the message
  75. $this->smtp_send('.', 250);
  76. // Close the connection if we're not using pipelining
  77. $this->pipelining or $this->smtp_disconnect();
  78. return true;
  79. }
  80. /**
  81. * Connects to the given smtp and says hello to the other server.
  82. */
  83. protected function smtp_connect()
  84. {
  85. if ($this->pipelining and ! empty($this->smtp_connection))
  86. {
  87. // re-use the existing connection
  88. return;
  89. }
  90. $this->smtp_connection = @fsockopen(
  91. $this->config['smtp']['host'],
  92. $this->config['smtp']['port'],
  93. $error_number,
  94. $error_string,
  95. $this->config['smtp']['timeout']
  96. );
  97. if(empty($this->smtp_connection))
  98. {
  99. throw new \SmtpConnectionException('Could not connect to SMTP: ('.$error_number.') '.$error_string);
  100. }
  101. // Clear the smtp response
  102. $this->smtp_get_response();
  103. // Just say hello!
  104. try
  105. {
  106. $this->smtp_send('EHLO'.' '.\Input::server('SERVER_NAME', 'localhost.local'), 250);
  107. }
  108. catch(\SmtpCommandFailureException $e)
  109. {
  110. // Didn't work? Try HELO
  111. $this->smtp_send('HELO'.' '.\Input::server('SERVER_NAME', 'localhost.local'), 250);
  112. }
  113. try
  114. {
  115. $this->smtp_send('HELP', 214);
  116. }
  117. catch(\SmtpCommandFailureException $e)
  118. {
  119. // Let this pass as some servers don't support this.
  120. }
  121. }
  122. /**
  123. * Close SMTP connection
  124. */
  125. protected function smtp_disconnect()
  126. {
  127. $this->smtp_send('QUIT', 221);
  128. fclose($this->smtp_connection);
  129. $this->smtp_connection = 0;
  130. }
  131. /**
  132. * Performs authentication with the SMTP host
  133. */
  134. protected function smtp_authenticate()
  135. {
  136. // Encode login data
  137. $username = base64_encode($this->config['smtp']['username']);
  138. $password = base64_encode($this->config['smtp']['password']);
  139. try
  140. {
  141. // Prepare login
  142. $this->smtp_send('AUTH LOGIN', 334);
  143. // Send username
  144. $this->smtp_send($username, 334);
  145. // Send password
  146. $this->smtp_send($password, 235);
  147. }
  148. catch(\SmtpCommandFailureException $e)
  149. {
  150. throw new \SmtpAuthenticationFailedException('Failed authentication.');
  151. }
  152. }
  153. /**
  154. * Sends data to the SMTP host
  155. *
  156. * @param string $data the SMTP command
  157. * @param mixed $expecting the expected response
  158. * @param bool $return_number set to true to return the status number
  159. * @return mixed result or result number, false when expecting is false
  160. * @throws SmtpCommandFailureException when the command failed an expecting is not set to false.
  161. */
  162. protected function smtp_send($data, $expecting, $return_number = false)
  163. {
  164. ! is_array($expecting) and $expecting !== false and $expecting = array($expecting);
  165. stream_set_timeout($this->smtp_connection, $this->config['smtp']['timeout']);
  166. if ( ! fputs($this->smtp_connection, $data . $this->config['newline']))
  167. {
  168. if($expecting === false)
  169. {
  170. return false;
  171. }
  172. throw new \SmtpCommandFailureException('Failed executing command: '. $data);
  173. }
  174. $info = stream_get_meta_data($this->smtp_connection);
  175. if($info['timed_out'])
  176. {
  177. throw new \SmtpTimeoutException('SMTP connection timed out.');
  178. }
  179. // Get the reponse
  180. $response = $this->smtp_get_response();
  181. // Get the reponse number
  182. $number = (int) substr(trim($response), 0, 3);
  183. // Check against expected result
  184. if($expecting !== false and ! in_array($number, $expecting))
  185. {
  186. throw new \SmtpCommandFailureException('Got an unexpected response from host on command: ['.$data.'] expecting: '.join(' or ',$expecting).' received: '.$response);
  187. }
  188. if($return_number)
  189. {
  190. return $number;
  191. }
  192. return $response;
  193. }
  194. /**
  195. * Get SMTP response
  196. *
  197. * @return string SMTP response
  198. */
  199. protected function smtp_get_response()
  200. {
  201. $data = '';
  202. // set the timeout.
  203. stream_set_timeout($this->smtp_connection, $this->config['smtp']['timeout']);
  204. while($str = fgets($this->smtp_connection, 512))
  205. {
  206. $info = stream_get_meta_data($this->smtp_connection);
  207. if($info['timed_out'])
  208. {
  209. throw new \SmtpTimeoutException('SMTP connection timed out.');
  210. }
  211. $data .= $str;
  212. if (substr($str, 3, 1) === ' ')
  213. {
  214. break;
  215. }
  216. }
  217. return $data;
  218. }
  219. }