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