1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Mail;
13:
14: use Nette,
15: Nette\Utils\Strings;
16:
17:
18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
28: class MimePart extends Nette\Object
29: {
30:
31: const ENCODING_BASE64 = 'base64',
32: ENCODING_7BIT = '7bit',
33: ENCODING_8BIT = '8bit',
34: ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
35:
36:
37: const EOL = "\r\n";
38: const LINE_LENGTH = 76;
39:
40:
41: private = array();
42:
43:
44: private $parts = array();
45:
46:
47: private $body;
48:
49:
50: 51: 52: 53: 54: 55: 56:
57: public function ($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) {
64: if (!$append) {
65: unset($this->headers[$name]);
66: }
67:
68: } elseif (is_array($value)) {
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: 98: 99: 100:
101: public function ($name)
102: {
103: return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
104: }
105:
106:
107: 108: 109: 110: 111:
112: public function ($name)
113: {
114: unset($this->headers[$name]);
115: return $this;
116: }
117:
118:
119: 120: 121: 122: 123: 124:
125: public function ($name)
126: {
127: $offset = strlen($name) + 2;
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) {
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);
148:
149: } elseif (preg_match('#^(\S+; (?:file)?name=)"(.*)"\z#', $this->headers[$name], $m)) {
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: 161: 162:
163: public function ()
164: {
165: return $this->headers;
166: }
167:
168:
169: 170: 171: 172: 173: 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: 184: 185: 186:
187: public function setEncoding($encoding)
188: {
189: $this->setHeader('Content-Transfer-Encoding', $encoding);
190: return $this;
191: }
192:
193:
194: 195: 196: 197:
198: public function getEncoding()
199: {
200: return $this->getHeader('Content-Transfer-Encoding');
201: }
202:
203:
204: 205: 206: 207:
208: public function addPart(MimePart $part = NULL)
209: {
210: return $this->parts[] = $part === NULL ? new self : $part;
211: }
212:
213:
214: 215: 216: 217:
218: public function setBody($body)
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 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:
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:
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: 333: 334: 335: 336: }
337: