Namespaces

  • 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

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