Packages

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • NMail
  • NMailMimePart
  • NSendmailMailer

Interfaces

  • IMailer
  • Overview
  • Package
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  * @package Nette\Mail
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * MIME message part.
 17:  *
 18:  * @author     David Grudl
 19:  *
 20:  * @property   string $encoding
 21:  * @property   string $body
 22:  * @property-read array $headers
 23:  * @package Nette\Mail
 24:  */
 25: class NMailMimePart extends NObject
 26: {
 27:     /**#@+ encoding */
 28:     const ENCODING_BASE64 = 'base64';
 29:     const ENCODING_7BIT = '7bit';
 30:     const ENCODING_8BIT = '8bit';
 31:     const ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
 32:     /**#@-*/
 33: 
 34:     /**#@+ @internal */
 35:     const EOL = "\r\n";
 36:     const LINE_LENGTH = 76;
 37:     /**#@-*/
 38: 
 39:     /** @var array */
 40:     private $headers = array();
 41: 
 42:     /** @var array */
 43:     private $parts = array();
 44: 
 45:     /** @var string */
 46:     private $body;
 47: 
 48: 
 49: 
 50:     /**
 51:      * Sets a header.
 52:      * @param  string
 53:      * @param  string|array  value or pair email => name
 54:      * @param  bool
 55:      * @return NMailMimePart  provides a fluent interface
 56:      */
 57:     public function setHeader($name, $value, $append = FALSE)
 58:     {
 59:         if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
 60:             throw new InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
 61:         }
 62: 
 63:         if ($value == NULL) { // intentionally ==
 64:             if (!$append) {
 65:                 unset($this->headers[$name]);
 66:             }
 67: 
 68:         } elseif (is_array($value)) { // email
 69:             $tmp = & $this->headers[$name];
 70:             if (!$append || !is_array($tmp)) {
 71:                 $tmp = array();
 72:             }
 73: 
 74:             foreach ($value as $email => $name) {
 75:                 if ($name !== NULL && !NString::checkEncoding($name)) {
 76:                     throw new InvalidArgumentException("Name is not valid UTF-8 string.");
 77:                 }
 78: 
 79:                 if (!preg_match('#^[^@",\s]+@[^@",\s]+\.[a-z]{2,10}$#i', $email)) {
 80:                     throw new InvalidArgumentException("Email address '$email' is not valid.");
 81:                 }
 82: 
 83:                 if (preg_match('#[\r\n]#', $name)) {
 84:                     throw new InvalidArgumentException("Name cannot contain the line separator.");
 85:                 }
 86:                 $tmp[$email] = $name;
 87:             }
 88: 
 89:         } else {
 90:             $value = (string) $value;
 91:             if (!NString::checkEncoding($value)) {
 92:                 throw new InvalidArgumentException("Header is not valid UTF-8 string.");
 93:             }
 94:             $this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
 95:         }
 96:         return $this;
 97:     }
 98: 
 99: 
100: 
101:     /**
102:      * Returns a header.
103:      * @param  string
104:      * @return mixed
105:      */
106:     public function getHeader($name)
107:     {
108:         return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
109:     }
110: 
111: 
112: 
113:     /**
114:      * Removes a header.
115:      * @param  string
116:      * @return NMailMimePart  provides a fluent interface
117:      */
118:     public function clearHeader($name)
119:     {
120:         unset($this->headers[$name]);
121:         return $this;
122:     }
123: 
124: 
125: 
126:     /**
127:      * Returns an encoded header.
128:      * @param  string
129:      * @param  string
130:      * @return string
131:      */
132:     public function getEncodedHeader($name)
133:     {
134:         $offset = strlen($name) + 2; // color + space
135: 
136:         if (!isset($this->headers[$name])) {
137:             return NULL;
138: 
139:         } elseif (is_array($this->headers[$name])) {
140:             $s = '';
141:             foreach ($this->headers[$name] as $email => $name) {
142:                 if ($name != NULL) { // intentionally ==
143:                     $s .= self::encodeHeader(
144:                         strpbrk($name, '.,;<@>()[]"=?') ? '"' . addcslashes($name, '"\\') . '"' : $name,
145:                         $offset
146:                     );
147:                     $email = " <$email>";
148:                 }
149:                 $email .= ',';
150:                 if ($s !== '' && $offset + strlen($email) > self::LINE_LENGTH) {
151:                     $s .= self::EOL . "\t";
152:                     $offset = 1;
153:                 }
154:                 $s .= $email;
155:                 $offset += strlen($email);
156:             }
157:             return substr($s, 0, -1); // last comma
158: 
159:         } else {
160:             return self::encodeHeader($this->headers[$name], $offset);
161:         }
162:     }
163: 
164: 
165: 
166:     /**
167:      * Returns all headers.
168:      * @return array
169:      */
170:     public function getHeaders()
171:     {
172:         return $this->headers;
173:     }
174: 
175: 
176: 
177:     /**
178:      * Sets Content-Type header.
179:      * @param  string
180:      * @param  string
181:      * @return NMailMimePart  provides a fluent interface
182:      */
183:     public function setContentType($contentType, $charset = NULL)
184:     {
185:         $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
186:         return $this;
187:     }
188: 
189: 
190: 
191:     /**
192:      * Sets Content-Transfer-Encoding header.
193:      * @param  string
194:      * @return NMailMimePart  provides a fluent interface
195:      */
196:     public function setEncoding($encoding)
197:     {
198:         $this->setHeader('Content-Transfer-Encoding', $encoding);
199:         return $this;
200:     }
201: 
202: 
203: 
204:     /**
205:      * Returns Content-Transfer-Encoding header.
206:      * @return string
207:      */
208:     public function getEncoding()
209:     {
210:         return $this->getHeader('Content-Transfer-Encoding');
211:     }
212: 
213: 
214: 
215:     /**
216:      * Adds or creates new multipart.
217:      * @param  NMailMimePart
218:      * @return NMailMimePart
219:      */
220:     public function addPart(NMailMimePart $part = NULL)
221:     {
222:         return $this->parts[] = $part === NULL ? new self : $part;
223:     }
224: 
225: 
226: 
227:     /**
228:      * Sets textual body.
229:      * @param  mixed
230:      * @return NMailMimePart  provides a fluent interface
231:      */
232:     public function setBody($body)
233:     {
234:         $this->body = $body;
235:         return $this;
236:     }
237: 
238: 
239: 
240:     /**
241:      * Gets textual body.
242:      * @return mixed
243:      */
244:     public function getBody()
245:     {
246:         return $this->body;
247:     }
248: 
249: 
250: 
251:     /********************* building ****************d*g**/
252: 
253: 
254: 
255:     /**
256:      * Returns encoded message.
257:      * @return string
258:      */
259:     public function generateMessage()
260:     {
261:         $output = '';
262:         $boundary = '--------' . md5(uniqid('', TRUE));
263: 
264:         foreach ($this->headers as $name => $value) {
265:             $output .= $name . ': ' . $this->getEncodedHeader($name);
266:             if ($this->parts && $name === 'Content-Type') {
267:                 $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
268:             }
269:             $output .= self::EOL;
270:         }
271:         $output .= self::EOL;
272: 
273:         $body = (string) $this->body;
274:         if ($body !== '') {
275:             switch ($this->getEncoding()) {
276:             case self::ENCODING_QUOTED_PRINTABLE:
277:                 $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
278:                 break;
279: 
280:             case self::ENCODING_BASE64:
281:                 $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
282:                 break;
283: 
284:             case self::ENCODING_7BIT:
285:                 $body = preg_replace('#[\x80-\xFF]+#', '', $body);
286:                 // break intentionally omitted
287: 
288:             case self::ENCODING_8BIT:
289:                 $body = str_replace(array("\x00", "\r"), '', $body);
290:                 $body = str_replace("\n", self::EOL, $body);
291:                 $output .= $body;
292:                 break;
293: 
294:             default:
295:                 throw new InvalidStateException('Unknown encoding');
296:             }
297:         }
298: 
299:         if ($this->parts) {
300:             if (substr($output, -strlen(self::EOL)) !== self::EOL) $output .= self::EOL;
301:             foreach ($this->parts as $part) {
302:                 $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
303:             }
304:             $output .= '--' . $boundary.'--';
305:         }
306: 
307:         return $output;
308:     }
309: 
310: 
311: 
312:     /********************* QuotedPrintable helpers ****************d*g**/
313: 
314: 
315: 
316:     /**
317:      * Converts a 8 bit header to a quoted-printable string.
318:      * @param  string
319:      * @param  string
320:      * @param  int
321:      * @return string
322:      */
323:     private static function encodeHeader($s, & $offset = 0)
324:     {
325:         $o = '';
326:         if ($offset >= 55) { // maximum for iconv_mime_encode
327:             $o = self::EOL . "\t";
328:             $offset = 1;
329:         }
330: 
331:         if (strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}=? _\r\n\t") === strlen($s)
332:             && ($offset + strlen($s) <= self::LINE_LENGTH)) {
333:             $offset += strlen($s);
334:             return $o . $s;
335:         }
336: 
337:         $o .= str_replace("\n ", "\n\t", substr(iconv_mime_encode(str_repeat(' ', $offset), $s, array(
338:             'scheme' => 'B', // Q is broken
339:             'input-charset' => 'UTF-8',
340:             'output-charset' => 'UTF-8',
341:         )), $offset + 2));
342: 
343:         $offset = strlen($o) - strrpos($o, "\n");
344:         return $o;
345:     }
346: 
347: 
348: 
349:     /**
350:      * Converts a 8 bit string to a quoted-printable string.
351:      * @param  string
352:      * @return string
353:      */public static function encodeQuotedPrintable($s)
354:     {
355:         $range = '!"#$%&\'()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}'; // \x21-\x7E without \x3D
356:         $pos = 0;
357:         $len = 0;
358:         $o = '';
359:         $size = strlen($s);
360:         while ($pos < $size) {
361:             if ($l = strspn($s, $range, $pos)) {
362:                 while ($len + $l > self::LINE_LENGTH - 1) { // 1 = length of suffix =
363:                     $lx = self::LINE_LENGTH - $len - 1;
364:                     $o .= substr($s, $pos, $lx) . '=' . self::EOL;
365:                     $pos += $lx;
366:                     $l -= $lx;
367:                     $len = 0;
368:                 }
369:                 $o .= substr($s, $pos, $l);
370:                 $len += $l;
371:                 $pos += $l;
372: 
373:             } else {
374:                 $len += 3;
375:                 if ($len > self::LINE_LENGTH - 1) {
376:                     $o .= '=' . self::EOL;
377:                     $len = 3;
378:                 }
379:                 $o .= '=' . strtoupper(bin2hex($s[$pos]));
380:                 $pos++;
381:             }
382:         }
383:         return rtrim($o, '=' . self::EOL);
384:     }
385: 
386: }
387: 
Nette Framework 0.9.7 (for PHP 5.2) API documentation generated by ApiGen 2.3.0