1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Mail;
9:
10: use Nette,
11: Nette\Utils\Strings;
12:
13:
14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
24: class MimePart extends Nette\Object
25: {
26:
27: const ENCODING_BASE64 = 'base64',
28: ENCODING_7BIT = '7bit',
29: ENCODING_8BIT = '8bit',
30: ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
31:
32:
33: const EOL = "\r\n";
34: const LINE_LENGTH = 76;
35:
36:
37: private = array();
38:
39:
40: private $parts = array();
41:
42:
43: private $body;
44:
45:
46: 47: 48: 49: 50: 51: 52:
53: public function ($name, $value, $append = FALSE)
54: {
55: if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
56: throw new Nette\InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
57: }
58:
59: if ($value == NULL) {
60: if (!$append) {
61: unset($this->headers[$name]);
62: }
63:
64: } elseif (is_array($value)) {
65: $tmp = & $this->headers[$name];
66: if (!$append || !is_array($tmp)) {
67: $tmp = array();
68: }
69:
70: foreach ($value as $email => $recipient) {
71: if ($recipient !== NULL && !Strings::checkEncoding($recipient)) {
72: Nette\Utils\Validators::assert($recipient, 'unicode', "header '$name'");
73: }
74: if (preg_match('#[\r\n]#', $recipient)) {
75: throw new Nette\InvalidArgumentException('Name must not contain line separator.');
76: }
77: Nette\Utils\Validators::assert($email, 'email', "header '$name'");
78: $tmp[$email] = $recipient;
79: }
80:
81: } else {
82: $value = (string) $value;
83: if (!Strings::checkEncoding($value)) {
84: throw new Nette\InvalidArgumentException('Header is not valid UTF-8 string.');
85: }
86: $this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
87: }
88: return $this;
89: }
90:
91:
92: 93: 94: 95: 96:
97: public function ($name)
98: {
99: return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
100: }
101:
102:
103: 104: 105: 106: 107:
108: public function ($name)
109: {
110: unset($this->headers[$name]);
111: return $this;
112: }
113:
114:
115: 116: 117: 118: 119: 120:
121: public function ($name)
122: {
123: $offset = strlen($name) + 2;
124:
125: if (!isset($this->headers[$name])) {
126: return NULL;
127:
128: } elseif (is_array($this->headers[$name])) {
129: $s = '';
130: foreach ($this->headers[$name] as $email => $name) {
131: if ($name != NULL) {
132: $s .= self::encodeHeader($name, $offset, strpbrk($name, '.,;<@>()[]"=?'));
133: $email = " <$email>";
134: }
135: $email .= ',';
136: if ($s !== '' && $offset + strlen($email) > self::LINE_LENGTH) {
137: $s .= self::EOL . "\t";
138: $offset = 1;
139: }
140: $s .= $email;
141: $offset += strlen($email);
142: }
143: return substr($s, 0, -1);
144:
145: } elseif (preg_match('#^(\S+; (?:file)?name=)"(.*)"\z#', $this->headers[$name], $m)) {
146: $offset += strlen($m[1]);
147: return $m[1] . '"' . self::encodeHeader($m[2], $offset) . '"';
148:
149: } else {
150: return self::encodeHeader($this->headers[$name], $offset);
151: }
152: }
153:
154:
155: 156: 157: 158:
159: public function ()
160: {
161: return $this->headers;
162: }
163:
164:
165: 166: 167: 168: 169: 170:
171: public function setContentType($contentType, $charset = NULL)
172: {
173: $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
174: return $this;
175: }
176:
177:
178: 179: 180: 181: 182:
183: public function setEncoding($encoding)
184: {
185: $this->setHeader('Content-Transfer-Encoding', $encoding);
186: return $this;
187: }
188:
189:
190: 191: 192: 193:
194: public function getEncoding()
195: {
196: return $this->getHeader('Content-Transfer-Encoding');
197: }
198:
199:
200: 201: 202: 203:
204: public function addPart(MimePart $part = NULL)
205: {
206: return $this->parts[] = $part === NULL ? new self : $part;
207: }
208:
209:
210: 211: 212: 213:
214: public function setBody($body)
215: {
216: if ($body instanceof Nette\Templating\ITemplate || $body instanceof Nette\Application\UI\ITemplate) {
217: $body->mail = $this;
218: $body = $body->__toString(TRUE);
219: }
220: $this->body = $body;
221: return $this;
222: }
223:
224:
225: 226: 227: 228:
229: public function getBody()
230: {
231: return $this->body;
232: }
233:
234:
235:
236:
237:
238: 239: 240: 241:
242: public function getEncodedMessage()
243: {
244: $output = '';
245: $boundary = '--------' . Nette\Utils\Random::generate();
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 .= quoted_printable_encode($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:
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->getEncodedMessage() . self::EOL;
288: }
289: $output .= '--' . $boundary.'--';
290: }
291:
292: return $output;
293: }
294:
295:
296:
297:
298:
299: 300: 301: 302: 303: 304: 305:
306: private static function ($s, & $offset = 0, $force = FALSE)
307: {
308: $o = '';
309: if ($offset >= 55) {
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',
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: