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, TRUE);
133: $email = " <$email>";
134: }
135: $s .= self::append($email . ',', $offset);
136: }
137: return ltrim(substr($s, 0, -1));
138:
139: } elseif (preg_match('#^(\S+; (?:file)?name=)"(.*)"\z#', $this->headers[$name], $m)) {
140: $offset += strlen($m[1]);
141: return $m[1] . '"' . self::encodeHeader($m[2], $offset) . '"';
142:
143: } else {
144: return ltrim(self::encodeHeader($this->headers[$name], $offset));
145: }
146: }
147:
148:
149: 150: 151: 152:
153: public function ()
154: {
155: return $this->headers;
156: }
157:
158:
159: 160: 161: 162: 163: 164:
165: public function setContentType($contentType, $charset = NULL)
166: {
167: $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
168: return $this;
169: }
170:
171:
172: 173: 174: 175: 176:
177: public function setEncoding($encoding)
178: {
179: $this->setHeader('Content-Transfer-Encoding', $encoding);
180: return $this;
181: }
182:
183:
184: 185: 186: 187:
188: public function getEncoding()
189: {
190: return $this->getHeader('Content-Transfer-Encoding');
191: }
192:
193:
194: 195: 196: 197:
198: public function addPart(MimePart $part = NULL)
199: {
200: return $this->parts[] = $part === NULL ? new self : $part;
201: }
202:
203:
204: 205: 206: 207:
208: public function setBody($body)
209: {
210: $this->body = $body;
211: return $this;
212: }
213:
214:
215: 216: 217: 218:
219: public function getBody()
220: {
221: return $this->body;
222: }
223:
224:
225:
226:
227:
228: 229: 230: 231:
232: public function generateMessage()
233: {
234: $output = '';
235: $boundary = '--------' . Strings::random();
236:
237: foreach ($this->headers as $name => $value) {
238: $output .= $name . ': ' . $this->getEncodedHeader($name);
239: if ($this->parts && $name === 'Content-Type') {
240: $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
241: }
242: $output .= self::EOL;
243: }
244: $output .= self::EOL;
245:
246: $body = (string) $this->body;
247: if ($body !== '') {
248: switch ($this->getEncoding()) {
249: case self::ENCODING_QUOTED_PRINTABLE:
250: $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
251: break;
252:
253: case self::ENCODING_BASE64:
254: $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
255: break;
256:
257: case self::ENCODING_7BIT:
258: $body = preg_replace('#[\x80-\xFF]+#', '', $body);
259:
260:
261: case self::ENCODING_8BIT:
262: $body = str_replace(array("\x00", "\r"), '', $body);
263: $body = str_replace("\n", self::EOL, $body);
264: $output .= $body;
265: break;
266:
267: default:
268: throw new Nette\InvalidStateException('Unknown encoding.');
269: }
270: }
271:
272: if ($this->parts) {
273: if (substr($output, -strlen(self::EOL)) !== self::EOL) {
274: $output .= self::EOL;
275: }
276: foreach ($this->parts as $part) {
277: $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
278: }
279: $output .= '--' . $boundary.'--';
280: }
281:
282: return $output;
283: }
284:
285:
286:
287:
288:
289: 290: 291: 292: 293: 294: 295:
296: private static function ($s, & $offset = 0, $quotes = FALSE)
297: {
298: if (strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}~=? _\r\n\t") === strlen($s)) {
299: if ($quotes && preg_match('#[^ a-zA-Z0-9!\#$%&\'*+/?^_`{|}~-]#', $s)) {
300: return self::append('"' . addcslashes($s, '"\\') . '"', $offset);
301: }
302: return self::append($s, $offset);
303: }
304:
305: $o = '';
306: if ($offset >= 55) {
307: $o = self::EOL . "\t";
308: $offset = 1;
309: }
310:
311: $s = iconv_mime_encode(str_repeat(' ', $old = $offset), $s, array(
312: 'scheme' => 'B',
313: 'input-charset' => 'UTF-8',
314: 'output-charset' => 'UTF-8',
315: ));
316:
317: $offset = strlen($s) - strrpos($s, "\n");
318: $s = str_replace("\n ", "\n\t", substr($s, $old + 2));
319: return $o . $s;
320: }
321:
322:
323: private static function append($s, & $offset = 0)
324: {
325: if ($offset + strlen($s) > self::LINE_LENGTH) {
326: $offset = 1;
327: $s = self::EOL . "\t" . $s;
328:
329: }
330: $offset += strlen($s);
331: return $s;
332: }
333:
334:
335: 336: 337: 338: 339: }
340: