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