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: $this->body = $body;
217: return $this;
218: }
219:
220:
221: 222: 223: 224:
225: public function getBody()
226: {
227: return $this->body;
228: }
229:
230:
231:
232:
233:
234: 235: 236: 237:
238: public function generateMessage()
239: {
240: $output = '';
241: $boundary = '--------' . Strings::random();
242:
243: foreach ($this->headers as $name => $value) {
244: $output .= $name . ': ' . $this->getEncodedHeader($name);
245: if ($this->parts && $name === 'Content-Type') {
246: $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
247: }
248: $output .= self::EOL;
249: }
250: $output .= self::EOL;
251:
252: $body = (string) $this->body;
253: if ($body !== '') {
254: switch ($this->getEncoding()) {
255: case self::ENCODING_QUOTED_PRINTABLE:
256: $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
257: break;
258:
259: case self::ENCODING_BASE64:
260: $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
261: break;
262:
263: case self::ENCODING_7BIT:
264: $body = preg_replace('#[\x80-\xFF]+#', '', $body);
265:
266:
267: case self::ENCODING_8BIT:
268: $body = str_replace(array("\x00", "\r"), '', $body);
269: $body = str_replace("\n", self::EOL, $body);
270: $output .= $body;
271: break;
272:
273: default:
274: throw new Nette\InvalidStateException('Unknown encoding.');
275: }
276: }
277:
278: if ($this->parts) {
279: if (substr($output, -strlen(self::EOL)) !== self::EOL) {
280: $output .= self::EOL;
281: }
282: foreach ($this->parts as $part) {
283: $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
284: }
285: $output .= '--' . $boundary.'--';
286: }
287:
288: return $output;
289: }
290:
291:
292:
293:
294:
295: 296: 297: 298: 299: 300: 301:
302: private static function ($s, & $offset = 0, $force = FALSE)
303: {
304: $o = '';
305: if ($offset >= 55) {
306: $o = self::EOL . "\t";
307: $offset = 1;
308: }
309:
310: if (!$force && strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}=? _\r\n\t") === strlen($s)
311: && ($offset + strlen($s) <= self::LINE_LENGTH)
312: ) {
313: $offset += strlen($s);
314: return $o . $s;
315: }
316:
317: $o .= str_replace("\n ", "\n\t", substr(iconv_mime_encode(str_repeat(' ', $offset), $s, array(
318: 'scheme' => 'B',
319: 'input-charset' => 'UTF-8',
320: 'output-charset' => 'UTF-8',
321: )), $offset + 2));
322:
323: $offset = strlen($o) - strrpos($o, "\n");
324: return $o;
325: }
326:
327:
328: 329: 330: 331: 332: }
333: