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 * The Message class for building emails.
13 *
14 * @author Chris Corbyn
15 */
16class Swift_Message extends Swift_Mime_SimpleMessage
17{
18    /**
19     * @var Swift_Signers_HeaderSigner[]
20     */
21    private $headerSigners = array();
22
23    /**
24     * @var Swift_Signers_BodySigner[]
25     */
26    private $bodySigners = array();
27
28    /**
29     * @var array
30     */
31    private $savedMessage = array();
32
33    /**
34     * Create a new Message.
35     *
36     * Details may be optionally passed into the constructor.
37     *
38     * @param string $subject
39     * @param string $body
40     * @param string $contentType
41     * @param string $charset
42     */
43    public function __construct($subject = null, $body = null, $contentType = null, $charset = null)
44    {
45        call_user_func_array(
46            array($this, 'Swift_Mime_SimpleMessage::__construct'),
47            Swift_DependencyContainer::getInstance()
48                ->createDependenciesFor('mime.message')
49            );
50
51        if (!isset($charset)) {
52            $charset = Swift_DependencyContainer::getInstance()
53                ->lookup('properties.charset');
54        }
55        $this->setSubject($subject);
56        $this->setBody($body);
57        $this->setCharset($charset);
58        if ($contentType) {
59            $this->setContentType($contentType);
60        }
61    }
62
63    /**
64     * Create a new Message.
65     *
66     * @param string $subject
67     * @param string $body
68     * @param string $contentType
69     * @param string $charset
70     *
71     * @return $this
72     */
73    public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null)
74    {
75        return new self($subject, $body, $contentType, $charset);
76    }
77
78    /**
79     * Add a MimePart to this Message.
80     *
81     * @param string|Swift_OutputByteStream $body
82     * @param string                        $contentType
83     * @param string                        $charset
84     *
85     * @return $this
86     */
87    public function addPart($body, $contentType = null, $charset = null)
88    {
89        return $this->attach(Swift_MimePart::newInstance($body, $contentType, $charset)->setEncoder($this->getEncoder()));
90    }
91
92    /**
93     * Detach a signature handler from a message.
94     *
95     * @param Swift_Signer $signer
96     *
97     * @return $this
98     */
99    public function attachSigner(Swift_Signer $signer)
100    {
101        if ($signer instanceof Swift_Signers_HeaderSigner) {
102            $this->headerSigners[] = $signer;
103        } elseif ($signer instanceof Swift_Signers_BodySigner) {
104            $this->bodySigners[] = $signer;
105        }
106
107        return $this;
108    }
109
110    /**
111     * Attach a new signature handler to the message.
112     *
113     * @param Swift_Signer $signer
114     *
115     * @return $this
116     */
117    public function detachSigner(Swift_Signer $signer)
118    {
119        if ($signer instanceof Swift_Signers_HeaderSigner) {
120            foreach ($this->headerSigners as $k => $headerSigner) {
121                if ($headerSigner === $signer) {
122                    unset($this->headerSigners[$k]);
123
124                    return $this;
125                }
126            }
127        } elseif ($signer instanceof Swift_Signers_BodySigner) {
128            foreach ($this->bodySigners as $k => $bodySigner) {
129                if ($bodySigner === $signer) {
130                    unset($this->bodySigners[$k]);
131
132                    return $this;
133                }
134            }
135        }
136
137        return $this;
138    }
139
140    /**
141     * Get this message as a complete string.
142     *
143     * @return string
144     */
145    public function toString()
146    {
147        if (empty($this->headerSigners) && empty($this->bodySigners)) {
148            return parent::toString();
149        }
150
151        $this->saveMessage();
152
153        $this->doSign();
154
155        $string = parent::toString();
156
157        $this->restoreMessage();
158
159        return $string;
160    }
161
162    /**
163     * Write this message to a {@link Swift_InputByteStream}.
164     *
165     * @param Swift_InputByteStream $is
166     */
167    public function toByteStream(Swift_InputByteStream $is)
168    {
169        if (empty($this->headerSigners) && empty($this->bodySigners)) {
170            parent::toByteStream($is);
171
172            return;
173        }
174
175        $this->saveMessage();
176
177        $this->doSign();
178
179        parent::toByteStream($is);
180
181        $this->restoreMessage();
182    }
183
184    public function __wakeup()
185    {
186        Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message');
187    }
188
189    /**
190     * loops through signers and apply the signatures.
191     */
192    protected function doSign()
193    {
194        foreach ($this->bodySigners as $signer) {
195            $altered = $signer->getAlteredHeaders();
196            $this->saveHeaders($altered);
197            $signer->signMessage($this);
198        }
199
200        foreach ($this->headerSigners as $signer) {
201            $altered = $signer->getAlteredHeaders();
202            $this->saveHeaders($altered);
203            $signer->reset();
204
205            $signer->setHeaders($this->getHeaders());
206
207            $signer->startBody();
208            $this->_bodyToByteStream($signer);
209            $signer->endBody();
210
211            $signer->addSignature($this->getHeaders());
212        }
213    }
214
215    /**
216     * save the message before any signature is applied.
217     */
218    protected function saveMessage()
219    {
220        $this->savedMessage = array('headers' => array());
221        $this->savedMessage['body'] = $this->getBody();
222        $this->savedMessage['children'] = $this->getChildren();
223        if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') {
224            $this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children']));
225            $this->setBody('');
226        }
227    }
228
229    /**
230     * save the original headers.
231     *
232     * @param array $altered
233     */
234    protected function saveHeaders(array $altered)
235    {
236        foreach ($altered as $head) {
237            $lc = strtolower($head);
238
239            if (!isset($this->savedMessage['headers'][$lc])) {
240                $this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head);
241            }
242        }
243    }
244
245    /**
246     * Remove or restore altered headers.
247     */
248    protected function restoreHeaders()
249    {
250        foreach ($this->savedMessage['headers'] as $name => $savedValue) {
251            $headers = $this->getHeaders()->getAll($name);
252
253            foreach ($headers as $key => $value) {
254                if (!isset($savedValue[$key])) {
255                    $this->getHeaders()->remove($name, $key);
256                }
257            }
258        }
259    }
260
261    /**
262     * Restore message body.
263     */
264    protected function restoreMessage()
265    {
266        $this->setBody($this->savedMessage['body']);
267        $this->setChildren($this->savedMessage['children']);
268
269        $this->restoreHeaders();
270        $this->savedMessage = array();
271    }
272
273    /**
274     * Clone Message Signers.
275     *
276     * @see Swift_Mime_SimpleMimeEntity::__clone()
277     */
278    public function __clone()
279    {
280        parent::__clone();
281        foreach ($this->bodySigners as $key => $bodySigner) {
282            $this->bodySigners[$key] = clone $bodySigner;
283        }
284
285        foreach ($this->headerSigners as $key => $headerSigner) {
286            $this->headerSigners[$key] = clone $headerSigner;
287        }
288    }
289}
290