~ubuntu-branches/debian/experimental/php-nette/experimental

« back to all changes in this revision

Viewing changes to Nette-2.0.13/Nette/Mail/MimePart.php

  • Committer: Package Import Robot
  • Author(s): David Prévot
  • Date: 2013-11-30 08:47:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20131130084754-4udf1xsu9085tnfc
Tags: 2.1.0~rc-1
* New upstream branch
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
 
3
 
/**
4
 
 * This file is part of the Nette Framework (http://nette.org)
5
 
 *
6
 
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7
 
 *
8
 
 * For the full copyright and license information, please view
9
 
 * the file license.txt that was distributed with this source code.
10
 
 */
11
 
 
12
 
namespace Nette\Mail;
13
 
 
14
 
use Nette,
15
 
        Nette\Utils\Strings;
16
 
 
17
 
 
18
 
/**
19
 
 * MIME message part.
20
 
 *
21
 
 * @author     David Grudl
22
 
 *
23
 
 * @property-read array $headers
24
 
 * @property-write $contentType
25
 
 * @property   string $encoding
26
 
 * @property   mixed $body
27
 
 */
28
 
class MimePart extends Nette\Object
29
 
{
30
 
        /** encoding */
31
 
        const ENCODING_BASE64 = 'base64',
32
 
                ENCODING_7BIT = '7bit',
33
 
                ENCODING_8BIT = '8bit',
34
 
                ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
35
 
 
36
 
        /** @internal */
37
 
        const EOL = "\r\n";
38
 
        const LINE_LENGTH = 76;
39
 
 
40
 
        /** @var array */
41
 
        private $headers = array();
42
 
 
43
 
        /** @var array */
44
 
        private $parts = array();
45
 
 
46
 
        /** @var string */
47
 
        private $body;
48
 
 
49
 
 
50
 
        /**
51
 
         * Sets a header.
52
 
         * @param  string
53
 
         * @param  string|array  value or pair email => name
54
 
         * @param  bool
55
 
         * @return self
56
 
         */
57
 
        public function setHeader($name, $value, $append = FALSE)
58
 
        {
59
 
                if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
60
 
                        throw new Nette\InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
61
 
                }
62
 
 
63
 
                if ($value == NULL) { // intentionally ==
64
 
                        if (!$append) {
65
 
                                unset($this->headers[$name]);
66
 
                        }
67
 
 
68
 
                } elseif (is_array($value)) { // email
69
 
                        $tmp = & $this->headers[$name];
70
 
                        if (!$append || !is_array($tmp)) {
71
 
                                $tmp = array();
72
 
                        }
73
 
 
74
 
                        foreach ($value as $email => $recipient) {
75
 
                                if ($recipient !== NULL && !Strings::checkEncoding($recipient)) {
76
 
                                        Nette\Utils\Validators::assert($recipient, 'unicode', "header '$name'");
77
 
                                }
78
 
                                if (preg_match('#[\r\n]#', $recipient)) {
79
 
                                        throw new Nette\InvalidArgumentException("Name must not contain line separator.");
80
 
                                }
81
 
                                Nette\Utils\Validators::assert($email, 'email', "header '$name'");
82
 
                                $tmp[$email] = $recipient;
83
 
                        }
84
 
 
85
 
                } else {
86
 
                        $value = (string) $value;
87
 
                        if (!Strings::checkEncoding($value)) {
88
 
                                throw new Nette\InvalidArgumentException("Header is not valid UTF-8 string.");
89
 
                        }
90
 
                        $this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
91
 
                }
92
 
                return $this;
93
 
        }
94
 
 
95
 
 
96
 
        /**
97
 
         * Returns a header.
98
 
         * @param  string
99
 
         * @return mixed
100
 
         */
101
 
        public function getHeader($name)
102
 
        {
103
 
                return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
104
 
        }
105
 
 
106
 
 
107
 
        /**
108
 
         * Removes a header.
109
 
         * @param  string
110
 
         * @return self
111
 
         */
112
 
        public function clearHeader($name)
113
 
        {
114
 
                unset($this->headers[$name]);
115
 
                return $this;
116
 
        }
117
 
 
118
 
 
119
 
        /**
120
 
         * Returns an encoded header.
121
 
         * @param  string
122
 
         * @param  string
123
 
         * @return string
124
 
         */
125
 
        public function getEncodedHeader($name)
126
 
        {
127
 
                $offset = strlen($name) + 2; // colon + space
128
 
 
129
 
                if (!isset($this->headers[$name])) {
130
 
                        return NULL;
131
 
 
132
 
                } elseif (is_array($this->headers[$name])) {
133
 
                        $s = '';
134
 
                        foreach ($this->headers[$name] as $email => $name) {
135
 
                                if ($name != NULL) { // intentionally ==
136
 
                                        $s .= self::encodeHeader($name, $offset, strpbrk($name, '.,;<@>()[]"=?'));
137
 
                                        $email = " <$email>";
138
 
                                }
139
 
                                $email .= ',';
140
 
                                if ($s !== '' && $offset + strlen($email) > self::LINE_LENGTH) {
141
 
                                        $s .= self::EOL . "\t";
142
 
                                        $offset = 1;
143
 
                                }
144
 
                                $s .= $email;
145
 
                                $offset += strlen($email);
146
 
                        }
147
 
                        return substr($s, 0, -1); // last comma
148
 
 
149
 
                } elseif (preg_match('#^(\S+; (?:file)?name=)"(.*)"\z#', $this->headers[$name], $m)) { // Content-Disposition
150
 
                        $offset += strlen($m[1]);
151
 
                        return $m[1] . '"' . self::encodeHeader($m[2], $offset) . '"';
152
 
 
153
 
                } else {
154
 
                        return self::encodeHeader($this->headers[$name], $offset);
155
 
                }
156
 
        }
157
 
 
158
 
 
159
 
        /**
160
 
         * Returns all headers.
161
 
         * @return array
162
 
         */
163
 
        public function getHeaders()
164
 
        {
165
 
                return $this->headers;
166
 
        }
167
 
 
168
 
 
169
 
        /**
170
 
         * Sets Content-Type header.
171
 
         * @param  string
172
 
         * @param  string
173
 
         * @return self
174
 
         */
175
 
        public function setContentType($contentType, $charset = NULL)
176
 
        {
177
 
                $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
178
 
                return $this;
179
 
        }
180
 
 
181
 
 
182
 
        /**
183
 
         * Sets Content-Transfer-Encoding header.
184
 
         * @param  string
185
 
         * @return self
186
 
         */
187
 
        public function setEncoding($encoding)
188
 
        {
189
 
                $this->setHeader('Content-Transfer-Encoding', $encoding);
190
 
                return $this;
191
 
        }
192
 
 
193
 
 
194
 
        /**
195
 
         * Returns Content-Transfer-Encoding header.
196
 
         * @return string
197
 
         */
198
 
        public function getEncoding()
199
 
        {
200
 
                return $this->getHeader('Content-Transfer-Encoding');
201
 
        }
202
 
 
203
 
 
204
 
        /**
205
 
         * Adds or creates new multipart.
206
 
         * @return MimePart
207
 
         */
208
 
        public function addPart(MimePart $part = NULL)
209
 
        {
210
 
                return $this->parts[] = $part === NULL ? new self : $part;
211
 
        }
212
 
 
213
 
 
214
 
        /**
215
 
         * Sets textual body.
216
 
         * @return self
217
 
         */
218
 
        public function setBody($body)
219
 
        {
220
 
                $this->body = $body;
221
 
                return $this;
222
 
        }
223
 
 
224
 
 
225
 
        /**
226
 
         * Gets textual body.
227
 
         * @return mixed
228
 
         */
229
 
        public function getBody()
230
 
        {
231
 
                return $this->body;
232
 
        }
233
 
 
234
 
 
235
 
        /********************* building ****************d*g**/
236
 
 
237
 
 
238
 
        /**
239
 
         * Returns encoded message.
240
 
         * @return string
241
 
         */
242
 
        public function generateMessage()
243
 
        {
244
 
                $output = '';
245
 
                $boundary = '--------' . Strings::random();
246
 
 
247
 
                foreach ($this->headers as $name => $value) {
248
 
                        $output .= $name . ': ' . $this->getEncodedHeader($name);
249
 
                        if ($this->parts && $name === 'Content-Type') {
250
 
                                $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
251
 
                        }
252
 
                        $output .= self::EOL;
253
 
                }
254
 
                $output .= self::EOL;
255
 
 
256
 
                $body = (string) $this->body;
257
 
                if ($body !== '') {
258
 
                        switch ($this->getEncoding()) {
259
 
                                case self::ENCODING_QUOTED_PRINTABLE:
260
 
                                        $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
261
 
                                        break;
262
 
 
263
 
                                case self::ENCODING_BASE64:
264
 
                                        $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
265
 
                                        break;
266
 
 
267
 
                                case self::ENCODING_7BIT:
268
 
                                        $body = preg_replace('#[\x80-\xFF]+#', '', $body);
269
 
                                        // break intentionally omitted
270
 
 
271
 
                                case self::ENCODING_8BIT:
272
 
                                        $body = str_replace(array("\x00", "\r"), '', $body);
273
 
                                        $body = str_replace("\n", self::EOL, $body);
274
 
                                        $output .= $body;
275
 
                                        break;
276
 
 
277
 
                                default:
278
 
                                        throw new Nette\InvalidStateException('Unknown encoding.');
279
 
                        }
280
 
                }
281
 
 
282
 
                if ($this->parts) {
283
 
                        if (substr($output, -strlen(self::EOL)) !== self::EOL) {
284
 
                                $output .= self::EOL;
285
 
                        }
286
 
                        foreach ($this->parts as $part) {
287
 
                                $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
288
 
                        }
289
 
                        $output .= '--' . $boundary.'--';
290
 
                }
291
 
 
292
 
                return $output;
293
 
        }
294
 
 
295
 
 
296
 
        /********************* QuotedPrintable helpers ****************d*g**/
297
 
 
298
 
 
299
 
        /**
300
 
         * Converts a 8 bit header to a string.
301
 
         * @param  string
302
 
         * @param  int
303
 
         * @param  bool
304
 
         * @return string
305
 
         */
306
 
        private static function encodeHeader($s, & $offset = 0, $force = FALSE)
307
 
        {
308
 
                $o = '';
309
 
                if ($offset >= 55) { // maximum for iconv_mime_encode
310
 
                        $o = self::EOL . "\t";
311
 
                        $offset = 1;
312
 
                }
313
 
 
314
 
                if (!$force && strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}=? _\r\n\t") === strlen($s)
315
 
                        && ($offset + strlen($s) <= self::LINE_LENGTH)
316
 
                ) {
317
 
                        $offset += strlen($s);
318
 
                        return $o . $s;
319
 
                }
320
 
 
321
 
                $o .= str_replace("\n ", "\n\t", substr(iconv_mime_encode(str_repeat(' ', $offset), $s, array(
322
 
                        'scheme' => 'B', // Q is broken
323
 
                        'input-charset' => 'UTF-8',
324
 
                        'output-charset' => 'UTF-8',
325
 
                )), $offset + 2));
326
 
 
327
 
                $offset = strlen($o) - strrpos($o, "\n");
328
 
                return $o;
329
 
        }
330
 
 
331
 
 
332
 
        /**
333
 
         * Converts a 8 bit string to a quoted-printable string.
334
 
         * @param  string
335
 
         * @return string
336
 
         */}