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