|
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 /** |
|
13 * Sends Messages over SMTP with ESMTP support. |
|
14 * @package Swift |
|
15 * @subpackage Transport |
|
16 * @author Chris Corbyn |
|
17 */ |
|
18 class Swift_Transport_EsmtpTransport |
|
19 extends Swift_Transport_AbstractSmtpTransport |
|
20 implements Swift_Transport_SmtpAgent |
|
21 { |
|
22 |
|
23 /** |
|
24 * ESMTP extension handlers. |
|
25 * @var Swift_Transport_EsmtpHandler[] |
|
26 * @access private |
|
27 */ |
|
28 private $_handlers = array(); |
|
29 |
|
30 /** |
|
31 * ESMTP capabilities. |
|
32 * @var string[] |
|
33 * @access private |
|
34 */ |
|
35 private $_capabilities = array(); |
|
36 |
|
37 /** |
|
38 * Connection buffer parameters. |
|
39 * @var array |
|
40 * @access protected |
|
41 */ |
|
42 private $_params = array( |
|
43 'protocol' => 'tcp', |
|
44 'host' => 'localhost', |
|
45 'port' => 25, |
|
46 'timeout' => 30, |
|
47 'blocking' => 1, |
|
48 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET |
|
49 ); |
|
50 |
|
51 /** |
|
52 * Creates a new EsmtpTransport using the given I/O buffer. |
|
53 * @param Swift_Transport_IoBuffer $buf |
|
54 * @param Swift_Transport_EsmtpHandler[] $extensionHandlers |
|
55 * @param Swift_Events_EventDispatcher $dispatcher |
|
56 */ |
|
57 public function __construct(Swift_Transport_IoBuffer $buf, |
|
58 array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher) |
|
59 { |
|
60 parent::__construct($buf, $dispatcher); |
|
61 $this->setExtensionHandlers($extensionHandlers); |
|
62 } |
|
63 |
|
64 /** |
|
65 * Set the host to connect to. |
|
66 * @param string $host |
|
67 */ |
|
68 public function setHost($host) |
|
69 { |
|
70 $this->_params['host'] = $host; |
|
71 return $this; |
|
72 } |
|
73 |
|
74 /** |
|
75 * Get the host to connect to. |
|
76 * @return string |
|
77 */ |
|
78 public function getHost() |
|
79 { |
|
80 return $this->_params['host']; |
|
81 } |
|
82 |
|
83 /** |
|
84 * Set the port to connect to. |
|
85 * @param int $port |
|
86 */ |
|
87 public function setPort($port) |
|
88 { |
|
89 $this->_params['port'] = (int) $port; |
|
90 return $this; |
|
91 } |
|
92 |
|
93 /** |
|
94 * Get the port to connect to. |
|
95 * @return int |
|
96 */ |
|
97 public function getPort() |
|
98 { |
|
99 return $this->_params['port']; |
|
100 } |
|
101 |
|
102 /** |
|
103 * Set the connection timeout. |
|
104 * @param int $timeout seconds |
|
105 */ |
|
106 public function setTimeout($timeout) |
|
107 { |
|
108 $this->_params['timeout'] = (int) $timeout; |
|
109 $this->_buffer->setParam('timeout', (int) $timeout); |
|
110 return $this; |
|
111 } |
|
112 |
|
113 /** |
|
114 * Get the connection timeout. |
|
115 * @return int |
|
116 */ |
|
117 public function getTimeout() |
|
118 { |
|
119 return $this->_params['timeout']; |
|
120 } |
|
121 |
|
122 /** |
|
123 * Set the encryption type (tls or ssl) |
|
124 * @param string $encryption |
|
125 */ |
|
126 public function setEncryption($enc) |
|
127 { |
|
128 $this->_params['protocol'] = $enc; |
|
129 return $this; |
|
130 } |
|
131 |
|
132 /** |
|
133 * Get the encryption type. |
|
134 * @return string |
|
135 */ |
|
136 public function getEncryption() |
|
137 { |
|
138 return $this->_params['protocol']; |
|
139 } |
|
140 |
|
141 /** |
|
142 * Sets the sourceIp |
|
143 * @param string $source |
|
144 */ |
|
145 public function setSourceIp($source) |
|
146 { |
|
147 $this->_params['sourceIp']=$source; |
|
148 return $this; |
|
149 } |
|
150 |
|
151 /** |
|
152 * Returns the ip used to connect to the destination |
|
153 * @return string |
|
154 */ |
|
155 public function getSourceIp() |
|
156 { |
|
157 return $this->_params['sourceIp']; |
|
158 } |
|
159 |
|
160 /** |
|
161 * Set ESMTP extension handlers. |
|
162 * @param Swift_Transport_EsmtpHandler[] $handlers |
|
163 */ |
|
164 public function setExtensionHandlers(array $handlers) |
|
165 { |
|
166 $assoc = array(); |
|
167 foreach ($handlers as $handler) |
|
168 { |
|
169 $assoc[$handler->getHandledKeyword()] = $handler; |
|
170 } |
|
171 uasort($assoc, array($this, '_sortHandlers')); |
|
172 $this->_handlers = $assoc; |
|
173 $this->_setHandlerParams(); |
|
174 return $this; |
|
175 } |
|
176 |
|
177 /** |
|
178 * Get ESMTP extension handlers. |
|
179 * @return Swift_Transport_EsmtpHandler[] |
|
180 */ |
|
181 public function getExtensionHandlers() |
|
182 { |
|
183 return array_values($this->_handlers); |
|
184 } |
|
185 |
|
186 /** |
|
187 * Run a command against the buffer, expecting the given response codes. |
|
188 * If no response codes are given, the response will not be validated. |
|
189 * If codes are given, an exception will be thrown on an invalid response. |
|
190 * @param string $command |
|
191 * @param int[] $codes |
|
192 * @param string[] &$failures |
|
193 * @return string |
|
194 */ |
|
195 public function executeCommand($command, $codes = array(), &$failures = null) |
|
196 { |
|
197 $failures = (array) $failures; |
|
198 $stopSignal = false; |
|
199 $response = null; |
|
200 foreach ($this->_getActiveHandlers() as $handler) |
|
201 { |
|
202 $response = $handler->onCommand( |
|
203 $this, $command, $codes, $failures, $stopSignal |
|
204 ); |
|
205 if ($stopSignal) |
|
206 { |
|
207 return $response; |
|
208 } |
|
209 } |
|
210 return parent::executeCommand($command, $codes, $failures); |
|
211 } |
|
212 |
|
213 // -- Mixin invocation code |
|
214 |
|
215 /** Mixin handling method for ESMTP handlers */ |
|
216 public function __call($method, $args) |
|
217 { |
|
218 foreach ($this->_handlers as $handler) |
|
219 { |
|
220 if (in_array(strtolower($method), |
|
221 array_map('strtolower', (array) $handler->exposeMixinMethods()) |
|
222 )) |
|
223 { |
|
224 $return = call_user_func_array(array($handler, $method), $args); |
|
225 //Allow fluid method calls |
|
226 if (is_null($return) && substr($method, 0, 3) == 'set') |
|
227 { |
|
228 return $this; |
|
229 } |
|
230 else |
|
231 { |
|
232 return $return; |
|
233 } |
|
234 } |
|
235 } |
|
236 trigger_error('Call to undefined method ' . $method, E_USER_ERROR); |
|
237 } |
|
238 |
|
239 // -- Protected methods |
|
240 |
|
241 /** Get the params to initialize the buffer */ |
|
242 protected function _getBufferParams() |
|
243 { |
|
244 return $this->_params; |
|
245 } |
|
246 |
|
247 /** Overridden to perform EHLO instead */ |
|
248 protected function _doHeloCommand() |
|
249 { |
|
250 try |
|
251 { |
|
252 $response = $this->executeCommand( |
|
253 sprintf("EHLO %s\r\n", $this->_domain), array(250) |
|
254 ); |
|
255 } |
|
256 catch (Swift_TransportException $e) |
|
257 { |
|
258 return parent::_doHeloCommand(); |
|
259 } |
|
260 |
|
261 $this->_capabilities = $this->_getCapabilities($response); |
|
262 $this->_setHandlerParams(); |
|
263 foreach ($this->_getActiveHandlers() as $handler) |
|
264 { |
|
265 $handler->afterEhlo($this); |
|
266 } |
|
267 } |
|
268 |
|
269 /** Overridden to add Extension support */ |
|
270 protected function _doMailFromCommand($address) |
|
271 { |
|
272 $handlers = $this->_getActiveHandlers(); |
|
273 $params = array(); |
|
274 foreach ($handlers as $handler) |
|
275 { |
|
276 $params = array_merge($params, (array) $handler->getMailParams()); |
|
277 } |
|
278 $paramStr = !empty($params) ? ' ' . implode(' ', $params) : ''; |
|
279 $this->executeCommand( |
|
280 sprintf("MAIL FROM: <%s>%s\r\n", $address, $paramStr), array(250) |
|
281 ); |
|
282 } |
|
283 |
|
284 /** Overridden to add Extension support */ |
|
285 protected function _doRcptToCommand($address) |
|
286 { |
|
287 $handlers = $this->_getActiveHandlers(); |
|
288 $params = array(); |
|
289 foreach ($handlers as $handler) |
|
290 { |
|
291 $params = array_merge($params, (array) $handler->getRcptParams()); |
|
292 } |
|
293 $paramStr = !empty($params) ? ' ' . implode(' ', $params) : ''; |
|
294 $this->executeCommand( |
|
295 sprintf("RCPT TO: <%s>%s\r\n", $address, $paramStr), array(250, 251, 252) |
|
296 ); |
|
297 } |
|
298 |
|
299 // -- Private methods |
|
300 |
|
301 /** Determine ESMTP capabilities by function group */ |
|
302 private function _getCapabilities($ehloResponse) |
|
303 { |
|
304 $capabilities = array(); |
|
305 $ehloResponse = trim($ehloResponse); |
|
306 $lines = explode("\r\n", $ehloResponse); |
|
307 array_shift($lines); |
|
308 foreach ($lines as $line) |
|
309 { |
|
310 if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) |
|
311 { |
|
312 $keyword = strtoupper($matches[1]); |
|
313 $paramStr = strtoupper(ltrim($matches[2], ' =')); |
|
314 $params = !empty($paramStr) ? explode(' ', $paramStr) : array(); |
|
315 $capabilities[$keyword] = $params; |
|
316 } |
|
317 } |
|
318 return $capabilities; |
|
319 } |
|
320 |
|
321 /** Set parameters which are used by each extension handler */ |
|
322 private function _setHandlerParams() |
|
323 { |
|
324 foreach ($this->_handlers as $keyword => $handler) |
|
325 { |
|
326 if (array_key_exists($keyword, $this->_capabilities)) |
|
327 { |
|
328 $handler->setKeywordParams($this->_capabilities[$keyword]); |
|
329 } |
|
330 } |
|
331 } |
|
332 |
|
333 /** Get ESMTP handlers which are currently ok to use */ |
|
334 private function _getActiveHandlers() |
|
335 { |
|
336 $handlers = array(); |
|
337 foreach ($this->_handlers as $keyword => $handler) |
|
338 { |
|
339 if (array_key_exists($keyword, $this->_capabilities)) |
|
340 { |
|
341 $handlers[] = $handler; |
|
342 } |
|
343 } |
|
344 return $handlers; |
|
345 } |
|
346 |
|
347 /** Custom sort for extension handler ordering */ |
|
348 private function _sortHandlers($a, $b) |
|
349 { |
|
350 return $a->getPriorityOver($b->getHandledKeyword()); |
|
351 } |
|
352 |
|
353 } |