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 using the mail() function.
14 *
15 * It is advised that users do not use this transport if at all possible
16 * since a number of plugin features cannot be used in conjunction with this
17 * transport due to the internal interface in PHP itself.
18 *
19 * The level of error reporting with this transport is incredibly weak, again
20 * due to limitations of PHP's internal mail() function.  You'll get an
21 * all-or-nothing result from sending.
22 *
23 * @package Swift
24 * @subpackage Transport
25 * @author Chris Corbyn
26 */
27class Swift_Transport_MailTransport implements Swift_Transport
28{
29
30  /** Addtional parameters to pass to mail() */
31  private $_extraParams = '-f%s';
32
33  /** The event dispatcher from the plugin API */
34  private $_eventDispatcher;
35
36  /** An invoker that calls the mail() function */
37  private $_invoker;
38
39  /**
40   * Create a new MailTransport with the $log.
41   * @param Swift_Transport_Log $log
42   */
43  public function __construct(Swift_Transport_MailInvoker $invoker,
44    Swift_Events_EventDispatcher $eventDispatcher)
45  {
46    $this->_invoker = $invoker;
47    $this->_eventDispatcher = $eventDispatcher;
48  }
49
50  /**
51   * Not used.
52   */
53  public function isStarted()
54  {
55    return false;
56  }
57
58  /**
59   * Not used.
60   */
61  public function start()
62  {
63  }
64
65  /**
66   * Not used.
67   */
68  public function stop()
69  {
70  }
71
72  /**
73   * Set the additional parameters used on the mail() function.
74   *
75   * This string is formatted for sprintf() where %s is the sender address.
76   *
77   * @param string $params
78   * @return Swift_Transport_MailTransport
79   */
80  public function setExtraParams($params)
81  {
82    $this->_extraParams = $params;
83    return $this;
84  }
85
86  /**
87   * Get the additional parameters used on the mail() function.
88   *
89   * This string is formatted for sprintf() where %s is the sender address.
90   *
91   * @return string
92   */
93  public function getExtraParams()
94  {
95    return $this->_extraParams;
96  }
97
98  /**
99   * Send the given Message.
100   *
101   * Recipient/sender data will be retrieved from the Message API.
102   * The return value is the number of recipients who were accepted for delivery.
103   *
104   * @param Swift_Mime_Message $message
105   * @param string[] &$failedRecipients to collect failures by-reference
106   * @return int
107   */
108  public function send(Swift_Mime_Message $message, &$failedRecipients = null)
109  {
110    $failedRecipients = (array) $failedRecipients;
111
112    if ($evt = $this->_eventDispatcher->createSendEvent($this, $message))
113    {
114      $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
115      if ($evt->bubbleCancelled())
116      {
117        return 0;
118      }
119    }
120
121    $count = (
122      count((array) $message->getTo())
123      + count((array) $message->getCc())
124      + count((array) $message->getBcc())
125      );
126
127    $toHeader = $message->getHeaders()->get('To');
128    $subjectHeader = $message->getHeaders()->get('Subject');
129
130    if (!$toHeader)
131    {
132      throw new Swift_TransportException(
133        'Cannot send message without a recipient'
134        );
135    }
136    $to = $toHeader->getFieldBody();
137    $subject = $subjectHeader ? $subjectHeader->getFieldBody() : '';
138
139    $reversePath = $this->_getReversePath($message);
140
141    //Remove headers that would otherwise be duplicated
142    $message->getHeaders()->remove('To');
143    $message->getHeaders()->remove('Subject');
144
145    $messageStr = $message->toString();
146
147    $message->getHeaders()->set($toHeader);
148    $message->getHeaders()->set($subjectHeader);
149
150    //Separate headers from body
151    if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n"))
152    {
153      $headers = substr($messageStr, 0, $endHeaders) . "\r\n"; //Keep last EOL
154      $body = substr($messageStr, $endHeaders + 4);
155    }
156    else
157    {
158      $headers = $messageStr . "\r\n";
159      $body = '';
160    }
161
162    unset($messageStr);
163
164    if ("\r\n" != PHP_EOL) //Non-windows (not using SMTP)
165    {
166      $headers = str_replace("\r\n", PHP_EOL, $headers);
167      $body = str_replace("\r\n", PHP_EOL, $body);
168    }
169    else //Windows, using SMTP
170    {
171      $headers = str_replace("\r\n.", "\r\n..", $headers);
172      $body = str_replace("\r\n.", "\r\n..", $body);
173    }
174
175    if ($this->_invoker->mail($to, $subject, $body, $headers,
176      sprintf($this->_extraParams, $reversePath)))
177    {
178      if ($evt)
179      {
180        $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
181        $evt->setFailedRecipients($failedRecipients);
182        $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
183      }
184    }
185    else
186    {
187      $failedRecipients = array_merge(
188        $failedRecipients,
189        array_keys((array) $message->getTo()),
190        array_keys((array) $message->getCc()),
191        array_keys((array) $message->getBcc())
192        );
193
194      if ($evt)
195      {
196        $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
197        $evt->setFailedRecipients($failedRecipients);
198        $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
199      }
200
201      $message->generateId();
202
203      $count = 0;
204    }
205
206    return $count;
207  }
208
209  /**
210   * Register a plugin.
211   *
212   * @param Swift_Events_EventListener $plugin
213   */
214  public function registerPlugin(Swift_Events_EventListener $plugin)
215  {
216    $this->_eventDispatcher->bindEventListener($plugin);
217  }
218
219  // -- Private methods
220
221  /** Determine the best-use reverse path for this message */
222  private function _getReversePath(Swift_Mime_Message $message)
223  {
224    $return = $message->getReturnPath();
225    $sender = $message->getSender();
226    $from = $message->getFrom();
227    $path = null;
228    if (!empty($return))
229    {
230      $path = $return;
231    }
232    elseif (!empty($sender))
233    {
234      $keys = array_keys($sender);
235      $path = array_shift($keys);
236    }
237    elseif (!empty($from))
238    {
239      $keys = array_keys($from);
240      $path = array_shift($keys);
241    }
242    return $path;
243  }
244
245}
246