Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

  • SmtpException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Mail;
  9: 
 10: use Nette;
 11: use Nette\Utils\Strings;
 12: 
 13: 
 14: /**
 15:  * MIME message part.
 16:  *
 17:  * @author     David Grudl
 18:  *
 19:  * @property   mixed $body
 20:  */
 21: class MimePart extends Nette\Object
 22: {
 23:     /** encoding */
 24:     const ENCODING_BASE64 = 'base64',
 25:         ENCODING_7BIT = '7bit',
 26:         ENCODING_8BIT = '8bit',
 27:         ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
 28: 
 29:     /** @internal */
 30:     const EOL = "\r\n";
 31:     const LINE_LENGTH = 76;
 32: 
 33:     /** @var array */
 34:     private $headers = array();
 35: 
 36:     /** @var array */
 37:     private $parts = array();
 38: 
 39:     /** @var string */
 40:     private $body;
 41: 
 42: 
 43:     /**
 44:      * Sets a header.
 45:      * @param  string
 46:      * @param  string|array  value or pair email => name
 47:      * @param  bool
 48:      * @return self
 49:      */
 50:     public function setHeader($name, $value, $append = FALSE)
 51:     {
 52:         if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
 53:             throw new Nette\InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
 54:         }
 55: 
 56:         if ($value == NULL) { // intentionally ==
 57:             if (!$append) {
 58:                 unset($this->headers[$name]);
 59:             }
 60: 
 61:         } elseif (is_array($value)) { // email
 62:             $tmp = & $this->headers[$name];
 63:             if (!$append || !is_array($tmp)) {
 64:                 $tmp = array();
 65:             }
 66: 
 67:             foreach ($value as $email => $recipient) {
 68:                 if ($recipient !== NULL && !Strings::checkEncoding($recipient)) {
 69:                     Nette\Utils\Validators::assert($recipient, 'unicode', "header '$name'");
 70:                 }
 71:                 if (preg_match('#[\r\n]#', $recipient)) {
 72:                     throw new Nette\InvalidArgumentException('Name must not contain line separator.');
 73:                 }
 74:                 Nette\Utils\Validators::assert($email, 'email', "header '$name'");
 75:                 $tmp[$email] = $recipient;
 76:             }
 77: 
 78:         } else {
 79:             $value = (string) $value;
 80:             if (!Strings::checkEncoding($value)) {
 81:                 throw new Nette\InvalidArgumentException('Header is not valid UTF-8 string.');
 82:             }
 83:             $this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
 84:         }
 85:         return $this;
 86:     }
 87: 
 88: 
 89:     /**
 90:      * Returns a header.
 91:      * @param  string
 92:      * @return mixed
 93:      */
 94:     public function getHeader($name)
 95:     {
 96:         return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
 97:     }
 98: 
 99: 
100:     /**
101:      * Removes a header.
102:      * @param  string
103:      * @return self
104:      */
105:     public function clearHeader($name)
106:     {
107:         unset($this->headers[$name]);
108:         return $this;
109:     }
110: 
111: 
112:     /**
113:      * Returns an encoded header.
114:      * @param  string
115:      * @param  string
116:      * @return string
117:      */
118:     public function getEncodedHeader($name)
119:     {
120:         $offset = strlen($name) + 2; // colon + space
121: 
122:         if (!isset($this->headers[$name])) {
123:             return NULL;
124: 
125:         } elseif (is_array($this->headers[$name])) {
126:             $s = '';
127:             foreach ($this->headers[$name] as $email => $name) {
128:                 if ($name != NULL) { // intentionally ==
129:                     $s .= self::encodeHeader($name, $offset, TRUE);
130:                     $email = " <$email>";
131:                 }
132:                 $s .= self::append($email . ',', $offset);
133:             }
134:             return ltrim(substr($s, 0, -1)); // last comma
135: 
136:         } elseif (preg_match('#^(\S+; (?:file)?name=)"(.*)"\z#', $this->headers[$name], $m)) { // Content-Disposition
137:             $offset += strlen($m[1]);
138:             return $m[1] . '"' . self::encodeHeader($m[2], $offset) . '"';
139: 
140:         } else {
141:             return ltrim(self::encodeHeader($this->headers[$name], $offset));
142:         }
143:     }
144: 
145: 
146:     /**
147:      * Returns all headers.
148:      * @return array
149:      */
150:     public function getHeaders()
151:     {
152:         return $this->headers;
153:     }
154: 
155: 
156:     /**
157:      * Sets Content-Type header.
158:      * @param  string
159:      * @param  string
160:      * @return self
161:      */
162:     public function setContentType($contentType, $charset = NULL)
163:     {
164:         $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
165:         return $this;
166:     }
167: 
168: 
169:     /**
170:      * Sets Content-Transfer-Encoding header.
171:      * @param  string
172:      * @return self
173:      */
174:     public function setEncoding($encoding)
175:     {
176:         $this->setHeader('Content-Transfer-Encoding', $encoding);
177:         return $this;
178:     }
179: 
180: 
181:     /**
182:      * Returns Content-Transfer-Encoding header.
183:      * @return string
184:      */
185:     public function getEncoding()
186:     {
187:         return $this->getHeader('Content-Transfer-Encoding');
188:     }
189: 
190: 
191:     /**
192:      * Adds or creates new multipart.
193:      * @return MimePart
194:      */
195:     public function addPart(MimePart $part = NULL)
196:     {
197:         return $this->parts[] = $part === NULL ? new self : $part;
198:     }
199: 
200: 
201:     /**
202:      * Sets textual body.
203:      * @return self
204:      */
205:     public function setBody($body)
206:     {
207:         if ($body instanceof Nette\Templating\ITemplate || $body instanceof Nette\Application\UI\ITemplate) {
208:             $body->mail = $this;
209:             $body = $body->__toString(TRUE);
210:         }
211:         $this->body = $body;
212:         return $this;
213:     }
214: 
215: 
216:     /**
217:      * Gets textual body.
218:      * @return mixed
219:      */
220:     public function getBody()
221:     {
222:         return $this->body;
223:     }
224: 
225: 
226:     /********************* building ****************d*g**/
227: 
228: 
229:     /**
230:      * Returns encoded message.
231:      * @return string
232:      */
233:     public function getEncodedMessage()
234:     {
235:         $output = '';
236:         $boundary = '--------' . Nette\Utils\Random::generate();
237: 
238:         foreach ($this->headers as $name => $value) {
239:             $output .= $name . ': ' . $this->getEncodedHeader($name);
240:             if ($this->parts && $name === 'Content-Type') {
241:                 $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
242:             }
243:             $output .= self::EOL;
244:         }
245:         $output .= self::EOL;
246: 
247:         $body = (string) $this->body;
248:         if ($body !== '') {
249:             switch ($this->getEncoding()) {
250:                 case self::ENCODING_QUOTED_PRINTABLE:
251:                     $output .= quoted_printable_encode($body);
252:                     break;
253: 
254:                 case self::ENCODING_BASE64:
255:                     $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
256:                     break;
257: 
258:                 case self::ENCODING_7BIT:
259:                     $body = preg_replace('#[\x80-\xFF]+#', '', $body);
260:                     // break intentionally omitted
261: 
262:                 case self::ENCODING_8BIT:
263:                     $body = str_replace(array("\x00", "\r"), '', $body);
264:                     $body = str_replace("\n", self::EOL, $body);
265:                     $output .= $body;
266:                     break;
267: 
268:                 default:
269:                     throw new Nette\InvalidStateException('Unknown encoding.');
270:             }
271:         }
272: 
273:         if ($this->parts) {
274:             if (substr($output, -strlen(self::EOL)) !== self::EOL) {
275:                 $output .= self::EOL;
276:             }
277:             foreach ($this->parts as $part) {
278:                 $output .= '--' . $boundary . self::EOL . $part->getEncodedMessage() . self::EOL;
279:             }
280:             $output .= '--' . $boundary.'--';
281:         }
282: 
283:         return $output;
284:     }
285: 
286: 
287:     /********************* QuotedPrintable helpers ****************d*g**/
288: 
289: 
290:     /**
291:      * Converts a 8 bit header to a string.
292:      * @param  string
293:      * @param  int
294:      * @param  bool
295:      * @return string
296:      */
297:     private static function encodeHeader($s, & $offset = 0, $quotes = FALSE)
298:     {
299:         if (strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}~=? _\r\n\t") === strlen($s)) {
300:             if ($quotes && preg_match('#[^ a-zA-Z0-9!\#$%&\'*+/?^_`{|}~-]#', $s)) { // RFC 2822 atext except =
301:                 return self::append('"' . addcslashes($s, '"\\') . '"', $offset);
302:             }
303:             return self::append($s, $offset);
304:         }
305: 
306:         $o = '';
307:         if ($offset >= 55) { // maximum for iconv_mime_encode
308:             $o = self::EOL . "\t";
309:             $offset = 1;
310:         }
311: 
312:         $s = iconv_mime_encode(str_repeat(' ', $old = $offset), $s, array(
313:             'scheme' => 'B', // Q is broken
314:             'input-charset' => 'UTF-8',
315:             'output-charset' => 'UTF-8',
316:         ));
317: 
318:         $offset = strlen($s) - strrpos($s, "\n");
319:         $s = str_replace("\n ", "\n\t", substr($s, $old + 2)); // adds ': '
320:         return $o . $s;
321:     }
322: 
323: 
324:     private static function append($s, & $offset = 0)
325:     {
326:         if ($offset + strlen($s) > self::LINE_LENGTH) {
327:             $offset = 1;
328:             $s = self::EOL . "\t" . $s;
329:         }
330:         $offset += strlen($s);
331:         return $s;
332:     }
333: 
334: }
335: 
Nette 2.2 API documentation generated by ApiGen 2.8.0