PopBeforeSmtpPlugin.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * Makes sure a connection to a POP3 host has been established prior to connecting to SMTP.
  11. *
  12. * @author Chris Corbyn
  13. */
  14. class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeListener, Swift_Plugins_Pop_Pop3Connection
  15. {
  16. /** A delegate connection to use (mostly a test hook) */
  17. private $connection;
  18. /** Hostname of the POP3 server */
  19. private $host;
  20. /** Port number to connect on */
  21. private $port;
  22. /** Encryption type to use (if any) */
  23. private $crypto;
  24. /** Username to use (if any) */
  25. private $username;
  26. /** Password to use (if any) */
  27. private $password;
  28. /** Established connection via TCP socket */
  29. private $socket;
  30. /** Connect timeout in seconds */
  31. private $timeout = 10;
  32. /** SMTP Transport to bind to */
  33. private $transport;
  34. /**
  35. * Create a new PopBeforeSmtpPlugin for $host and $port.
  36. *
  37. * @param string $host Hostname or IP. Literal IPv6 addresses should be
  38. * wrapped in square brackets.
  39. * @param int $port
  40. * @param string $crypto as "tls" or "ssl"
  41. */
  42. public function __construct($host, $port = 110, $crypto = null)
  43. {
  44. $this->host = $host;
  45. $this->port = $port;
  46. $this->crypto = $crypto;
  47. }
  48. /**
  49. * Set a Pop3Connection to delegate to instead of connecting directly.
  50. *
  51. * @return $this
  52. */
  53. public function setConnection(Swift_Plugins_Pop_Pop3Connection $connection)
  54. {
  55. $this->connection = $connection;
  56. return $this;
  57. }
  58. /**
  59. * Bind this plugin to a specific SMTP transport instance.
  60. */
  61. public function bindSmtp(Swift_Transport $smtp)
  62. {
  63. $this->transport = $smtp;
  64. }
  65. /**
  66. * Set the connection timeout in seconds (default 10).
  67. *
  68. * @param int $timeout
  69. *
  70. * @return $this
  71. */
  72. public function setTimeout($timeout)
  73. {
  74. $this->timeout = (int) $timeout;
  75. return $this;
  76. }
  77. /**
  78. * Set the username to use when connecting (if needed).
  79. *
  80. * @param string $username
  81. *
  82. * @return $this
  83. */
  84. public function setUsername($username)
  85. {
  86. $this->username = $username;
  87. return $this;
  88. }
  89. /**
  90. * Set the password to use when connecting (if needed).
  91. *
  92. * @param string $password
  93. *
  94. * @return $this
  95. */
  96. public function setPassword($password)
  97. {
  98. $this->password = $password;
  99. return $this;
  100. }
  101. /**
  102. * Connect to the POP3 host and authenticate.
  103. *
  104. * @throws Swift_Plugins_Pop_Pop3Exception if connection fails
  105. */
  106. public function connect()
  107. {
  108. if (isset($this->connection)) {
  109. $this->connection->connect();
  110. } else {
  111. if (!isset($this->socket)) {
  112. if (!$socket = fsockopen(
  113. $this->getHostString(), $this->port, $errno, $errstr, $this->timeout)) {
  114. throw new Swift_Plugins_Pop_Pop3Exception(sprintf('Failed to connect to POP3 host [%s]: %s', $this->host, $errstr));
  115. }
  116. $this->socket = $socket;
  117. if (false === $greeting = fgets($this->socket)) {
  118. throw new Swift_Plugins_Pop_Pop3Exception(sprintf('Failed to connect to POP3 host [%s]', trim($greeting ?? '')));
  119. }
  120. $this->assertOk($greeting);
  121. if ($this->username) {
  122. $this->command(sprintf("USER %s\r\n", $this->username));
  123. $this->command(sprintf("PASS %s\r\n", $this->password));
  124. }
  125. }
  126. }
  127. }
  128. /**
  129. * Disconnect from the POP3 host.
  130. */
  131. public function disconnect()
  132. {
  133. if (isset($this->connection)) {
  134. $this->connection->disconnect();
  135. } else {
  136. $this->command("QUIT\r\n");
  137. if (!fclose($this->socket)) {
  138. throw new Swift_Plugins_Pop_Pop3Exception(sprintf('POP3 host [%s] connection could not be stopped', $this->host));
  139. }
  140. $this->socket = null;
  141. }
  142. }
  143. /**
  144. * Invoked just before a Transport is started.
  145. */
  146. public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt)
  147. {
  148. if (isset($this->transport)) {
  149. if ($this->transport !== $evt->getTransport()) {
  150. return;
  151. }
  152. }
  153. $this->connect();
  154. $this->disconnect();
  155. }
  156. /**
  157. * Not used.
  158. */
  159. public function transportStarted(Swift_Events_TransportChangeEvent $evt)
  160. {
  161. }
  162. /**
  163. * Not used.
  164. */
  165. public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt)
  166. {
  167. }
  168. /**
  169. * Not used.
  170. */
  171. public function transportStopped(Swift_Events_TransportChangeEvent $evt)
  172. {
  173. }
  174. private function command($command)
  175. {
  176. if (!fwrite($this->socket, $command)) {
  177. throw new Swift_Plugins_Pop_Pop3Exception(sprintf('Failed to write command [%s] to POP3 host', trim($command ?? '')));
  178. }
  179. if (false === $response = fgets($this->socket)) {
  180. throw new Swift_Plugins_Pop_Pop3Exception(sprintf('Failed to read from POP3 host after command [%s]', trim($command ?? '')));
  181. }
  182. $this->assertOk($response);
  183. return $response;
  184. }
  185. private function assertOk($response)
  186. {
  187. if ('+OK' != substr($response, 0, 3)) {
  188. throw new Swift_Plugins_Pop_Pop3Exception(sprintf('POP3 command failed [%s]', trim($response ?? '')));
  189. }
  190. }
  191. private function getHostString()
  192. {
  193. $host = $this->host;
  194. switch (strtolower($this->crypto ?? '')) {
  195. case 'ssl':
  196. $host = 'ssl://'.$host;
  197. break;
  198. case 'tls':
  199. $host = 'tls://'.$host;
  200. break;
  201. }
  202. return $host;
  203. }
  204. }