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
      • 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
    • Bridges
      • Nette

Classes

  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

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