Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • Mail
  • MailMimePart
  • SendmailMailer

Interfaces

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