1<?php namespace Tx\Mailer;
2/***************************************************\
3 *
4 *  Mailer (https://github.com/txthinking/Mailer)
5 *
6 *  A lightweight PHP SMTP mail sender.
7 *  Implement RFC0821, RFC0822, RFC1869, RFC2045, RFC2821
8 *
9 *  Support html body, don't worry that the receiver's
10 *  mail client can't support html, because Mailer will
11 *  send both text/plain and text/html body, so if the
12 *  mail client can't support html, it will display the
13 *  text/plain body.
14 *
15 *  Create Date 2012-07-25.
16 *  Under the MIT license.
17 *
18 \***************************************************/
19class Message
20{
21    /**
22     * from name
23     */
24    protected $fromName;
25
26    /**
27     * from email
28     */
29    protected $fromEmail;
30
31    /**
32     * fake from name
33     */
34    protected $fakeFromName;
35
36    /**
37     * fake from email
38     */
39    protected $fakeFromEmail;
40
41    /**
42     * to email
43     */
44    protected $to = array();
45
46    /**
47     * mail subject
48     */
49    protected $subject;
50
51    /**
52     * mail body
53     */
54    protected $body;
55
56    /**
57     *mail attachment
58     */
59    protected $attachment = array();
60
61    /**
62     * message header
63     */
64    protected $header = array();
65
66    /**
67     * charset
68     */
69    protected $charset = "UTF-8";
70
71    /**
72     * header multipart boundaryMixed
73     */
74    protected $boundaryMixed;
75
76    /**
77     * header multipart alternative
78     */
79    protected $boundaryAlternative;
80
81    /**
82     * $this->CRLF
83     * @var string
84     */
85    protected $CRLF = "\r\n";
86
87    /**
88     * set mail from
89     * @param string $name
90     * @param string $email
91     * @return $this
92     */
93    public function setFrom($name, $email)
94    {
95        $this->fromName = $name;
96        $this->fromEmail = $email;
97        return $this;
98    }
99
100
101    /**
102     * set mail fake from
103     * @param string $name
104     * @param string $email
105     * @return $this
106     */
107    public function setFakeFrom($name, $email)
108    {
109        $this->fakeFromName = $name;
110        $this->fakeFromEmail = $email;
111        return $this;
112    }
113
114    /**
115     * set mail receiver
116     * @param string $name
117     * @param string $email
118     * @return $this
119     */
120    public function setTo($name, $email){
121        $this->to[$name] = $email;
122        return $this;
123    }
124
125    /**
126     * add mail receiver
127     * @param string $name
128     * @param string $email
129     * @return $this
130     */
131    public function addTo($name, $email){
132        $this->to[$name] = $email;
133        return $this;
134    }
135
136    /**
137     * set mail subject
138     * @param string $subject
139     * @return $this
140     */
141    public function setSubject($subject){
142        $this->subject = $subject;
143        return $this;
144    }
145
146    /**
147     * set mail body
148     * @param string $body
149     * @return $this
150     */
151    public function setBody($body){
152        $this->body = $body;
153        return $this;
154    }
155
156    /**
157     * add mail attachment
158     * @param $name
159     * @param $path
160     * @return $this
161     */
162    public function setAttachment($name, $path){
163        $this->attachment[$name] = $path;
164        return $this;
165    }
166
167    /**
168     * add mail attachment
169     * @param $name
170     * @param $path
171     * @return $this
172     */
173    public function addAttachment($name, $path){
174        $this->attachment[$name] = $path;
175        return $this;
176    }
177
178    /**
179     * @return string
180     */
181    public function getFromName()
182    {
183        return $this->fromName;
184    }
185
186    /**
187     * @return string
188     */
189    public function getFromEmail()
190    {
191        return $this->fromEmail;
192    }
193
194
195    /**
196     * @return string
197     */
198    public function getFakeFromName()
199    {
200        return $this->fakeFromName;
201    }
202
203    /**
204     * @return string
205     */
206    public function getFakeFromEmail()
207    {
208        return $this->fakeFromEmail;
209    }
210
211    /**
212     * @return mixed
213     */
214    public function getTo()
215    {
216        return $this->to;
217    }
218
219    /**
220     * @return mixed
221     */
222    public function getSubject()
223    {
224        return $this->subject;
225    }
226
227    /**
228     * @return mixed
229     */
230    public function getBody()
231    {
232        return $this->body;
233    }
234
235    /**
236     * @return array
237     */
238    public function getAttachment()
239    {
240        return $this->attachment;
241    }
242
243    /**
244     * Create mail header
245     * @return $this
246     */
247    protected function createHeader(){
248        $this->header['Date'] = date('r');
249
250        if(!empty($this->fakeFromEmail)){
251            $this->header['Return-Path'] = $this->fakeFromEmail;
252            $this->header['From'] = $this->fakeFromName . " <" . $this->fakeFromEmail . ">";
253        } else{
254            $this->header['Return-Path'] = $this->fromEmail;
255            $this->header['From'] = $this->fromName . " <" . $this->fromEmail .">";
256        }
257
258        $this->header['To'] = '';
259        foreach ($this->to as $toName => $toEmail) {
260            $this->header['To'] .= $toName . " <" . $toEmail . ">, ";
261        }
262        $this->header['To'] = substr($this->header['To'], 0, -2);
263        $this->header['Subject'] = $this->subject;
264        $this->header['Message-ID'] = '<' . md5('TX'.md5(time()).uniqid()) . '@' . $this->fromEmail . '>';
265        $this->header['X-Priority'] = '3';
266        $this->header['X-Mailer'] = 'Mailer (https://github.com/txthinking/Mailer)';
267        $this->header['MIME-Version'] = '1.0';
268        if (!empty($this->attachment)){
269            $this->boundaryMixed = md5(md5(time().'TxMailer').uniqid());
270            $this->header['Content-Type'] = "multipart/mixed; \r\n\tboundary=\"" . $this->boundaryMixed . "\"";
271        }
272        $this->boundaryAlternative = md5(md5(time().'TXMailer').uniqid());
273        return $this;
274    }
275
276    /**
277     * @brief createBody create body
278     *
279     * @return string
280     */
281    protected function createBody(){
282        $in = "";
283        $in .= "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"" . $this->CRLF;
284        $in .= $this->CRLF;
285        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
286        $in .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->CRLF;
287        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
288        $in .= $this->CRLF;
289        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
290        $in .= $this->CRLF;
291        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
292        $in .= "Content-Type: text/html; charset=\"" . $this->charset ."\"" . $this->CRLF;
293        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
294        $in .= $this->CRLF;
295        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
296        $in .= $this->CRLF;
297        $in .= "--" . $this->boundaryAlternative . "--" . $this->CRLF;
298        return $in;
299    }
300
301    /**
302     * @brief createBodyWithAttachment create body with attachment
303     *
304     * @return string
305     */
306    protected function createBodyWithAttachment(){
307        $in = "";
308        $in .= $this->CRLF;
309        $in .= $this->CRLF;
310        $in .= '--' . $this->boundaryMixed . $this->CRLF;
311        $in .= "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"" . $this->CRLF;
312        $in .= $this->CRLF;
313        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
314        $in .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->CRLF;
315        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
316        $in .= $this->CRLF;
317        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
318        $in .= $this->CRLF;
319        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
320        $in .= "Content-Type: text/html; charset=\"" . $this->charset ."\"" . $this->CRLF;
321        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
322        $in .= $this->CRLF;
323        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
324        $in .= $this->CRLF;
325        $in .= "--" . $this->boundaryAlternative . "--" . $this->CRLF;
326        foreach ($this->attachment as $name => $path){
327            $in .= $this->CRLF;
328            $in .= '--' . $this->boundaryMixed . $this->CRLF;
329            $in .= "Content-Type: application/octet-stream; name=\"". $name ."\"" . $this->CRLF;
330            $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
331            $in .= "Content-Disposition: attachment; filename=\"" . $name . "\"" . $this->CRLF;
332            $in .= $this->CRLF;
333            $in .= chunk_split(base64_encode(file_get_contents($path))) . $this->CRLF;
334        }
335        $in .= $this->CRLF;
336        $in .= $this->CRLF;
337        $in .= '--' . $this->boundaryMixed . '--' . $this->CRLF;
338        return $in;
339    }
340
341    public function toString(){
342        $in = '';
343        $this->createHeader();
344        foreach ($this->header as $key => $value) {
345            $in .= $key . ': ' . $value . $this->CRLF;
346        }
347        if (empty($this->attachment)) {
348            $in .= $this->createBody();
349        } else {
350            $in .= $this->createBodyWithAttachment();
351        }
352        $in .= $this->CRLF . $this->CRLF . "." . $this->CRLF;
353        return $in;
354    }
355
356}
357