├── bootstrap.php
├── classes
├── email.php
└── email
│ ├── driver.php
│ └── driver
│ ├── mail.php
│ ├── mailgun.php
│ ├── mandrill.php
│ ├── noop.php
│ ├── sendmail.php
│ └── smtp.php
├── composer.json
├── config
└── email.php
└── readme.md
/bootstrap.php:
--------------------------------------------------------------------------------
1 | __DIR__.'/classes/email.php',
20 | 'Email\\Email_Driver' => __DIR__.'/classes/email/driver.php',
21 | 'Email\\Email_Driver_Mail' => __DIR__.'/classes/email/driver/mail.php',
22 | 'Email\\Email_Driver_Smtp' => __DIR__.'/classes/email/driver/smtp.php',
23 | 'Email\\Email_Driver_Sendmail' => __DIR__.'/classes/email/driver/sendmail.php',
24 | 'Email\\Email_Driver_Noop' => __DIR__.'/classes/email/driver/noop.php',
25 | 'Email\\Email_Driver_Mailgun' => __DIR__.'/classes/email/driver/mailgun.php',
26 | 'Email\\Email_Driver_Mandrill' => __DIR__.'/classes/email/driver/mandrill.php',
27 |
28 | /**
29 | * Email exceptions.
30 | */
31 | 'Email\\AttachmentNotFoundException' => __DIR__.'/classes/email.php',
32 | 'Email\\InvalidAttachmentsException' => __DIR__.'/classes/email.php',
33 | 'Email\\InvalidEmailStringEncoding' => __DIR__.'/classes/email.php',
34 | 'Email\\EmailSendingFailedException' => __DIR__.'/classes/email.php',
35 | 'Email\\EmailValidationFailedException' => __DIR__.'/classes/email.php',
36 |
37 | /**
38 | * Smtp exceptions
39 | */
40 | 'Email\\SmtpTimeoutException' => __DIR__.'/classes/email/driver/smtp.php',
41 | 'Email\\SmtpConnectionException' => __DIR__.'/classes/email/driver/smtp.php',
42 | 'Email\\SmtpCommandFailureException' => __DIR__.'/classes/email/driver/smtp.php',
43 | 'Email\\SmtpAuthenticationFailedException' => __DIR__.'/classes/email/driver/smtp.php',
44 |
45 | /**
46 | * Sendmail exceptions
47 | */
48 | 'Email\\SendmailFailedException' => __DIR__.'/classes/email/driver/sendmail.php',
49 | 'Email\\SendmailConnectionException' => __DIR__.'/classes/email/driver/sendmail.php',
50 | ));
51 |
--------------------------------------------------------------------------------
/classes/email.php:
--------------------------------------------------------------------------------
1 | array(),
47 | 'attachment' => array(),
48 | );
49 |
50 | /**
51 | * Message body
52 | */
53 | protected $body = '';
54 |
55 | /**
56 | * Message alt body
57 | */
58 | protected $alt_body = '';
59 |
60 | /**
61 | * Message subject
62 | */
63 | protected $subject = '';
64 |
65 | /**
66 | * Invalid addresses
67 | */
68 | protected $invalid_addresses = array();
69 |
70 | /**
71 | * Message boundaries
72 | */
73 | protected $boundaries = array();
74 |
75 | /**
76 | * Message headers
77 | */
78 | protected $headers = array();
79 |
80 | /**
81 | * Custom headers
82 | */
83 | protected $extra_headers = array();
84 |
85 | /**
86 | * Pipelining enabled?
87 | */
88 | protected $pipelining = false;
89 |
90 | /**
91 | * Mail type
92 | */
93 | protected $type = 'plain';
94 |
95 | /**
96 | * Driver constructor
97 | *
98 | * @param array $config driver config
99 | */
100 | public function __construct(array $config)
101 | {
102 | $this->config = $config;
103 | }
104 |
105 | /**
106 | * Get a driver config setting.
107 | *
108 | * @param string $key Config key
109 | * @param string $default Default value
110 | *
111 | * @return mixed the config setting value
112 | */
113 | public function get_config($key, $default = null)
114 | {
115 | return \Arr::get($this->config, $key, $default);
116 | }
117 |
118 | /**
119 | * Set a driver config setting.
120 | *
121 | * @param string $key the config key
122 | * @param mixed $value the new config value
123 | * @return object $this
124 | */
125 | public function set_config($key, $value)
126 | {
127 | \Arr::set($this->config, $key, $value);
128 |
129 | return $this;
130 | }
131 |
132 | /**
133 | * Enables or disables driver pipelining.
134 | *
135 | * @param bool $pipelining Whether or not to enable pipelining
136 | *
137 | * @return $this
138 | */
139 | public function pipelining($pipelining = true)
140 | {
141 | $this->pipelining = (bool) $pipelining;
142 |
143 | return $this;
144 | }
145 |
146 | /**
147 | * Gets the body
148 | *
149 | * @return string the message body
150 | */
151 | public function get_body()
152 | {
153 | return $this->body;
154 | }
155 |
156 | /**
157 | * Sets the body
158 | *
159 | * @param string $body The message body
160 | *
161 | * @return $this
162 | */
163 | public function body($body)
164 | {
165 | $this->body = (string) $body;
166 |
167 | return $this;
168 | }
169 |
170 | /**
171 | * Sets the alt body
172 | *
173 | * @param string $alt_body The message alt body
174 | *
175 | * @return $this
176 | */
177 | public function alt_body($alt_body)
178 | {
179 | $this->alt_body = (string) $alt_body;
180 |
181 | return $this;
182 | }
183 |
184 | /**
185 | * Sets the mail priority
186 | *
187 | * @param string $priority The message priority
188 | *
189 | * @return $this
190 | */
191 | public function priority($priority)
192 | {
193 | $this->config['priority'] = $priority;
194 |
195 | return $this;
196 | }
197 |
198 | /**
199 | * Sets the html body and optionally a generated alt body.
200 | *
201 | * @param string $html The body html
202 | * @param bool $generate_alt Whether to generate the alt body, will set is html to true
203 | * @param bool $auto_attach Whether to auto attach inline files
204 | *
205 | * @return $this
206 | */
207 | public function html_body($html, $generate_alt = null, $auto_attach = null)
208 | {
209 | $this->config['is_html'] = true;
210 |
211 | // Check settings
212 | $generate_alt = is_bool($generate_alt) ? $generate_alt : $this->config['generate_alt'];
213 | $auto_attach = is_bool($auto_attach) ? $auto_attach : $this->config['auto_attach'];
214 | $remove_html_comments = isset($this->config['remove_html_comments']) ? (bool) $this->config['remove_html_comments'] : true;
215 |
216 | // Remove html comments
217 | if ($remove_html_comments)
218 | {
219 | $html = preg_replace('//', '', (string) $html);
220 | }
221 |
222 | if ($auto_attach)
223 | {
224 | // Auto attach all images
225 | preg_match_all("/(src|background)=\"(.*)\"/Ui", $html, $images);
226 | if ( ! empty($images[2]))
227 | {
228 | foreach ($images[2] as $i => $image_url)
229 | {
230 | // convert inline images to cid attachments
231 | if (preg_match('/^data:image\/(.*);base64,\s(.*)$/', $image_url, $image))
232 | {
233 | // create a temp image for the attachmment
234 | $file = strtolower(tempnam(sys_get_temp_dir(), 'inline-').'.'.$image[1]);
235 | file_put_contents($file, base64_decode($image[2]));
236 |
237 | // attach the temp file
238 | $cid = 'cid:'.md5($file);
239 | $this->attach($file, true, $cid);
240 |
241 | // and remove it
242 | unlink($file);
243 | $html = preg_replace("/".$images[1][$i]."=\"".preg_quote($image_url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $html);
244 | }
245 | // Don't attach absolute urls
246 | elseif ( ! preg_match('/(^http\:\/\/|^https\:\/\/|^\/\/|^cid\:|^data\:|^#)/Ui', $image_url))
247 | {
248 | $cid = 'cid:'.md5(pathinfo($image_url, PATHINFO_BASENAME));
249 | if ( ! isset($this->attachments['inline'][$cid]))
250 | {
251 | $this->attach($image_url, true, $cid);
252 | }
253 | $html = preg_replace("/".$images[1][$i]."=\"".preg_quote($image_url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $html);
254 | }
255 |
256 | // Deal with relative protocol URI's if needed
257 | elseif ($scheme = $this->get_config('relative_protocol_replacement', false) and strpos($image_url, '//') === 0)
258 | {
259 | $html = preg_replace("/".$images[1][$i]."=\"".preg_quote($image_url, '/')."\"/Ui", $images[1][$i]."=\"".$scheme.substr($image_url, 2)."\"", $html);
260 | }
261 | }
262 | }
263 | }
264 |
265 | $this->body = $html;
266 |
267 | $generate_alt and $this->alt_body = static::generate_alt($html, $this->config['wordwrap'], $this->config['newline']);
268 |
269 | return $this;
270 | }
271 |
272 | /**
273 | * Gets the message subject
274 | *
275 | * @return string the message subject
276 | */
277 | public function get_subject()
278 | {
279 | return $this->subject;
280 | }
281 |
282 | /**
283 | * Sets the message subject
284 | *
285 | * @param string $subject The message subject
286 | *
287 | * @return $this
288 | */
289 | public function subject($subject)
290 | {
291 | if ($this->config['encode_headers'])
292 | {
293 | $subject = $this->encode_mimeheader((string) $subject);
294 | }
295 | $this->subject = (string) $subject;
296 |
297 | return $this;
298 | }
299 |
300 | /**
301 | * Gets from address and name
302 | *
303 | * @return array from address and name
304 | */
305 | public function get_from()
306 | {
307 | return $this->config['from'];
308 | }
309 |
310 | /**
311 | * Sets the from address and name
312 | *
313 | * @param string $email The from email address
314 | * @param bool|string $name The optional from name
315 | *
316 | * @return $this
317 | */
318 | public function from($email, $name = false)
319 | {
320 | if (is_array($email) and isset($email['email']) and isset($email['name']))
321 | {
322 | $this->config['from'] = $email;
323 | }
324 | else
325 | {
326 | $this->config['from']['email'] = (string) $email;
327 | $this->config['from']['name'] = (is_string($name)) ? $name : false;
328 |
329 | }
330 |
331 | if ($this->config['encode_headers'] and ! empty($this->config['from']['name']))
332 | {
333 | $this->config['from']['name'] = $this->encode_mimeheader((string) $this->config['from']['name']);
334 | }
335 |
336 | return $this;
337 | }
338 |
339 | /**
340 | * Gets to recipients list.
341 | *
342 | * @return array to recipients list
343 | */
344 | public function get_to()
345 | {
346 | return $this->to;
347 | }
348 |
349 | /**
350 | * Add to the to recipients list.
351 | *
352 | * @param string|array $email Email address or list of email addresses, array(email => name, email)
353 | * @param string|bool $name Recipient name, false, null or empty for no name
354 | *
355 | * @return $this
356 | */
357 | public function to($email, $name = false)
358 | {
359 | static::add_to_list('to', $email, $name);
360 |
361 | return $this;
362 | }
363 |
364 | /**
365 | * Gets to cc recipients list.
366 | *
367 | * @return array to cc recipients list
368 | */
369 | public function get_cc()
370 | {
371 | return $this->cc;
372 | }
373 |
374 | /**
375 | * Add to the cc recipients list.
376 | *
377 | * @param string|array $email Email address or list of email addresses, array(email => name, email)
378 | * @param string|bool $name Recipient name, false, null or empty for no name
379 | *
380 | * @return $this
381 | */
382 | public function cc($email, $name = false)
383 | {
384 | static::add_to_list('cc', $email, $name);
385 |
386 | return $this;
387 | }
388 |
389 | /**
390 | * Gets to bcc recipients list.
391 | *
392 | * @return array to bcc recipients list
393 | */
394 | public function get_bcc()
395 | {
396 | return $this->bcc;
397 | }
398 |
399 | /**
400 | * Add to the bcc recipients list.
401 | *
402 | * @param string|array $email Email address or list of email addresses, array(email => name, email)
403 | * @param string|bool $name Recipient name, false, null or empty for no name
404 | *
405 | * @return $this
406 | */
407 | public function bcc($email, $name = false)
408 | {
409 | static::add_to_list('bcc', $email, $name);
410 |
411 | return $this;
412 | }
413 |
414 | /**
415 | * Gets to 'reply to' recipients list.
416 | *
417 | * @return array to 'reply to' recipients list
418 | */
419 | public function get_reply_to()
420 | {
421 | return $this->reply_to;
422 | }
423 |
424 | /**
425 | * Add to the 'reply to' list.
426 | *
427 | * @param string|array $email Email address or list of email addresses, array(email => name, email)
428 | * @param string|bool $name The name, false, null or empty for no name
429 | *
430 | * @return $this
431 | */
432 | public function reply_to($email, $name = false)
433 | {
434 | static::add_to_list('reply_to', $email, $name);
435 |
436 | return $this;
437 | }
438 |
439 | /**
440 | * Sets the return-path address
441 | *
442 | * @param string $email The return-path email address
443 | *
444 | * @return $this
445 | */
446 | public function return_path($email)
447 | {
448 | $this->config['return_path'] = (string) $email;
449 |
450 | return $this;
451 | }
452 |
453 | /**
454 | * Add to a recipients list.
455 | *
456 | * @param string $list List to add to (to, cc, bcc)
457 | * @param string|array $email Email address or list of email addresses, array(email => name, email)
458 | * @param string|bool $name Recipient name, false, null or empty for no name
459 | *
460 | * @return void
461 | */
462 | protected function add_to_list($list, $email, $name = false)
463 | {
464 | if ( ! is_array($email))
465 | {
466 | $email = (is_string($name)) ? array($email => $name) : array($email);
467 | }
468 |
469 | if (isset($email['name']) and isset($email['email']))
470 | {
471 | $this->{$list}[$email['email']] = $email;
472 | }
473 | else
474 | {
475 | foreach ($email as $_email => $name)
476 | {
477 | if (is_numeric($_email))
478 | {
479 | $_email = $name;
480 | $name = false;
481 | }
482 |
483 | if ($this->config['encode_headers'] and ! empty($name))
484 | {
485 | $name = $this->encode_mimeheader($name);
486 | }
487 |
488 | $this->{$list}[$_email] = array(
489 | 'name' => $name,
490 | 'email' => $_email,
491 | );
492 | }
493 | }
494 | }
495 |
496 | /**
497 | * Clear the a recipient list.
498 | *
499 | * @param string|array $list List or array of lists
500 | *
501 | * @return void
502 | */
503 | protected function clear_list($list)
504 | {
505 | is_array($list) or $list = array($list);
506 |
507 | foreach ($list as $_list)
508 | {
509 | $this->{$_list} = array();
510 | }
511 | }
512 |
513 | /**
514 | * Clear all recipient lists.
515 | *
516 | * @return $this
517 | */
518 | public function clear_recipients()
519 | {
520 | static::clear_list(array('to', 'cc', 'bcc'));
521 |
522 | return $this;
523 | }
524 |
525 | /**
526 | * Clear all address lists.
527 | *
528 | * @return $this
529 | */
530 | public function clear_addresses()
531 | {
532 | static::clear_list(array('to', 'cc', 'bcc', 'reply_to'));
533 |
534 | return $this;
535 | }
536 |
537 | /**
538 | * Clear the 'to' recipient list.
539 | *
540 | * @return $this
541 | */
542 | public function clear_to()
543 | {
544 | static::clear_list('to');
545 |
546 | return $this;
547 | }
548 |
549 | /**
550 | * Clear the 'cc' recipient list.
551 | *
552 | * @return $this
553 | */
554 | public function clear_cc()
555 | {
556 | static::clear_list('cc');
557 |
558 | return $this;
559 | }
560 |
561 | /**
562 | * Clear the 'bcc' recipient list.
563 | *
564 | * @return $this
565 | */
566 | public function clear_bcc()
567 | {
568 | static::clear_list('bcc');
569 |
570 | return $this;
571 | }
572 |
573 | /**
574 | * Clear the 'reply to' recipient list.
575 | *
576 | * @return $this
577 | */
578 | public function clear_reply_to()
579 | {
580 | static::clear_list('reply_to');
581 |
582 | return $this;
583 | }
584 |
585 | /**
586 | * Sets custom headers.
587 | *
588 | * @param string|array $header Header type or array of headers
589 | * @param string $value Header value
590 | * @return $this
591 | */
592 | public function header($header, $value = null)
593 | {
594 | if(is_array($header))
595 | {
596 | foreach($header as $_header => $_value)
597 | {
598 | empty($_value) or $this->extra_headers[$_header] = $_value;
599 | }
600 | }
601 | else
602 | {
603 | empty($value) or $this->extra_headers[$header] = $value;
604 | }
605 |
606 | return $this;
607 | }
608 |
609 | /**
610 | * Attaches a file to the email. This method will search for the file in the attachment paths set (config/email.php) in the attach_paths array
611 | *
612 | * @param string $file The file to attach
613 | * @param bool $inline Whether to include the file inline
614 | * @param string $cid The content identifier. Used when attaching inline images
615 | * @param string $mime The file's mime-type
616 | * @param string $name The attachment's name
617 | *
618 | * @throws \InvalidAttachmentsException Could not read attachment or attachment is empty
619 | *
620 | * @return $this
621 | */
622 | public function attach($file, $inline = false, $cid = null, $mime = null, $name = null)
623 | {
624 | $file = (array) $file;
625 |
626 | // Ensure the attachment name
627 | if ( ! isset($file[1]))
628 | {
629 | $name or $name = pathinfo($file[0], PATHINFO_BASENAME);
630 | $file[1] = $name;
631 | }
632 |
633 | // Find the attachment.
634 | $file[0] = $this->find_attachment($file[0]);
635 |
636 | if (($contents = file_get_contents($file[0])) === false or empty($contents))
637 | {
638 | throw new \InvalidAttachmentsException('Could not read attachment or attachment is empty: '.$file[0]);
639 | }
640 |
641 | $disp = ($inline) ? 'inline' : 'attachment';
642 |
643 | $cid = empty($cid) ? 'cid:'.md5($file[1]) : trim($cid);
644 | $cid = strpos($cid, 'cid:') === 0 ? $cid : 'cid:'.$cid;
645 |
646 | // Fetch the file mime type.
647 | $mime or $mime = static::attachment_mime($file[0]);
648 |
649 | $this->attachments[$disp][$cid] = array(
650 | 'file' => $file,
651 | 'contents' => chunk_split(base64_encode($contents), 76, $this->config['newline']),
652 | 'mime' => $mime,
653 | 'disp' => $disp,
654 | 'cid' => $cid,
655 | );
656 |
657 | return $this;
658 | }
659 |
660 | /**
661 | * Finds an attachment.
662 | *
663 | * @param $file
664 | *
665 | * @throws \AttachmentNotFoundException Email attachment not found
666 | *
667 | * @return string path of the first found attachment
668 | */
669 | protected function find_attachment($file)
670 | {
671 | foreach($this->get_config('attach_paths') as $path)
672 | {
673 | if(is_file($path.$file))
674 | {
675 | return $path.$file;
676 | }
677 | }
678 |
679 | // No file found?
680 | throw new \AttachmentNotFoundException('Email attachment not found: '.$file);
681 | }
682 |
683 | /**
684 | * Attach a file using string input
685 | *
686 | * @param string $contents File contents
687 | * @param string $filename The files name
688 | * @param string $cid The content identifier. Used when attaching inline images
689 | * @param bool $inline Whether it's an inline attachment
690 | * @param string $mime The file's mime-type
691 | *
692 | * @return $this
693 | */
694 | public function string_attach($contents, $filename, $cid = null, $inline = false, $mime = null)
695 | {
696 | $disp = ($inline) ? 'inline' : 'attachment';
697 | $cid = empty($cid) ? 'cid:'.md5($filename) : trim($cid);
698 | $cid = strpos($cid, 'cid:') === 0 ? $cid : 'cid:'.$cid;
699 | $mime or $mime = static::attachment_mime($filename);
700 |
701 | $this->attachments[$disp][$cid] = array(
702 | 'file' => array(0 => $filename, 1 => pathinfo($filename, PATHINFO_BASENAME)),
703 | 'contents' => static::encode_string($contents, 'base64', $this->config['newline']),
704 | 'mime' => $mime,
705 | 'disp' => $disp,
706 | 'cid' => $cid,
707 | );
708 |
709 | return $this;
710 | }
711 |
712 | /**
713 | * Clear the attachments list.
714 | *
715 | * @return $this
716 | */
717 | public function clear_attachments()
718 | {
719 | $this->attachments = array(
720 | 'inline' => array(),
721 | 'attachment' => array(),
722 | );
723 |
724 | return $this;
725 | }
726 |
727 | /**
728 | * Get the mimetype for an attachment
729 | *
730 | * @param string $file The path to the attachment
731 | *
732 | * @return $this
733 | */
734 | protected static function attachment_mime($file)
735 | {
736 | static $mimes = false;
737 |
738 | if ( ! $mimes)
739 | {
740 | $mimes = \Config::load('mimes');
741 | }
742 |
743 | $ext = pathinfo($file, PATHINFO_EXTENSION);
744 |
745 | $mime = \Arr::get($mimes, $ext, 'application/octet-stream');
746 | is_array($mime) and $mime = reset($mime);
747 |
748 | return $mime;
749 | }
750 |
751 | /**
752 | * Validates all the email addresses.
753 | *
754 | * @return bool|array True if all are valid or an array of recipients which failed validation.
755 | */
756 | protected function validate_addresses()
757 | {
758 | $failed = array();
759 |
760 | foreach (array('to', 'cc', 'bcc') as $list)
761 | {
762 | foreach ($this->{$list} as $recipient)
763 | {
764 | if ( ! filter_var($recipient['email'], FILTER_VALIDATE_EMAIL))
765 | {
766 | $failed[$list][] = $recipient;
767 | }
768 | }
769 | }
770 |
771 | if (count($failed) === 0)
772 | {
773 | return true;
774 | }
775 |
776 | return $failed;
777 | }
778 |
779 | /**
780 | * Sets unique message boundaries
781 | */
782 | protected function set_boundaries()
783 | {
784 | $uniq_id = md5(uniqid(microtime(true)));
785 |
786 | // Message part boundary, (separates message and attachments).
787 | $this->boundaries[0] = 'B1_'.$uniq_id;
788 |
789 | // Message body boundary (separates message, alt message)
790 | $this->boundaries[1] = 'B2_'.$uniq_id;
791 |
792 | $this->boundaries[2] = 'B3_'.$uniq_id;
793 | }
794 |
795 | /**
796 | * Initiates the sending process.
797 | *
798 | * @param bool Whether to validate the addresses, falls back to config setting
799 | *
800 | * @throws \EmailValidationFailedException One or more email addresses did not pass validation
801 | * @throws \FuelException Cannot send without from address/Cannot send without recipients
802 | *
803 | * @return bool
804 | */
805 | public function send($validate = null)
806 | {
807 | if (empty($this->to) and empty($this->cc) and empty($this->bcc))
808 | {
809 | throw new \FuelException('Cannot send email without recipients.');
810 | }
811 |
812 | if (($from = $this->config['from']['email']) === false or empty($from))
813 | {
814 | throw new \FuelException('Cannot send without from address.');
815 | }
816 |
817 | // Check which validation bool to use
818 | is_bool($validate) or $validate = $this->config['validate'];
819 |
820 | // Validate the email addresses if specified
821 | if ($validate and ($failed = $this->validate_addresses()) !== true)
822 | {
823 | $this->invalid_addresses = $failed;
824 |
825 | $error_str = '';
826 | foreach($failed as $_list => $_contents)
827 | {
828 | $error_str .= $_list.': '.htmlentities(static::format_addresses($_contents)).'.'.PHP_EOL;
829 | }
830 |
831 | throw new \EmailValidationFailedException('One or more email addresses did not pass validation: '.$error_str);
832 | }
833 |
834 | // Reset the headers
835 | $this->headers = array();
836 |
837 | // Set the email boundaries
838 | $this->set_boundaries();
839 |
840 | // Set RFC 822 formatted date
841 | $this->set_header('Date', date('r'));
842 |
843 | // Set return path
844 | if ($this->config['return_path'] !== false)
845 | {
846 | $this->set_header('Return-Path', $this->config['return_path']);
847 | }
848 | else
849 | {
850 | $this->set_header('Return-Path', $this->config['from']['email']);
851 | }
852 |
853 | if ( ! empty($this->config['force_to']))
854 | {
855 | foreach (array('to', 'cc', 'bcc', 'reply_to') as $list)
856 | {
857 | foreach ($this->{$list} as $index => $value)
858 | {
859 | $this->{$list}[$index]['email'] = $this->config['force_to'];
860 | }
861 | }
862 | $this->set_header('Return-Path', $this->config['force_to']);
863 |
864 | if (empty($this->reply_to))
865 | {
866 | $this->reply_to($this->config['force_to']);
867 | }
868 | }
869 |
870 | if (($this instanceof Email_Driver_Mail) !== true)
871 | {
872 | if ( ! empty($this->to))
873 | {
874 | // Set from
875 | $this->set_header('To', static::format_addresses($this->to));
876 | }
877 |
878 | // Set subject
879 | $this->set_header('Subject', $this->subject);
880 | }
881 |
882 | $this->set_header('From', static::format_addresses(array($this->config['from'])));
883 |
884 | foreach (array('cc' => 'Cc', 'bcc' => 'Bcc', 'reply_to' => 'Reply-To') as $list => $header)
885 | {
886 | if (count($this->{$list}) > 0)
887 | {
888 | $this->set_header($header, static::format_addresses($this->{$list}));
889 | }
890 | }
891 |
892 | // Set message id
893 | $this->set_header('Message-ID', $this->get_message_id());
894 |
895 | // Set mime version
896 | $this->set_header('MIME-Version', '1.0');
897 |
898 | // Set priority
899 | $this->set_header('X-Priority', $this->config['priority']);
900 |
901 | // Set mailer useragent
902 | $this->set_header('X-Mailer', $this->config['useragent']);
903 |
904 | $newline = $this->config['newline'];
905 |
906 | $this->type = $this->get_mail_type();
907 |
908 | $encoding = $this->config['encoding'];
909 | $charset = $this->config['charset'];
910 |
911 | if ($this->type !== 'plain' and $this->type !== 'html')
912 | {
913 | $this->set_header('Content-Type', $this->get_content_type($this->type, $newline."\tboundary=\"".$this->boundaries[0].'"'));
914 | }
915 | else
916 | {
917 | $this->set_header('Content-Transfer-Encoding', $encoding);
918 | $this->set_header('Content-Type', 'text/'.$this->type.'; charset="'.$this->config['charset'].'"');
919 | }
920 |
921 | // Encode messages
922 | $this->body = static::encode_string($this->body, $encoding, $newline);
923 | $this->alt_body = static::encode_string($this->alt_body, $encoding, $newline);
924 |
925 | // Set wordwrapping
926 | $wrapping = $this->config['wordwrap'];
927 | $qp_mode = $encoding === 'quoted-printable';
928 |
929 | $is_html = (stripos($this->type, 'html') !== false);
930 |
931 | // Don't wrap the text when using quoted-printable
932 | if ($wrapping and ! $qp_mode)
933 | {
934 | $this->body = static::wrap_text($this->body, $wrapping, $newline, $is_html);
935 | $this->alt_body = static::wrap_text($this->alt_body, $wrapping, $newline, false);
936 | }
937 |
938 | // Send
939 | $this->_send();
940 |
941 | return true;
942 | }
943 |
944 | /**
945 | * Get the invalid addresses
946 | *
947 | * @return array An array of invalid email addresses
948 | */
949 | public function get_invalid_addresses()
950 | {
951 | return $this->invalid_addresses;
952 | }
953 |
954 | /**
955 | * Sets the message headers
956 | *
957 | * @param string $header The header type
958 | * @param string $value The header value
959 | */
960 | protected function set_header($header, $value)
961 | {
962 | empty($value) or $this->headers[$header] = $value;
963 | }
964 |
965 | /**
966 | * Gets the header
967 | *
968 | * @param string $header The header name. Will return all headers, if not specified
969 | * @param bool $formatted Adds newline as suffix and colon as prefix, if true
970 | *
971 | * @return string|array Mail header or array of headers
972 | */
973 | protected function get_header($header = null, $formatted = true)
974 | {
975 | if ($header === null)
976 | {
977 | return $this->headers;
978 | }
979 |
980 | if (array_key_exists($header, $this->headers))
981 | {
982 | $prefix = ($formatted) ? $header.': ' : '';
983 | $suffix = ($formatted) ? $this->config['newline'] : '';
984 | return $prefix.$this->headers[$header].$suffix;
985 | }
986 |
987 | return '';
988 | }
989 |
990 | /**
991 | * Encodes a mimeheader.
992 | *
993 | * @param string $header Header to encode
994 | *
995 | * @return string Mimeheader encoded string
996 | */
997 | protected function encode_mimeheader($header)
998 | {
999 | // we need mbstring for this
1000 | if ( ! MBSTRING)
1001 | {
1002 | throw new \RuntimeException('Email requires the multibyte ("mbstring") package to be installed!');
1003 | }
1004 |
1005 | // determine the transfer encoding to be used
1006 | $transfer_encoding = ($this->config['encoding'] === 'quoted-printable') ? 'Q' : 'B' ;
1007 |
1008 | // encode
1009 | $header = mb_encode_mimeheader($header, $this->config['charset'], $transfer_encoding, $this->config['newline']);
1010 |
1011 | // and return it
1012 | return $header;
1013 | }
1014 |
1015 | /**
1016 | * Get the attachment headers
1017 | *
1018 | */
1019 | protected function get_attachment_headers($type, $boundary)
1020 | {
1021 | $return = '';
1022 |
1023 | $newline = $this->config['newline'];
1024 |
1025 | foreach ($this->attachments[$type] as $attachment)
1026 | {
1027 | $return .= '--'.$boundary.$newline;
1028 | $return .= 'Content-Type: '.$attachment['mime'].'; name="'.$attachment['file'][1].'"'.$newline;
1029 | $return .= 'Content-Transfer-Encoding: base64'.$newline;
1030 | $type === 'inline' and $return .= 'Content-ID: <'.substr($attachment['cid'], 4).'>'.$newline;
1031 | $return .= 'Content-Disposition: '.$type.'; filename="'.$attachment['file'][1].'"'.$newline.$newline;
1032 | $return .= $attachment['contents'].$newline.$newline;
1033 | }
1034 |
1035 | return $return;
1036 | }
1037 |
1038 | /**
1039 | * Get a unique message id
1040 | *
1041 | * @return string The message id
1042 | */
1043 | protected function get_message_id()
1044 | {
1045 | $from = $this->config['from']['email'];
1046 | return "<".uniqid('').strstr($from, '@').">";
1047 | }
1048 |
1049 | /**
1050 | * Returns the mail's type
1051 | *
1052 | * @return string Mail type
1053 | */
1054 | protected function get_mail_type()
1055 | {
1056 | $return = $this->config['is_html'] ? 'html' : 'plain' ;
1057 | $alt = trim($this->alt_body);
1058 | $return .= ($this->config['is_html'] and ! empty($alt)) ? '_alt' : '';
1059 | $return .= ($this->config['is_html'] and count($this->attachments['inline'])) ? '_inline' : '';
1060 | $return .= (count($this->attachments['attachment'])) ? '_attach' : '';
1061 | return $return;
1062 | }
1063 |
1064 | /**
1065 | * Returns the content type
1066 | *
1067 | * @param string $mail_type Type of email (plain, html, html_inline, etc…)
1068 | * @param $boundary
1069 | *
1070 | * @throws \FuelException Invalid content-type
1071 | *
1072 | * @return string Mail content type
1073 | */
1074 | protected function get_content_type($mail_type, $boundary)
1075 | {
1076 | $related = $this->config['force_mixed'] ? 'multipart/mixed; ' : 'multipart/related; ';
1077 |
1078 | switch ($mail_type)
1079 | {
1080 | case 'plain':
1081 | return 'text/plain';
1082 | case 'plain_attach':
1083 | case 'html_attach':
1084 | return $related.$boundary;
1085 | case 'html':
1086 | return 'text/html';
1087 | case 'html_alt_attach':
1088 | case 'html_inline_attach':
1089 | case 'html_alt_inline_attach':
1090 | return 'multipart/mixed; '.$boundary;
1091 | case 'html_alt_inline':
1092 | case 'html_alt':
1093 | case 'html_inline':
1094 | return 'multipart/alternative; '.$boundary;
1095 | default:
1096 | throw new \FuelException('Invalid content-type'.$mail_type);
1097 | }
1098 | }
1099 |
1100 | /**
1101 | * Builds the headers and body
1102 | *
1103 | * @param bool $no_bcc Whether to exclude Bcc headers.
1104 | *
1105 | * @return array An array containing the headers and the body
1106 | */
1107 | protected function build_message($no_bcc = false)
1108 | {
1109 | $newline = $this->config['newline'];
1110 | $charset = $this->config['charset'];
1111 | $encoding = $this->config['encoding'];
1112 |
1113 | $headers = '';
1114 | $parts = array('Date', 'Return-Path', 'From', 'To', 'Cc', 'Bcc', 'Reply-To', 'Subject', 'Message-ID', 'X-Priority', 'X-Mailer', 'MIME-Version', 'Content-Type');
1115 | $no_bcc and array_splice($parts, 5, 1);
1116 |
1117 | foreach ($parts as $part)
1118 | {
1119 | $headers .= $this->get_header($part);
1120 | }
1121 |
1122 | foreach ($this->extra_headers as $header => $value)
1123 | {
1124 | $headers .= $header.': '.$value.$newline;
1125 | }
1126 |
1127 | $headers .= $newline;
1128 |
1129 | $body = '';
1130 |
1131 | if ($this->type === 'plain' or $this->type === 'html')
1132 | {
1133 | $body = $this->body;
1134 | }
1135 | else
1136 | {
1137 | switch ($this->type)
1138 | {
1139 | case 'html_alt':
1140 | $body .= '--'.$this->boundaries[0].$newline;
1141 | $body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
1142 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1143 | $body .= $this->alt_body.$newline.$newline;
1144 | $body .= '--'.$this->boundaries[0].$newline;
1145 | $body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
1146 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1147 | $body .= $this->body.$newline.$newline;
1148 | $body .= '--'.$this->boundaries[0].'--';
1149 | break;
1150 | case 'plain_attach':
1151 | case 'html_attach':
1152 | case 'html_inline':
1153 | $body .= '--'.$this->boundaries[0].$newline;
1154 | $text_type = (stripos($this->type, 'html') !== false) ? 'html' : 'plain';
1155 | $body .= 'Content-Type: text/'.$text_type.'; charset="'.$charset.'"'.$newline;
1156 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1157 | $body .= $this->body.$newline.$newline;
1158 | $attach_type = (stripos($this->type, 'attach') !== false) ? 'attachment' : 'inline';
1159 | $body .= $this->get_attachment_headers($attach_type, $this->boundaries[0]);
1160 | $body .= '--'.$this->boundaries[0].'--';
1161 | break;
1162 | case 'html_alt_inline':
1163 | $body .= '--'.$this->boundaries[0].$newline;
1164 | $body .= 'Content-Type: text/plain'.'; charset="'.$charset.'"'.$newline;
1165 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1166 | $body .= $this->alt_body.$newline.$newline;
1167 | $body .= '--'.$this->boundaries[0].$newline;
1168 | $body .= 'Content-Type: multipart/related;'.$newline."\tboundary=\"{$this->boundaries[1]}\"".$newline.$newline;
1169 | $body .= '--'.$this->boundaries[1].$newline;
1170 | $body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
1171 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1172 | $body .= $this->body.$newline.$newline;
1173 | $body .= $this->get_attachment_headers('inline', $this->boundaries[1]);
1174 | $body .= '--'.$this->boundaries[1].'--'.$newline.$newline;
1175 | $body .= '--'.$this->boundaries[0].'--';
1176 | break;
1177 | case 'html_alt_attach':
1178 | case 'html_inline_attach':
1179 | $body .= '--'.$this->boundaries[0].$newline;
1180 | $body .= 'Content-Type: multipart/alternative;'.$newline."\t boundary=\"{$this->boundaries[1]}\"".$newline.$newline;
1181 | if (stripos($this->type, 'alt') !== false)
1182 | {
1183 | $body .= '--'.$this->boundaries[1].$newline;
1184 | $body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
1185 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1186 | $body .= $this->alt_body.$newline.$newline;
1187 | }
1188 | $body .= '--'.$this->boundaries[1].$newline;
1189 | $body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
1190 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1191 | $body .= $this->body.$newline.$newline;
1192 | if (stripos($this->type, 'inline') !== false)
1193 | {
1194 | $body .= $this->get_attachment_headers('inline', $this->boundaries[1]);
1195 | $body .= $this->alt_body.$newline.$newline;
1196 | }
1197 | $body .= '--'.$this->boundaries[1].'--'.$newline.$newline;
1198 | $body .= $this->get_attachment_headers('attachment', $this->boundaries[0]);
1199 | $body .= '--'.$this->boundaries[0].'--';
1200 | break;
1201 | case 'html_alt_inline_attach':
1202 | $body .= '--'.$this->boundaries[0].$newline;
1203 | $body .= 'Content-Type: multipart/alternative;'.$newline."\t boundary=\"{$this->boundaries[1]}\"".$newline.$newline;
1204 | $body .= '--'.$this->boundaries[1].$newline;
1205 | $body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
1206 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1207 | $body .= $this->alt_body.$newline.$newline;
1208 | $body .= '--'.$this->boundaries[1].$newline;
1209 | $body .= 'Content-Type: multipart/related;'.$newline."\t boundary=\"{$this->boundaries[2]}\"".$newline.$newline;
1210 | $body .= '--'.$this->boundaries[2].$newline;
1211 | $body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
1212 | $body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
1213 | $body .= $this->body.$newline.$newline;
1214 | $body .= $this->get_attachment_headers('inline', $this->boundaries[2]);
1215 | $body .= $this->alt_body.$newline.$newline;
1216 | $body .= '--'.$this->boundaries[2].'--'.$newline.$newline;
1217 | $body .= '--'.$this->boundaries[1].'--'.$newline.$newline;
1218 | $body .= $this->get_attachment_headers('attachment', $this->boundaries[0]);
1219 | $body .= '--'.$this->boundaries[0].'--';
1220 | break;
1221 | }
1222 |
1223 | }
1224 |
1225 | return array(
1226 | 'header' => $headers,
1227 | 'body' => $body,
1228 | );
1229 | }
1230 |
1231 | /**
1232 | * Wraps the body or alt text
1233 | *
1234 | * @param string $message The text to wrap
1235 | * @param int $length The max line length
1236 | * @param string $newline The newline delimiter
1237 | * @param bool $is_html
1238 | *
1239 | * @internal param string $charset the text charset
1240 | * @internal param bool $qp_mode whether the text is quoted printable encoded
1241 | *
1242 | * @return string
1243 | */
1244 | protected static function wrap_text($message, $length, $newline, $is_html = true)
1245 | {
1246 | $length = ($length > 76) ? 76 : $length;
1247 | $is_html and $message = preg_replace('/[\r\n\t ]+/m', ' ', $message);
1248 | $message = wordwrap($message, $length, $newline, false);
1249 |
1250 | return $message;
1251 | }
1252 |
1253 | /**
1254 | * Standardize newlines.
1255 | *
1256 | * @param string $string String to prep
1257 | * @param string $newline The newline delimiter
1258 | *
1259 | * @return string String with standardized newlines
1260 | */
1261 | protected static function prep_newlines($string, $newline = null)
1262 | {
1263 | $newline or $newline = \Config::get('email.defaults.newline', "\n");
1264 |
1265 | $replace = array(
1266 | "\r\n" => "\n",
1267 | "\n\r" => "\n",
1268 | "\r" => "\n",
1269 | "\n" => $newline,
1270 | );
1271 |
1272 | foreach ($replace as $from => $to)
1273 | {
1274 | $string = str_replace($from, $to, $string);
1275 | }
1276 |
1277 | return $string;
1278 | }
1279 |
1280 | /**
1281 | * Encodes a string in the given encoding.
1282 | *
1283 | * @param string $string String to encode
1284 | * @param string $encoding The charset
1285 | * @param string $newline Newline delimeter
1286 | *
1287 | * @throws \InvalidEmailStringEncoding Encoding is not a supported by encoding method
1288 | *
1289 | * @return string Encoded string
1290 | */
1291 | protected static function encode_string($string, $encoding, $newline = null)
1292 | {
1293 | $newline or $newline = \Config::get('email.defaults.newline', "\n");
1294 |
1295 | switch ($encoding)
1296 | {
1297 | case 'quoted-printable':
1298 | return quoted_printable_encode($string);
1299 | case '7bit':
1300 | case '8bit':
1301 | return static::prep_newlines(rtrim($string, $newline), $newline);
1302 | case 'base64':
1303 | return chunk_split(base64_encode($string), 76, $newline);
1304 | default:
1305 | throw new \InvalidEmailStringEncoding($encoding.' is not a supported encoding method.');
1306 | }
1307 | }
1308 |
1309 | /**
1310 | * Returns a formatted string of email addresses.
1311 | *
1312 | * @param array $addresses Array of adresses array(array(name=>name, email=>email));
1313 | *
1314 | * @return string Correctly formatted email addresses
1315 | */
1316 | protected static function format_addresses($addresses)
1317 | {
1318 | $return = array();
1319 |
1320 | foreach ($addresses as $recipient)
1321 | {
1322 | $recipient['name'] and $recipient['email'] = '"'.$recipient['name'].'" <'.$recipient['email'].'>';
1323 | $return[] = $recipient['email'];
1324 | }
1325 |
1326 | return join(', ', $return);
1327 | }
1328 |
1329 | /**
1330 | * Generates an alt body
1331 | *
1332 | * @param string $html html Body to al body generate from
1333 | * @param int $wordwrap Wordwrap length
1334 | * @param string $newline Line separator to use
1335 | *
1336 | * @return string The generated alt body
1337 | */
1338 | protected static function generate_alt($html, $wordwrap, $newline)
1339 | {
1340 | $html = preg_replace('/[ | ]{2,}/m', ' ', $html);
1341 | $html = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $html)));
1342 | $lines = explode($newline, $html);
1343 | $result = array();
1344 | $first_newline = true;
1345 | foreach ($lines as $line)
1346 | {
1347 | $line = trim($line);
1348 | if ( ! empty($line) or $first_newline)
1349 | {
1350 | $first_newline = false;
1351 | $result[] = $line;
1352 | }
1353 | else
1354 | {
1355 | $first_newline = true;
1356 | }
1357 | }
1358 |
1359 | $html = join($newline, $result);
1360 |
1361 | if ( ! $wordwrap)
1362 | {
1363 | return $html;
1364 | }
1365 |
1366 | return wordwrap($html, $wordwrap, $newline, true);
1367 | }
1368 |
1369 | /**
1370 | * Initiates the sending process.
1371 | *
1372 | * @return bool success boolean
1373 | */
1374 | abstract protected function _send();
1375 |
1376 | }
1377 |
--------------------------------------------------------------------------------
/classes/email/driver/mail.php:
--------------------------------------------------------------------------------
1 | build_message();
27 | $return_path = ($this->config['return_path'] !== false) ? $this->config['return_path'] : $this->config['from']['email'];
28 | if ( ! @mail(static::format_addresses($this->to), $this->subject, $message['body'], $message['header'], '-oi -f '.$return_path))
29 | {
30 | throw new \EmailSendingFailedException('Failed sending email');
31 | }
32 | return true;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/classes/email/driver/mailgun.php:
--------------------------------------------------------------------------------
1 | type = 'html';
20 |
21 | $message = $this->build_message();
22 |
23 | $config_mailgun = $this->config['mailgun'];
24 |
25 | $endpoint = isset($config_mailgun['endpoint']) ? $config_mailgun['endpoint'] : null;
26 |
27 | if (empty($endpoint))
28 | {
29 | $mg = \Mailgun\Mailgun::create($config_mailgun['key']);
30 | }
31 | else
32 | {
33 | $mg = \Mailgun\Mailgun::create($config_mailgun['key'], $endpoint);
34 | }
35 |
36 | // Mailgun does not consider these "arbitrary headers"
37 | $exclude = array('From'=>'From', 'To'=>'To', 'Cc'=>'Cc', 'Bcc'=>'Bcc', 'Subject'=>'Subject', 'Content-Type'=>'Content-Type', 'Content-Transfer-Encoding' => 'Content-Transfer-Encoding');
38 | $headers = array_diff_key($this->headers, $exclude);
39 |
40 | foreach ($this->extra_headers as $header => $value)
41 | {
42 | $headers[$header] = $value;
43 | }
44 |
45 | // Standard required fields
46 | $post_data = array(
47 | 'from' => static::format_addresses(array(array('email' => $this->config['from']['email'], 'name' => $this->config['from']['name']))),
48 | 'to' => static::format_addresses($this->to),
49 | 'subject' => $this->subject,
50 | 'html' => $message['body'],
51 | 'attachment' => array(),
52 | 'inline' => array()
53 | );
54 |
55 | // Optionally cc, bcc and alt_body
56 | $this->cc and $post_data['cc'] = static::format_addresses($this->cc);
57 | $this->bcc and $post_data['bcc'] = static::format_addresses($this->bcc);
58 | $this->alt_body and $post_data['text'] = $this->alt_body;
59 |
60 | // Mailgun's "arbitrary headers" are h: prefixed
61 | foreach ($headers as $name => $value)
62 | {
63 | $post_data["h:{$name}"] = $value;
64 | }
65 |
66 | foreach ($this->attachments['attachment'] as $cid => $file)
67 | {
68 | $post_data['attachment'][] = array('filePath' => $file['file'][0], 'remoteName' => $file['file'][1]);
69 | }
70 |
71 | foreach ($this->attachments['inline'] as $cid => $file)
72 | {
73 | $post_data['inline'][] = array('filePath' => $file['file'][0], 'remoteName' => substr($cid, 4));
74 | }
75 |
76 | // And send the message out
77 | $mg->messages()->send($config_mailgun['domain'], $post_data);
78 |
79 | return true;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/classes/email/driver/mandrill.php:
--------------------------------------------------------------------------------
1 | config['mandrill']['key']);
65 |
66 | $message = new Mandrill_Messages($mandrill);
67 |
68 | $headers = $this->extra_headers;
69 |
70 | // Get recipients
71 | $to = $this->build_rcpt();
72 | $cc = $this->build_rcpt('cc');
73 | $bcc = $this->build_rcpt('bcc');
74 |
75 | $to = array_merge($bcc, $cc, $to);
76 |
77 | // Get recipient merge vars
78 | $merge_vars = array();
79 |
80 | foreach ($this->rcpt_merge_vars as $rcpt => $_merge_vars)
81 | {
82 | $merge_vars[] = array(
83 | 'rcpt' => $rcpt,
84 | 'vars' => \Arr::keyval_to_assoc($_merge_vars, 'name', 'content'),
85 | );
86 | }
87 |
88 | // Get recipient meta data
89 | $metadata = array();
90 |
91 | foreach ($this->rcpt_metadata as $rcpt => $_metadata)
92 | {
93 | $metadata[] = array(
94 | 'rcpt' => $rcpt,
95 | 'values' => $_metadata,
96 | );
97 | }
98 |
99 | // Get attachments
100 | $attachments = array();
101 |
102 | foreach ($this->attachments['attachment'] as $cid => $attachment)
103 | {
104 | $attachments[] = array(
105 | 'type' => $attachment['mime'],
106 | 'name' => $attachment['file'][1],
107 | 'content' => $attachment['contents'],
108 | );
109 | }
110 |
111 | // Get inline images
112 | $images = array();
113 |
114 | foreach ($this->attachments['inline'] as $cid => $attachment)
115 | {
116 | if (\Str::starts_with($attachment['mime'], 'image/'))
117 | {
118 | $name = substr($cid, 4); // remove cid:
119 |
120 | $images[] = array(
121 | 'type' => $attachment['mime'],
122 | 'name' => $name,
123 | 'content' => $attachment['contents'],
124 | );
125 | }
126 | }
127 |
128 | // Get reply-to addresses
129 | if ( ! empty($this->reply_to))
130 | {
131 | $headers['Reply-To'] = static::format_addresses($this->reply_to);
132 | }
133 |
134 | $important = false;
135 |
136 | if (in_array($this->config['priority'], array(\Email::P_HIGH, \Email::P_HIGHEST)))
137 | {
138 | $important = true;
139 | }
140 |
141 | $message_data = array(
142 | 'html' => $this->body,
143 | 'text' => isset($this->alt_body) ? $this->alt_body : '',
144 | 'subject' => $this->subject,
145 | 'from_email' => $this->config['from']['email'],
146 | 'from_name' => $this->config['from']['name'],
147 | 'to' => $to,
148 | 'headers' => $headers,
149 | 'global_merge_vars' => \Arr::keyval_to_assoc($this->merge_vars, 'name', 'content'),
150 | 'merge_vars' => $merge_vars,
151 | 'metadata' => $this->metadata,
152 | 'recipient_metadata' => $metadata,
153 | 'attachments' => $attachments,
154 | 'images' => $images,
155 | 'important' => $important,
156 | );
157 |
158 | $message_options = \Arr::filter_keys($this->get_config('mandrill.message_options', array()), array_keys($message_data), true);
159 |
160 | $message_data = \Arr::merge($message_data, $message_options);
161 |
162 | $send_options = extract($this->config['mandrill']['send_options'], EXTR_SKIP);
163 |
164 | $message->send($message_data, $async, $ip_pool, $send_at);
165 |
166 | return true;
167 | }
168 |
169 | /**
170 | * {@inheritdoc}
171 | */
172 | public function attach($file, $inline = false, $cid = null, $mime = null, $name = null)
173 | {
174 | parent::attach($file, $inline, $cid, $mime, $name);
175 |
176 | if ($inline === true)
177 | {
178 | // Check the last attachment
179 | $attachment = end($this->attachments['inline']);
180 |
181 | if ( ! \Str::starts_with($attachment['mime'], 'image/'))
182 | {
183 | throw new \InvalidAttachmentsException('Non-image inline attachments are not supported by this driver.');
184 | }
185 | }
186 | }
187 |
188 | /**
189 | * Add type to recipient list
190 | *
191 | * @param string $list to, cc, bcc
192 | * @return array
193 | */
194 | protected function build_rcpt($list = 'to')
195 | {
196 | return array_map(function ($item) use ($list)
197 | {
198 | $item['type'] = $list;
199 |
200 | return $item;
201 | }, $this->{$list});
202 | }
203 |
204 | /**
205 | * {@inheritdoc}
206 | */
207 | protected function clear_list($list)
208 | {
209 | is_array($list) or $list = array($list);
210 |
211 | foreach ($list as $_list)
212 | {
213 | $rcpt = array_keys($this->{$_list});
214 | \Arr::delete($this->rcpt_merge_vars, $rcpt);
215 | \Arr::delete($this->rcpt_metadata, $rcpt);
216 | }
217 |
218 | return parent::clear_list($list);
219 | }
220 |
221 | /**
222 | * Get merge vars
223 | *
224 | * @param mixed $key Null for all, string for specific
225 | * @param mixed $rcpt Null for global, string for recipient
226 | * @return array
227 | */
228 | public function get_merge_vars($key = null, $rcpt = null)
229 | {
230 | if (is_null($rcpt))
231 | {
232 | return \Arr::get($this->merge_vars, $key);
233 | }
234 | elseif (isset($this->rcpt_merge_vars[$rcpt]))
235 | {
236 | return \Arr::get($this->rcpt_merge_vars[$rcpt], $key);
237 | }
238 | }
239 |
240 | /**
241 | * Add merge vars
242 | *
243 | * @param array $merge_vars Key-value pairs
244 | * @param mixed $rcpt Null for global, string for recipient
245 | * @return array
246 | */
247 | public function add_merge_vars(array $merge_vars, $rcpt = null)
248 | {
249 | if (is_null($rcpt))
250 | {
251 | $this->merge_vars = $merge_vars;
252 | }
253 | else
254 | {
255 | $this->rcpt_merge_vars[$rcpt] = $merge_vars;
256 | }
257 |
258 | return $this;
259 | }
260 |
261 | /**
262 | * Set one or several merge vars
263 | *
264 | * @param mixed $key Array for many vars, string for one
265 | * @param mixed $value In case of many vars it is ommited
266 | * @param mixed $rcpt Null for global, string for recipient
267 | * @return object $this
268 | */
269 | public function set_merge_var($key, $value = null, $rcpt = null)
270 | {
271 | is_array($key) or $key = array($key => $value);
272 |
273 | if (is_null($rcpt))
274 | {
275 | $this->merge_vars = \Arr::merge($this->merge_vars, $key);
276 | }
277 | else
278 | {
279 | $merge_vars = \Arr::get($this->rcpt_merge_vars, $rcpt, array());
280 | $this->rcpt_merge_vars[$rcpt] = \Arr::merge($merge_vars, $key);
281 | }
282 |
283 | return $this;
284 | }
285 |
286 | /**
287 | * Get metadata
288 | *
289 | * @param mixed $key Null for all, string for specific
290 | * @param mixed $rcpt Null for global, string for recipient
291 | * @return array
292 | */
293 | public function get_metadata($key = null, $rcpt = null)
294 | {
295 | if (is_null($rcpt))
296 | {
297 | return \Arr::get($this->metadata, $key);
298 | }
299 | elseif (isset($this->rcpt_metadata[$rcpt]))
300 | {
301 | return \Arr::get($this->rcpt_metadata[$rcpt], $key);
302 | }
303 | }
304 |
305 | /**
306 | * Add metadata
307 | *
308 | * @param array $metadata Key-value pairs
309 | * @param mixed $rcpt Null for global, string for recipient
310 | * @return array
311 | */
312 | public function add_metadata(array $metadata, $rcpt = null)
313 | {
314 | if (is_null($rcpt))
315 | {
316 | $this->metadata = $metadata;
317 | }
318 | else
319 | {
320 | $this->rcpt_metadata[$rcpt] = $metadata;
321 | }
322 |
323 | return $this;
324 | }
325 |
326 | /**
327 | * Set one or several metadata
328 | *
329 | * @param mixed $key Array for many, string for one
330 | * @param mixed $value In case of many it is ommited
331 | * @param mixed $rcpt Null for global, string for recipient
332 | * @return object $this
333 | */
334 | public function set_metadata($key, $value = null, $rcpt = null)
335 | {
336 | is_array($key) or $key = array($key => $value);
337 |
338 | if (is_null($rcpt))
339 | {
340 | $this->metadata = \Arr::merge($this->metadata, $key);
341 | }
342 | else
343 | {
344 | $metadata = \Arr::get($this->rcpt_metadata, $rcpt, array());
345 | $this->rcpt_metadata[$rcpt] = \Arr::merge($metadata, $key);
346 | }
347 |
348 | return $this;
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/classes/email/driver/noop.php:
--------------------------------------------------------------------------------
1 | build_message();
25 |
26 | logger(\Fuel::L_INFO, 'To: '.static::format_addresses($this->to), 'Email NoOp driver');
27 | logger(\Fuel::L_INFO, 'Subject: '.$this->subject, 'Email NoOp driver');
28 | logger(\Fuel::L_INFO, 'Header: '.$message['header'], 'Email NoOp driver');
29 | logger(\Fuel::L_INFO, 'Body: '.$message['body'], 'Email NoOp driver');
30 |
31 | return true;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/classes/email/driver/sendmail.php:
--------------------------------------------------------------------------------
1 | build_message();
33 |
34 | // Open a connection
35 | $return_path = ($this->config['return_path'] !== false) ? $this->config['return_path'] : $this->config['from']['email'];
36 | $handle = @popen($this->config['sendmail_path'] . " -oi -f ".$return_path." -t", 'w');
37 |
38 | // No connection?
39 | if(! is_resource($handle))
40 | {
41 | throw new \SendmailConnectionException('Could not open a sendmail connection at: '.$this->config['sendmail_path']);
42 | }
43 |
44 | // Send the headers
45 | fputs($handle, $message['header']);
46 |
47 | // Send the body
48 | fputs($handle, $message['body']);
49 |
50 | if(pclose($handle) === -1)
51 | {
52 | throw new \SendmailFailedException('Failed sending email through sendmail.');
53 | }
54 |
55 | return true;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/classes/email/driver/smtp.php:
--------------------------------------------------------------------------------
1 | smtp_connection)
32 | {
33 | $this->smtp_disconnect();
34 | }
35 | }
36 |
37 | /**
38 | * The SMTP connection
39 | */
40 | protected $smtp_connection = null;
41 |
42 | /**
43 | * Initiates the sending process.
44 | *
45 | * @return bool success boolean
46 | */
47 | protected function _send()
48 | {
49 | // send the email
50 | try
51 | {
52 | return $this->_send_email();
53 | }
54 |
55 | // something failed
56 | catch (\Exception $e)
57 | {
58 | // disconnect if needed
59 | if ($this->smtp_connection)
60 | {
61 | if ($e instanceOf SmtpTimeoutException)
62 | {
63 | // simply close the connection
64 | fclose($this->smtp_connection);
65 | $this->smtp_connection = null;
66 | }
67 | else
68 | {
69 | // proper close, with a QUIT
70 | $this->smtp_disconnect();
71 | }
72 | }
73 |
74 | // rethrow the exception
75 | throw $e;
76 | }
77 | }
78 |
79 | /**
80 | * Sends the actual email
81 | *
82 | * @return bool success boolean
83 | */
84 | protected function _send_email()
85 | {
86 | $message = $this->build_message(true);
87 |
88 | if(empty($this->config['smtp']['host']) or empty($this->config['smtp']['port']))
89 | {
90 | throw new \FuelException('Must supply a SMTP host and port, none given.');
91 | }
92 |
93 | // Use authentication?
94 | $authenticate = (empty($this->smtp_connection) and ! empty($this->config['smtp']['username']) and ! empty($this->config['smtp']['password']));
95 |
96 | // Connect
97 | $this->smtp_connect();
98 |
99 | // Authenticate when needed
100 | $authenticate and $this->smtp_authenticate();
101 |
102 | // Set return path
103 | $return_path = empty($this->config['return_path']) ? $this->config['from']['email'] : $this->config['return_path'];
104 | $this->smtp_send('MAIL FROM:<' . $return_path .'>', 250);
105 |
106 | foreach(array('to', 'cc', 'bcc') as $list)
107 | {
108 | foreach($this->{$list} as $recipient)
109 | {
110 | $this->smtp_send('RCPT TO:<'.$recipient['email'].'>', array(250, 251));
111 | }
112 | }
113 |
114 | // Prepare for data sending
115 | $this->smtp_send('DATA', 354);
116 |
117 | $lines = explode($this->config['newline'], $message['header'].preg_replace('/^\./m', '..$1', $message['body']));
118 |
119 | foreach($lines as $line)
120 | {
121 | if(substr($line, 0, 1) === '.')
122 | {
123 | $line = '.'.$line;
124 | }
125 |
126 | fputs($this->smtp_connection, $line.$this->config['newline']);
127 | }
128 |
129 | // Finish the message
130 | $this->smtp_send('.', 250);
131 |
132 | // Close the connection if we're not using pipelining
133 | $this->pipelining or $this->smtp_disconnect();
134 |
135 | return true;
136 | }
137 |
138 | /**
139 | * Connects to the given smtp and says hello to the other server.
140 | */
141 | protected function smtp_connect()
142 | {
143 | // re-use the existing connection
144 | if ( ! empty($this->smtp_connection))
145 | {
146 | return;
147 | }
148 |
149 | // add a transport if not given
150 | if (strpos($this->config['smtp']['host'], '://') === false)
151 | {
152 | $this->config['smtp']['host'] = 'tcp://'.$this->config['smtp']['host'];
153 | }
154 |
155 | $context = stream_context_create();
156 | if (is_array($this->config['smtp']['options']) and ! empty($this->config['smtp']['options']))
157 | {
158 | stream_context_set_option($context, $this->config['smtp']['options']);
159 | }
160 |
161 | $this->smtp_connection = stream_socket_client(
162 | $this->config['smtp']['host'].':'.$this->config['smtp']['port'],
163 | $error_number,
164 | $error_string,
165 | $this->config['smtp']['timeout'],
166 | STREAM_CLIENT_CONNECT,
167 | $context
168 | );
169 |
170 | if(empty($this->smtp_connection))
171 | {
172 | throw new SmtpConnectionException('Could not connect to SMTP: ('.$error_number.') '.$error_string);
173 | }
174 |
175 | // Clear the smtp response
176 | $this->smtp_get_response();
177 |
178 | // Just say hello!
179 | try
180 | {
181 | $this->smtp_send('EHLO'.' '.\Input::server('SERVER_NAME', 'localhost.local'), 250);
182 | }
183 | catch(SmtpCommandFailureException $e)
184 | {
185 | // Didn't work? Try HELO
186 | $this->smtp_send('HELO'.' '.\Input::server('SERVER_NAME', 'localhost.local'), 250);
187 | }
188 |
189 | // Enable TLS encryption if needed, and we're connecting using TCP
190 | if (\Arr::get($this->config, 'smtp.starttls', false) and strpos($this->config['smtp']['host'], 'tcp://') === 0)
191 | {
192 | try
193 | {
194 | $this->smtp_send('STARTTLS', 220);
195 | if ( ! stream_socket_enable_crypto($this->smtp_connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT))
196 | {
197 | throw new SmtpConnectionException('STARTTLS failed, Crypto client can not be enabled.');
198 | }
199 | }
200 | catch(SmtpCommandFailureException $e)
201 | {
202 | throw new SmtpConnectionException('STARTTLS failed, invalid return code received from server.');
203 | }
204 |
205 | // Say hello again, the service list might be updated (see RFC 3207 section 4.2)
206 | try
207 | {
208 | $this->smtp_send('EHLO'.' '.\Input::server('SERVER_NAME', 'localhost.local'), 250);
209 | }
210 | catch(SmtpCommandFailureException $e)
211 | {
212 | // Didn't work? Try HELO
213 | $this->smtp_send('HELO'.' '.\Input::server('SERVER_NAME', 'localhost.local'), 250);
214 | }
215 | }
216 |
217 | try
218 | {
219 | $this->smtp_send('HELP', 214);
220 | }
221 | catch(SmtpCommandFailureException $e)
222 | {
223 | // Let this pass as some servers don't support this.
224 | }
225 | }
226 |
227 | /**
228 | * Close SMTP connection
229 | */
230 | protected function smtp_disconnect()
231 | {
232 | $this->smtp_send('QUIT', false);
233 | fclose($this->smtp_connection);
234 | $this->smtp_connection = null;
235 | }
236 |
237 | /**
238 | * Performs authentication with the SMTP host
239 | */
240 | protected function smtp_authenticate()
241 | {
242 | // Encode login data
243 | $username = base64_encode($this->config['smtp']['username']);
244 | $password = base64_encode($this->config['smtp']['password']);
245 |
246 | try
247 | {
248 | // Prepare login
249 | $this->smtp_send('AUTH LOGIN', 334);
250 |
251 | // Send username
252 | $this->smtp_send($username, 334);
253 |
254 | // Send password
255 | $this->smtp_send($password, 235);
256 |
257 | }
258 | catch(SmtpCommandFailureException $e)
259 | {
260 | throw new SmtpAuthenticationFailedException('Failed authentication.', 0, $e);
261 | }
262 |
263 | }
264 |
265 | /**
266 | * Sends data to the SMTP host
267 | *
268 | * @param string $data The SMTP command
269 | * @param string|bool|string $expecting The expected response
270 | * @param bool $return_number Set to true to return the status number
271 | *
272 | * @throws \SmtpCommandFailureException When the command failed an expecting is not set to false.
273 | * @throws \SmtpTimeoutException SMTP connection timed out
274 | *
275 | * @return mixed Result or result number, false when expecting is false
276 | */
277 | protected function smtp_send($data, $expecting, $return_number = false)
278 | {
279 | ! is_array($expecting) and $expecting !== false and $expecting = array($expecting);
280 |
281 | stream_set_timeout($this->smtp_connection, $this->config['smtp']['timeout']);
282 | if ( ! fputs($this->smtp_connection, $data . $this->config['newline']))
283 | {
284 | if($expecting === false)
285 | {
286 | return false;
287 | }
288 | throw new SmtpCommandFailureException('Failed executing command: '. $data);
289 | }
290 |
291 | $info = stream_get_meta_data($this->smtp_connection);
292 | if($info['timed_out'])
293 | {
294 | throw new SmtpTimeoutException('SMTP connection timed out.');
295 | }
296 |
297 | // Get the reponse
298 | $response = $this->smtp_get_response();
299 |
300 | // Get the reponse number
301 | $number = (int) substr(trim($response), 0, 3);
302 |
303 | // Check against expected result
304 | if($expecting !== false and ! in_array($number, $expecting))
305 | {
306 | throw new SmtpCommandFailureException('Got an unexpected response from host on command: ['.$data.'] expecting: '.join(' or ', $expecting).' received: '.$response);
307 | }
308 |
309 | if($return_number)
310 | {
311 | return $number;
312 | }
313 |
314 | return $response;
315 | }
316 |
317 | /**
318 | * Get SMTP response
319 | *
320 | * @throws \SmtpTimeoutException
321 | *
322 | * @return string SMTP response
323 | */
324 | protected function smtp_get_response()
325 | {
326 | $data = '';
327 |
328 | // set the timeout.
329 | stream_set_timeout($this->smtp_connection, $this->config['smtp']['timeout']);
330 |
331 | while($str = fgets($this->smtp_connection, 512))
332 | {
333 | $info = stream_get_meta_data($this->smtp_connection);
334 | if($info['timed_out'])
335 | {
336 | throw new SmtpTimeoutException('SMTP connection timed out.');
337 | }
338 |
339 | $data .= $str;
340 |
341 | if (substr($str, 3, 1) === ' ')
342 | {
343 | break;
344 | }
345 | }
346 |
347 | return $data;
348 | }
349 |
350 | }
351 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fuel/email",
3 | "description" : "FuelPHP 1.x Email Package",
4 | "type": "fuel-package",
5 | "homepage": "https://github.com/fuel/email",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "FuelPHP Development Team",
10 | "email": "team@fuelphp.com"
11 | }
12 | ],
13 | "require": {
14 | "composer/installers": "~1.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/config/email.php:
--------------------------------------------------------------------------------
1 | 'default',
28 |
29 | /**
30 | * Default setup groups
31 | */
32 | 'setups' => array(
33 | 'default' => array(),
34 | ),
35 |
36 | /**
37 | * Default settings
38 | */
39 | 'defaults' => array(
40 |
41 | /**
42 | * Mail useragent string
43 | */
44 | 'useragent' => 'Fuel, a PHP Framework',
45 |
46 | /**
47 | * Mail driver (mail, smtp, sendmail, noop)
48 | */
49 | 'driver' => 'mail',
50 |
51 | /**
52 | * Whether to send as html, set to null for autodetection.
53 | */
54 | 'is_html' => null,
55 |
56 | /**
57 | * Email charset
58 | */
59 | 'charset' => 'utf-8',
60 |
61 | /**
62 | * Whether to encode subject and recipient names.
63 | * Requires the mbstring extension: http://www.php.net/manual/en/ref.mbstring.php
64 | */
65 | 'encode_headers' => true,
66 |
67 | /**
68 | * Ecoding (8bit, base64 or quoted-printable)
69 | */
70 | 'encoding' => '8bit',
71 |
72 | /**
73 | * Email priority
74 | */
75 | 'priority' => \Email::P_NORMAL,
76 |
77 | /**
78 | * Default sender details
79 | */
80 | 'from' => array(
81 | 'email' => false,
82 | 'name' => false,
83 | ),
84 |
85 | /**
86 | * If set to an email address, the email driver will replace all
87 | * email addresses in to, cc, bcc and reply-to with this address
88 | * This can be used for testing purposes, to make sure no actual
89 | * emails are send out by mistake.
90 | */
91 | 'force_to' => null,
92 |
93 | /**
94 | * Whether to validate email addresses
95 | */
96 | 'validate' => true,
97 |
98 | /**
99 | * Auto attach inline files
100 | */
101 | 'auto_attach' => true,
102 |
103 | /**
104 | * Auto generate alt body from html body
105 | */
106 | 'generate_alt' => true,
107 |
108 | /**
109 | * Forces content type multipart/related to be set as multipart/mixed.
110 | */
111 | 'force_mixed' => false,
112 |
113 | /**
114 | * Wordwrap size, set to null, 0 or false to disable wordwrapping
115 | */
116 | 'wordwrap' => 76,
117 |
118 | /**
119 | * Path to sendmail
120 | */
121 | 'sendmail_path' => '/usr/sbin/sendmail',
122 |
123 | /**
124 | * SMTP settings
125 | */
126 | 'smtp' => array(
127 | 'host' => '',
128 | 'port' => 25,
129 | 'username' => '',
130 | 'password' => '',
131 | 'timeout' => 5,
132 | 'starttls' => false,
133 | 'options' => array(
134 | ),
135 | ),
136 |
137 | /**
138 | * Newline
139 | */
140 | 'newline' => "\n",
141 |
142 | /**
143 | * Attachment paths
144 | */
145 | 'attach_paths' => array(
146 | '', // absolute path
147 | DOCROOT, // relative to docroot.
148 | ),
149 |
150 | /**
151 | * Default return path
152 | */
153 | 'return_path' => false,
154 |
155 | /**
156 | * Remove html comments
157 | */
158 | 'remove_html_comments' => true,
159 |
160 | /**
161 | * Mandrill settings, see http://mandrill.com/
162 | */
163 | 'mandrill' => array(
164 | 'key' => 'api_key',
165 | 'message_options' => array(),
166 | 'send_options' => array(
167 | 'async' => false,
168 | 'ip_pool' => null,
169 | 'send_at' => null,
170 | ),
171 | ),
172 |
173 | /**
174 | * Mailgun settings, see http://www.mailgun.com/
175 | */
176 | 'mailgun' => array(
177 | 'key' => 'api_key',
178 | 'domain' => 'domain',
179 | 'endpoint' => null // optional API URL; example: 'https://api.eu.mailgun.net/v3'; to use default, omit entirely or set to null
180 | ),
181 |
182 | /**
183 | * When relative protocol uri's ("//uri") are used in the email body,
184 | * you can specify here what you want them to be replaced with. Options
185 | * are "http://", "https://" or \Input::protocol() if you want to use
186 | * whatever was used to request the controller.
187 | */
188 | 'relative_protocol_replacement' => false,
189 | ),
190 | );
191 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Fuel Email Package.
2 |
3 | A full fledged email class for Fuel. Send mails using php's mail function, sendmail or SMTP.
4 |
5 | # Summary
6 |
7 | * Send plain/text or html with (optional) alternative plain/text bodies using mail, sendmail or SMTP.
8 | * Add attachments, normal or inline and string or file.
9 | * Automatic inline file attachments for html bodies.
10 | * Configurable attachment paths.
11 |
12 | # Usage
13 |
14 | $mail = Email::forge();
15 | $mail->from('me@domain.com', 'Your Name Here');
16 |
17 | // Set to
18 | $mail->to('mail@domain.com');
19 |
20 | // Set with name
21 | $mail->to('mail@domain.com', 'His/Her Name');
22 |
23 | // Set as array
24 | $mail->to(array(
25 | // Without name
26 | 'mail@domain.com',
27 |
28 | // With name
29 | 'mail@domain.com' => 'His/Her Name',
30 | ));
31 |
32 | // Work the same for ->cc and ->bcc and ->reply_to
33 |
34 |
35 | // Set a body message
36 | $email->body('My email body');
37 |
38 | // Set a html body message
39 | $email->html_body(\View::forge('email/template', $email_data));
40 |
41 | /**
42 |
43 | By default this will also generate an alt body from the html,
44 | and attach any inline files (not paths like http://...)
45 |
46 | **/
47 |
48 | // Set an alt body
49 | $email->alt_body('This is my alt body, for non-html viewers.');
50 |
51 | // Set a subject
52 | $email->subject('This is the subject');
53 |
54 | // Change the priority
55 | $email->priority(\Email::P_HIGH);
56 |
57 | // And send it
58 | $result = $email->send();
59 |
60 | # Exceptions
61 |
62 | + \EmailValidationFailedException, thrown when one or more email addresses doesn't pass validation
63 | + \EmailSendingFailedException, thrown when the driver failed to send the exception
64 |
65 | Example:
66 |
67 | // Use the default config and change the driver
68 | $email = \Email::forge('default', array('driver' => 'smtp'));
69 | $email->subject('My Subject');
70 | $email->html_body(\View::forge('email/template', $email_data));
71 | $email->from('me@example.com', 'It's Me!');
72 | $email->to('other@example.com', 'It's the Other!');
73 |
74 | try
75 | {
76 | $email->send();
77 | }
78 | catch(\EmailValidationFailedException $e)
79 | {
80 | // The validation failed
81 | }
82 | catch(\EmailSendingFailedException $e)
83 | {
84 | // The driver could not send the email
85 | }
86 |
87 | # Priorities
88 |
89 | These can me one of the following:
90 |
91 | + \Email::P_LOWEST - 1 (lowest)
92 | + \Email::P_LOW - 2 (low)
93 | + \Email::P_NORMAL - 3 (normal) - this is the default
94 | + \Email::P_HIGH - 4 (high)
95 | + \Email::P_HIGHEST - 5 (highest)
96 |
97 | # Attachments
98 |
99 | There are multiple ways to add attachments:
100 |
101 | $email = Email::forge();
102 |
103 | // Add an attachment
104 | $email->attach(DOCROOT.'dir/my_img.png');
105 |
106 | // Add an inline attachment
107 | // Add a cid here to point to the html
108 | $email->attach(DOCROOT.'dir/my_img.png', true, 'cid:my_conten_id');
109 |
110 |
111 | You can also add string attachments
112 |
113 | $contents = file_get_contents($my_file);
114 | $email->string_attach($contents, $filename);
115 |
116 | By default html images are auto included, but it only includes local files.
117 | Look at the following html to see how it works.
118 |
119 | // This is included
120 |
121 |
122 | // This is not included
123 |
124 |
125 |
126 | Drivers
127 | =======
128 | The drivers allow the use of this library with mostly anything that can send mails.
129 |
130 | ### Mailgun
131 | Mailgun is an online service by Rackspace (http://www.mailgun.com/) that allows you to send emails by demand. You will need to install the mailgun library (https://github.com/mailgun/mailgun-php) with composer in your FuelPHP.
132 |
133 | Once you have installed the package you will have to set up the config for your App:
134 |
135 | ```php
136 | array(
142 | 'driver' => 'mailgun',
143 | 'mailgun' => array(
144 | 'key' => 'YOUR KEY',
145 | 'domain' => 'YOUR DOMAIN',
146 | 'endpoint' => null | 'OPTIONAL ALT. API ENDPOINT URL' // e.g. 'https://api.eu.mailgun.net/v3'
147 | ),
148 | ),
149 | );
150 | ```
151 |
152 | # That's it. Questions?
153 |
--------------------------------------------------------------------------------