Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • NMail
  • NMailMimePart
  • NSendmailMailer
  • NSmtpMailer

Interfaces

  • IMailer

Exceptions

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