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