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