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 default email message class.
13 *
14 * @author Chris Corbyn
15 */
16class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime_Message
17{
18    const PRIORITY_HIGHEST = 1;
19    const PRIORITY_HIGH = 2;
20    const PRIORITY_NORMAL = 3;
21    const PRIORITY_LOW = 4;
22    const PRIORITY_LOWEST = 5;
23
24    /**
25     * Create a new SimpleMessage with $headers, $encoder and $cache.
26     *
27     * @param Swift_Mime_HeaderSet      $headers
28     * @param Swift_Mime_ContentEncoder $encoder
29     * @param Swift_KeyCache            $cache
30     * @param Swift_Mime_Grammar        $grammar
31     * @param string                    $charset
32     */
33    public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $charset = null)
34    {
35        parent::__construct($headers, $encoder, $cache, $grammar, $charset);
36        $this->getHeaders()->defineOrdering(array(
37            'Return-Path',
38            'Received',
39            'DKIM-Signature',
40            'DomainKey-Signature',
41            'Sender',
42            'Message-ID',
43            'Date',
44            'Subject',
45            'From',
46            'Reply-To',
47            'To',
48            'Cc',
49            'Bcc',
50            'MIME-Version',
51            'Content-Type',
52            'Content-Transfer-Encoding',
53            ));
54        $this->getHeaders()->setAlwaysDisplayed(array('Date', 'Message-ID', 'From'));
55        $this->getHeaders()->addTextHeader('MIME-Version', '1.0');
56        $this->setDate(time());
57        $this->setId($this->getId());
58        $this->getHeaders()->addMailboxHeader('From');
59    }
60
61    /**
62     * Always returns {@link LEVEL_TOP} for a message instance.
63     *
64     * @return int
65     */
66    public function getNestingLevel()
67    {
68        return self::LEVEL_TOP;
69    }
70
71    /**
72     * Set the subject of this message.
73     *
74     * @param string $subject
75     *
76     * @return $this
77     */
78    public function setSubject($subject)
79    {
80        if (!$this->_setHeaderFieldModel('Subject', $subject)) {
81            $this->getHeaders()->addTextHeader('Subject', $subject);
82        }
83
84        return $this;
85    }
86
87    /**
88     * Get the subject of this message.
89     *
90     * @return string
91     */
92    public function getSubject()
93    {
94        return $this->_getHeaderFieldModel('Subject');
95    }
96
97    /**
98     * Set the date at which this message was created.
99     *
100     * @param int $date
101     *
102     * @return $this
103     */
104    public function setDate($date)
105    {
106        if (!$this->_setHeaderFieldModel('Date', $date)) {
107            $this->getHeaders()->addDateHeader('Date', $date);
108        }
109
110        return $this;
111    }
112
113    /**
114     * Get the date at which this message was created.
115     *
116     * @return int
117     */
118    public function getDate()
119    {
120        return $this->_getHeaderFieldModel('Date');
121    }
122
123    /**
124     * Set the return-path (the bounce address) of this message.
125     *
126     * @param string $address
127     *
128     * @return $this
129     */
130    public function setReturnPath($address)
131    {
132        if (!$this->_setHeaderFieldModel('Return-Path', $address)) {
133            $this->getHeaders()->addPathHeader('Return-Path', $address);
134        }
135
136        return $this;
137    }
138
139    /**
140     * Get the return-path (bounce address) of this message.
141     *
142     * @return string
143     */
144    public function getReturnPath()
145    {
146        return $this->_getHeaderFieldModel('Return-Path');
147    }
148
149    /**
150     * Set the sender of this message.
151     *
152     * This does not override the From field, but it has a higher significance.
153     *
154     * @param string $address
155     * @param string $name    optional
156     *
157     * @return $this
158     */
159    public function setSender($address, $name = null)
160    {
161        if (!is_array($address) && isset($name)) {
162            $address = array($address => $name);
163        }
164
165        if (!$this->_setHeaderFieldModel('Sender', (array) $address)) {
166            $this->getHeaders()->addMailboxHeader('Sender', (array) $address);
167        }
168
169        return $this;
170    }
171
172    /**
173     * Get the sender of this message.
174     *
175     * @return string
176     */
177    public function getSender()
178    {
179        return $this->_getHeaderFieldModel('Sender');
180    }
181
182    /**
183     * Add a From: address to this message.
184     *
185     * If $name is passed this name will be associated with the address.
186     *
187     * @param string $address
188     * @param string $name    optional
189     *
190     * @return $this
191     */
192    public function addFrom($address, $name = null)
193    {
194        $current = $this->getFrom();
195        $current[$address] = $name;
196
197        return $this->setFrom($current);
198    }
199
200    /**
201     * Set the from address of this message.
202     *
203     * You may pass an array of addresses if this message is from multiple people.
204     *
205     * If $name is passed and the first parameter is a string, this name will be
206     * associated with the address.
207     *
208     * @param string|array $addresses
209     * @param string       $name      optional
210     *
211     * @return $this
212     */
213    public function setFrom($addresses, $name = null)
214    {
215        if (!is_array($addresses) && isset($name)) {
216            $addresses = array($addresses => $name);
217        }
218
219        if (!$this->_setHeaderFieldModel('From', (array) $addresses)) {
220            $this->getHeaders()->addMailboxHeader('From', (array) $addresses);
221        }
222
223        return $this;
224    }
225
226    /**
227     * Get the from address of this message.
228     *
229     * @return mixed
230     */
231    public function getFrom()
232    {
233        return $this->_getHeaderFieldModel('From');
234    }
235
236    /**
237     * Add a Reply-To: address to this message.
238     *
239     * If $name is passed this name will be associated with the address.
240     *
241     * @param string $address
242     * @param string $name    optional
243     *
244     * @return $this
245     */
246    public function addReplyTo($address, $name = null)
247    {
248        $current = $this->getReplyTo();
249        $current[$address] = $name;
250
251        return $this->setReplyTo($current);
252    }
253
254    /**
255     * Set the reply-to address of this message.
256     *
257     * You may pass an array of addresses if replies will go to multiple people.
258     *
259     * If $name is passed and the first parameter is a string, this name will be
260     * associated with the address.
261     *
262     * @param mixed  $addresses
263     * @param string $name      optional
264     *
265     * @return $this
266     */
267    public function setReplyTo($addresses, $name = null)
268    {
269        if (!is_array($addresses) && isset($name)) {
270            $addresses = array($addresses => $name);
271        }
272
273        if (!$this->_setHeaderFieldModel('Reply-To', (array) $addresses)) {
274            $this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses);
275        }
276
277        return $this;
278    }
279
280    /**
281     * Get the reply-to address of this message.
282     *
283     * @return string
284     */
285    public function getReplyTo()
286    {
287        return $this->_getHeaderFieldModel('Reply-To');
288    }
289
290    /**
291     * Add a To: address to this message.
292     *
293     * If $name is passed this name will be associated with the address.
294     *
295     * @param string $address
296     * @param string $name    optional
297     *
298     * @return $this
299     */
300    public function addTo($address, $name = null)
301    {
302        $current = $this->getTo();
303        $current[$address] = $name;
304
305        return $this->setTo($current);
306    }
307
308    /**
309     * Set the to addresses of this message.
310     *
311     * If multiple recipients will receive the message an array should be used.
312     * Example: array('receiver@domain.org', 'other@domain.org' => 'A name')
313     *
314     * If $name is passed and the first parameter is a string, this name will be
315     * associated with the address.
316     *
317     * @param mixed  $addresses
318     * @param string $name      optional
319     *
320     * @return $this
321     */
322    public function setTo($addresses, $name = null)
323    {
324        if (!is_array($addresses) && isset($name)) {
325            $addresses = array($addresses => $name);
326        }
327
328        if (!$this->_setHeaderFieldModel('To', (array) $addresses)) {
329            $this->getHeaders()->addMailboxHeader('To', (array) $addresses);
330        }
331
332        return $this;
333    }
334
335    /**
336     * Get the To addresses of this message.
337     *
338     * @return array
339     */
340    public function getTo()
341    {
342        return $this->_getHeaderFieldModel('To');
343    }
344
345    /**
346     * Add a Cc: address to this message.
347     *
348     * If $name is passed this name will be associated with the address.
349     *
350     * @param string $address
351     * @param string $name    optional
352     *
353     * @return $this
354     */
355    public function addCc($address, $name = null)
356    {
357        $current = $this->getCc();
358        $current[$address] = $name;
359
360        return $this->setCc($current);
361    }
362
363    /**
364     * Set the Cc addresses of this message.
365     *
366     * If $name is passed and the first parameter is a string, this name will be
367     * associated with the address.
368     *
369     * @param mixed  $addresses
370     * @param string $name      optional
371     *
372     * @return $this
373     */
374    public function setCc($addresses, $name = null)
375    {
376        if (!is_array($addresses) && isset($name)) {
377            $addresses = array($addresses => $name);
378        }
379
380        if (!$this->_setHeaderFieldModel('Cc', (array) $addresses)) {
381            $this->getHeaders()->addMailboxHeader('Cc', (array) $addresses);
382        }
383
384        return $this;
385    }
386
387    /**
388     * Get the Cc address of this message.
389     *
390     * @return array
391     */
392    public function getCc()
393    {
394        return $this->_getHeaderFieldModel('Cc');
395    }
396
397    /**
398     * Add a Bcc: address to this message.
399     *
400     * If $name is passed this name will be associated with the address.
401     *
402     * @param string $address
403     * @param string $name    optional
404     *
405     * @return $this
406     */
407    public function addBcc($address, $name = null)
408    {
409        $current = $this->getBcc();
410        $current[$address] = $name;
411
412        return $this->setBcc($current);
413    }
414
415    /**
416     * Set the Bcc addresses of this message.
417     *
418     * If $name is passed and the first parameter is a string, this name will be
419     * associated with the address.
420     *
421     * @param mixed  $addresses
422     * @param string $name      optional
423     *
424     * @return $this
425     */
426    public function setBcc($addresses, $name = null)
427    {
428        if (!is_array($addresses) && isset($name)) {
429            $addresses = array($addresses => $name);
430        }
431
432        if (!$this->_setHeaderFieldModel('Bcc', (array) $addresses)) {
433            $this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses);
434        }
435
436        return $this;
437    }
438
439    /**
440     * Get the Bcc addresses of this message.
441     *
442     * @return array
443     */
444    public function getBcc()
445    {
446        return $this->_getHeaderFieldModel('Bcc');
447    }
448
449    /**
450     * Set the priority of this message.
451     *
452     * The value is an integer where 1 is the highest priority and 5 is the lowest.
453     *
454     * @param int $priority
455     *
456     * @return $this
457     */
458    public function setPriority($priority)
459    {
460        $priorityMap = array(
461            self::PRIORITY_HIGHEST => 'Highest',
462            self::PRIORITY_HIGH => 'High',
463            self::PRIORITY_NORMAL => 'Normal',
464            self::PRIORITY_LOW => 'Low',
465            self::PRIORITY_LOWEST => 'Lowest',
466            );
467        $pMapKeys = array_keys($priorityMap);
468        if ($priority > max($pMapKeys)) {
469            $priority = max($pMapKeys);
470        } elseif ($priority < min($pMapKeys)) {
471            $priority = min($pMapKeys);
472        }
473        if (!$this->_setHeaderFieldModel('X-Priority',
474            sprintf('%d (%s)', $priority, $priorityMap[$priority]))) {
475            $this->getHeaders()->addTextHeader('X-Priority',
476                sprintf('%d (%s)', $priority, $priorityMap[$priority]));
477        }
478
479        return $this;
480    }
481
482    /**
483     * Get the priority of this message.
484     *
485     * The returned value is an integer where 1 is the highest priority and 5
486     * is the lowest.
487     *
488     * @return int
489     */
490    public function getPriority()
491    {
492        list($priority) = sscanf($this->_getHeaderFieldModel('X-Priority'),
493            '%[1-5]'
494            );
495
496        return isset($priority) ? $priority : 3;
497    }
498
499    /**
500     * Ask for a delivery receipt from the recipient to be sent to $addresses.
501     *
502     * @param array $addresses
503     *
504     * @return $this
505     */
506    public function setReadReceiptTo($addresses)
507    {
508        if (!$this->_setHeaderFieldModel('Disposition-Notification-To', $addresses)) {
509            $this->getHeaders()
510                ->addMailboxHeader('Disposition-Notification-To', $addresses);
511        }
512
513        return $this;
514    }
515
516    /**
517     * Get the addresses to which a read-receipt will be sent.
518     *
519     * @return string
520     */
521    public function getReadReceiptTo()
522    {
523        return $this->_getHeaderFieldModel('Disposition-Notification-To');
524    }
525
526    /**
527     * Attach a {@link Swift_Mime_MimeEntity} such as an Attachment or MimePart.
528     *
529     * @param Swift_Mime_MimeEntity $entity
530     *
531     * @return $this
532     */
533    public function attach(Swift_Mime_MimeEntity $entity)
534    {
535        $this->setChildren(array_merge($this->getChildren(), array($entity)));
536
537        return $this;
538    }
539
540    /**
541     * Remove an already attached entity.
542     *
543     * @param Swift_Mime_MimeEntity $entity
544     *
545     * @return $this
546     */
547    public function detach(Swift_Mime_MimeEntity $entity)
548    {
549        $newChildren = array();
550        foreach ($this->getChildren() as $child) {
551            if ($entity !== $child) {
552                $newChildren[] = $child;
553            }
554        }
555        $this->setChildren($newChildren);
556
557        return $this;
558    }
559
560    /**
561     * Attach a {@link Swift_Mime_MimeEntity} and return it's CID source.
562     * This method should be used when embedding images or other data in a message.
563     *
564     * @param Swift_Mime_MimeEntity $entity
565     *
566     * @return string
567     */
568    public function embed(Swift_Mime_MimeEntity $entity)
569    {
570        $this->attach($entity);
571
572        return 'cid:'.$entity->getId();
573    }
574
575    /**
576     * Get this message as a complete string.
577     *
578     * @return string
579     */
580    public function toString()
581    {
582        if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') {
583            $this->setChildren(array_merge(array($this->_becomeMimePart()), $children));
584            $string = parent::toString();
585            $this->setChildren($children);
586        } else {
587            $string = parent::toString();
588        }
589
590        return $string;
591    }
592
593    /**
594     * Returns a string representation of this object.
595     *
596     * @see toString()
597     *
598     * @return string
599     */
600    public function __toString()
601    {
602        return $this->toString();
603    }
604
605    /**
606     * Write this message to a {@link Swift_InputByteStream}.
607     *
608     * @param Swift_InputByteStream $is
609     */
610    public function toByteStream(Swift_InputByteStream $is)
611    {
612        if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') {
613            $this->setChildren(array_merge(array($this->_becomeMimePart()), $children));
614            parent::toByteStream($is);
615            $this->setChildren($children);
616        } else {
617            parent::toByteStream($is);
618        }
619    }
620
621    /** @see Swift_Mime_SimpleMimeEntity::_getIdField() */
622    protected function _getIdField()
623    {
624        return 'Message-ID';
625    }
626
627    /** Turn the body of this message into a child of itself if needed */
628    protected function _becomeMimePart()
629    {
630        $part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(),
631            $this->_getCache(), $this->_getGrammar(), $this->_userCharset
632            );
633        $part->setContentType($this->_userContentType);
634        $part->setBody($this->getBody());
635        $part->setFormat($this->_userFormat);
636        $part->setDelSp($this->_userDelSp);
637        $part->_setNestingLevel($this->_getTopNestingLevel());
638
639        return $part;
640    }
641
642    /** Get the highest nesting level nested inside this message */
643    private function _getTopNestingLevel()
644    {
645        $highestLevel = $this->getNestingLevel();
646        foreach ($this->getChildren() as $child) {
647            $childLevel = $child->getNestingLevel();
648            if ($highestLevel < $childLevel) {
649                $highestLevel = $childLevel;
650            }
651        }
652
653        return $highestLevel;
654    }
655}
656