Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • FallbackMailer
  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

  • FallbackMailerException
  • SendException
  • SmtpException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  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:  * Mail provides functionality to compose and send both text and MIME-compliant multipart email messages.
 16:  *
 17:  * @property   string $subject
 18:  * @property   string $htmlBody
 19:  */
 20: class Message extends MimePart
 21: {
 22:     /** Priority */
 23:     const HIGH = 1,
 24:         NORMAL = 3,
 25:         LOW = 5;
 26: 
 27:     /** @var array */
 28:     public static $defaultHeaders = [
 29:         'MIME-Version' => '1.0',
 30:         'X-Mailer' => 'Nette Framework',
 31:     ];
 32: 
 33:     /** @var array */
 34:     private $attachments = [];
 35: 
 36:     /** @var array */
 37:     private $inlines = [];
 38: 
 39:     /** @var string */
 40:     private $htmlBody = '';
 41: 
 42: 
 43:     public function __construct()
 44:     {
 45:         foreach (static::$defaultHeaders as $name => $value) {
 46:             $this->setHeader($name, $value);
 47:         }
 48:         $this->setHeader('Date', date('r'));
 49:     }
 50: 
 51: 
 52:     /**
 53:      * Sets the sender of the message.
 54:      * @param  string  email or format "John Doe" <doe@example.com>
 55:      * @param  string
 56:      * @return static
 57:      */
 58:     public function setFrom($email, $name = null)
 59:     {
 60:         $this->setHeader('From', $this->formatEmail($email, $name));
 61:         return $this;
 62:     }
 63: 
 64: 
 65:     /**
 66:      * Returns the sender of the message.
 67:      * @return array
 68:      */
 69:     public function getFrom()
 70:     {
 71:         return $this->getHeader('From');
 72:     }
 73: 
 74: 
 75:     /**
 76:      * Adds the reply-to address.
 77:      * @param  string  email or format "John Doe" <doe@example.com>
 78:      * @param  string
 79:      * @return static
 80:      */
 81:     public function addReplyTo($email, $name = null)
 82:     {
 83:         $this->setHeader('Reply-To', $this->formatEmail($email, $name), true);
 84:         return $this;
 85:     }
 86: 
 87: 
 88:     /**
 89:      * Sets the subject of the message.
 90:      * @param  string
 91:      * @return static
 92:      */
 93:     public function setSubject($subject)
 94:     {
 95:         $this->setHeader('Subject', $subject);
 96:         return $this;
 97:     }
 98: 
 99: 
100:     /**
101:      * Returns the subject of the message.
102:      * @return string|null
103:      */
104:     public function getSubject()
105:     {
106:         return $this->getHeader('Subject');
107:     }
108: 
109: 
110:     /**
111:      * Adds email recipient.
112:      * @param  string  email or format "John Doe" <doe@example.com>
113:      * @param  string
114:      * @return static
115:      */
116:     public function addTo($email, $name = null) // addRecipient()
117:     {
118:         $this->setHeader('To', $this->formatEmail($email, $name), true);
119:         return $this;
120:     }
121: 
122: 
123:     /**
124:      * Adds carbon copy email recipient.
125:      * @param  string  email or format "John Doe" <doe@example.com>
126:      * @param  string
127:      * @return static
128:      */
129:     public function addCc($email, $name = null)
130:     {
131:         $this->setHeader('Cc', $this->formatEmail($email, $name), true);
132:         return $this;
133:     }
134: 
135: 
136:     /**
137:      * Adds blind carbon copy email recipient.
138:      * @param  string  email or format "John Doe" <doe@example.com>
139:      * @param  string
140:      * @return static
141:      */
142:     public function addBcc($email, $name = null)
143:     {
144:         $this->setHeader('Bcc', $this->formatEmail($email, $name), true);
145:         return $this;
146:     }
147: 
148: 
149:     /**
150:      * Formats recipient email.
151:      * @param  string
152:      * @param  string|null
153:      * @return array
154:      */
155:     private function formatEmail($email, $name)
156:     {
157:         if (!$name && preg_match('#^(.+) +<(.*)>\z#', $email, $matches)) {
158:             return [$matches[2] => $matches[1]];
159:         } else {
160:             return [$email => $name];
161:         }
162:     }
163: 
164: 
165:     /**
166:      * Sets the Return-Path header of the message.
167:      * @param  string  email
168:      * @return static
169:      */
170:     public function setReturnPath($email)
171:     {
172:         $this->setHeader('Return-Path', $email);
173:         return $this;
174:     }
175: 
176: 
177:     /**
178:      * Returns the Return-Path header.
179:      * @return string
180:      */
181:     public function getReturnPath()
182:     {
183:         return $this->getHeader('Return-Path');
184:     }
185: 
186: 
187:     /**
188:      * Sets email priority.
189:      * @param  int
190:      * @return static
191:      */
192:     public function setPriority($priority)
193:     {
194:         $this->setHeader('X-Priority', (int) $priority);
195:         return $this;
196:     }
197: 
198: 
199:     /**
200:      * Returns email priority.
201:      * @return int
202:      */
203:     public function getPriority()
204:     {
205:         return $this->getHeader('X-Priority');
206:     }
207: 
208: 
209:     /**
210:      * Sets HTML body.
211:      * @param  string
212:      * @param  string
213:      * @return static
214:      */
215:     public function setHtmlBody($html, $basePath = null)
216:     {
217:         $html = (string) $html;
218: 
219:         if ($basePath) {
220:             $cids = [];
221:             $matches = Strings::matchAll(
222:                 $html,
223:                 '#
224:                     (<img[^<>]*\s src\s*=\s*
225:                     |<body[^<>]*\s background\s*=\s*
226:                     |<[^<>]+\s style\s*=\s* ["\'][^"\'>]+[:\s] url\(
227:                     |<style[^>]*>[^<]+ [:\s] url\()
228:                     (["\']?)(?![a-z]+:|[/\\#])([^"\'>)\s]+)
229:                     |\[\[ ([\w()+./@~-]+) \]\]
230:                 #ix',
231:                 PREG_OFFSET_CAPTURE
232:             );
233:             foreach (array_reverse($matches) as $m) {
234:                 $file = rtrim($basePath, '/\\') . '/' . (isset($m[4]) ? $m[4][0] : urldecode($m[3][0]));
235:                 if (!isset($cids[$file])) {
236:                     $cids[$file] = substr($this->addEmbeddedFile($file)->getHeader('Content-ID'), 1, -1);
237:                 }
238:                 $html = substr_replace($html,
239:                     "{$m[1][0]}{$m[2][0]}cid:{$cids[$file]}",
240:                     $m[0][1], strlen($m[0][0])
241:                 );
242:             }
243:         }
244: 
245:         if ($this->getSubject() == null) { // intentionally ==
246:             $html = Strings::replace($html, '#<title>(.+?)</title>#is', function ($m) {
247:                 $this->setSubject(html_entity_decode($m[1], ENT_QUOTES, 'UTF-8'));
248:             });
249:         }
250: 
251:         $this->htmlBody = ltrim(str_replace("\r", '', $html), "\n");
252: 
253:         if ($this->getBody() === '' && $html !== '') {
254:             $this->setBody($this->buildText($html));
255:         }
256: 
257:         return $this;
258:     }
259: 
260: 
261:     /**
262:      * Gets HTML body.
263:      * @return string
264:      */
265:     public function getHtmlBody()
266:     {
267:         return $this->htmlBody;
268:     }
269: 
270: 
271:     /**
272:      * Adds embedded file.
273:      * @param  string
274:      * @param  string
275:      * @param  string
276:      * @return MimePart
277:      */
278:     public function addEmbeddedFile($file, $content = null, $contentType = null)
279:     {
280:         return $this->inlines[$file] = $this->createAttachment($file, $content, $contentType, 'inline')
281:             ->setHeader('Content-ID', $this->getRandomId());
282:     }
283: 
284: 
285:     /**
286:      * Adds inlined Mime Part.
287:      * @param  MimePart
288:      * @return static
289:      */
290:     public function addInlinePart(MimePart $part)
291:     {
292:         $this->inlines[] = $part;
293:         return $this;
294:     }
295: 
296: 
297:     /**
298:      * Adds attachment.
299:      * @param  string
300:      * @param  string
301:      * @param  string
302:      * @return MimePart
303:      */
304:     public function addAttachment($file, $content = null, $contentType = null)
305:     {
306:         return $this->attachments[] = $this->createAttachment($file, $content, $contentType, 'attachment');
307:     }
308: 
309: 
310:     /**
311:      * Gets all email attachments.
312:      * @return MimePart[]
313:      */
314:     public function getAttachments()
315:     {
316:         return $this->attachments;
317:     }
318: 
319: 
320:     /**
321:      * Creates file MIME part.
322:      * @param  string
323:      * @param  string|null
324:      * @param  string|null
325:      * @param  string
326:      * @return MimePart
327:      */
328:     private function createAttachment($file, $content, $contentType, $disposition)
329:     {
330:         $part = new MimePart;
331:         if ($content === null) {
332:             $content = @file_get_contents($file); // @ is escalated to exception
333:             if ($content === false) {
334:                 throw new Nette\FileNotFoundException("Unable to read file '$file'.");
335:             }
336:         } else {
337:             $content = (string) $content;
338:         }
339:         if (!$contentType) {
340:             $contentType = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content);
341:         }
342:         if (!strcasecmp($contentType, 'message/rfc822')) { // not allowed for attached files
343:             $contentType = 'application/octet-stream';
344:         }
345: 
346:         $part->setBody($content);
347:         $part->setContentType($contentType);
348:         $part->setEncoding(preg_match('#(multipart|message)/#A', $contentType) ? self::ENCODING_8BIT : self::ENCODING_BASE64);
349:         $part->setHeader('Content-Disposition', $disposition . '; filename="' . Strings::fixEncoding(basename($file)) . '"');
350:         return $part;
351:     }
352: 
353: 
354:     /********************* building and sending ****************d*g**/
355: 
356: 
357:     /**
358:      * Returns encoded message.
359:      * @return string
360:      */
361:     public function generateMessage()
362:     {
363:         return $this->build()->getEncodedMessage();
364:     }
365: 
366: 
367:     /**
368:      * Builds email. Does not modify itself, but returns a new object.
369:      * @return static
370:      */
371:     protected function build()
372:     {
373:         $mail = clone $this;
374:         $mail->setHeader('Message-ID', $this->getRandomId());
375: 
376:         $cursor = $mail;
377:         if ($mail->attachments) {
378:             $tmp = $cursor->setContentType('multipart/mixed');
379:             $cursor = $cursor->addPart();
380:             foreach ($mail->attachments as $value) {
381:                 $tmp->addPart($value);
382:             }
383:         }
384: 
385:         if ($mail->htmlBody !== '') {
386:             $tmp = $cursor->setContentType('multipart/alternative');
387:             $cursor = $cursor->addPart();
388:             $alt = $tmp->addPart();
389:             if ($mail->inlines) {
390:                 $tmp = $alt->setContentType('multipart/related');
391:                 $alt = $alt->addPart();
392:                 foreach ($mail->inlines as $value) {
393:                     $tmp->addPart($value);
394:                 }
395:             }
396:             $alt->setContentType('text/html', 'UTF-8')
397:                 ->setEncoding(preg_match('#[^\n]{990}#', $mail->htmlBody)
398:                     ? self::ENCODING_QUOTED_PRINTABLE
399:                     : (preg_match('#[\x80-\xFF]#', $mail->htmlBody) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
400:                 ->setBody($mail->htmlBody);
401:         }
402: 
403:         $text = $mail->getBody();
404:         $mail->setBody('');
405:         $cursor->setContentType('text/plain', 'UTF-8')
406:             ->setEncoding(preg_match('#[^\n]{990}#', $text)
407:                 ? self::ENCODING_QUOTED_PRINTABLE
408:                 : (preg_match('#[\x80-\xFF]#', $text) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
409:             ->setBody($text);
410: 
411:         return $mail;
412:     }
413: 
414: 
415:     /**
416:      * Builds text content.
417:      * @param  string
418:      * @return string
419:      */
420:     protected function buildText($html)
421:     {
422:         $text = Strings::replace($html, [
423:             '#<(style|script|head).*</\\1>#Uis' => '',
424:             '#<t[dh][ >]#i' => ' $0',
425:             '#<a\s[^>]*href=(?|"([^"]+)"|\'([^\']+)\')[^>]*>(.*?)</a>#is' => '$2 &lt;$1&gt;',
426:             '#[\r\n]+#' => ' ',
427:             '#<(/?p|/?h\d|li|br|/tr)[ >/]#i' => "\n$0",
428:         ]);
429:         $text = html_entity_decode(strip_tags($text), ENT_QUOTES, 'UTF-8');
430:         $text = Strings::replace($text, '#[ \t]+#', ' ');
431:         return trim($text);
432:     }
433: 
434: 
435:     /** @return string */
436:     private function getRandomId()
437:     {
438:         return '<' . Nette\Utils\Random::generate() . '@'
439:             . preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n'))
440:             . '>';
441:     }
442: }
443: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0