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
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Utils
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

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