Guest User

conf

a guest
Aug 9th, 2017
603
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 56.42 KB | None | 0 0
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39.  
  40. /**
  41. * CodeIgniter Email Class
  42. *
  43. * Permits email to be sent using Mail, Sendmail, or SMTP.
  44. *
  45. * @package CodeIgniter
  46. * @subpackage Libraries
  47. * @category Libraries
  48. * @author EllisLab Dev Team
  49. * @link https://codeigniter.com/user_guide/libraries/email.html
  50. */
  51. class CI_Email {
  52.  
  53. /**
  54. * Used as the User-Agent and X-Mailer headers' value.
  55. *
  56. * @var string
  57. */
  58. public $useragent = 'CodeIgniter';
  59.  
  60. /**
  61. * Path to the Sendmail binary.
  62. *
  63. * @var string
  64. */
  65. public $mailpath = '/usr/sbin/sendmail'; // Sendmail path
  66.  
  67. /**
  68. * Which method to use for sending e-mails.
  69. *
  70. * @var string 'mail', 'sendmail' or 'smtp'
  71. */
  72. public $protocol = 'smtp'; // mail/sendmail/smtp
  73.  
  74. /**
  75. * STMP Server host
  76. *
  77. * @var string
  78. */
  79. public $smtp_host = 'mail.smtp2go.com';
  80.  
  81. /**
  82. * SMTP Username
  83. *
  84. * @var string
  85. */
  86. public $smtp_user = '';
  87.  
  88. /**
  89. * SMTP Password
  90. *
  91. * @var string
  92. */
  93. public $smtp_pass = 'Mp0VcEGaUlNP';
  94.  
  95. /**
  96. * SMTP Server port
  97. *
  98. * @var int
  99. */
  100. public $smtp_port = 25;
  101.  
  102. /**
  103. * SMTP connection timeout in seconds
  104. *
  105. * @var int
  106. */
  107. public $smtp_timeout = 5;
  108.  
  109. /**
  110. * SMTP persistent connection
  111. *
  112. * @var bool
  113. */
  114. public $smtp_keepalive = FALSE;
  115.  
  116. /**
  117. * SMTP Encryption
  118. *
  119. * @var string empty, 'tls' or 'ssl'
  120. */
  121. public $smtp_crypto = 'tls';
  122.  
  123. /**
  124. * Whether to apply word-wrapping to the message body.
  125. *
  126. * @var bool
  127. */
  128. public $wordwrap = TRUE;
  129.  
  130. /**
  131. * Number of characters to wrap at.
  132. *
  133. * @see CI_Email::$wordwrap
  134. * @var int
  135. */
  136. public $wrapchars = 76;
  137.  
  138. /**
  139. * Message format.
  140. *
  141. * @var string 'text' or 'html'
  142. */
  143. public $mailtype = 'html';
  144.  
  145. /**
  146. * Character set (default: utf-8)
  147. *
  148. * @var string
  149. */
  150. public $charset = 'utf-8';
  151.  
  152. /**
  153. * Alternative message (for HTML messages only)
  154. *
  155. * @var string
  156. */
  157. public $alt_message = '';
  158.  
  159. /**
  160. * Whether to validate e-mail addresses.
  161. *
  162. * @var bool
  163. */
  164. public $validate = FALSE;
  165.  
  166. /**
  167. * X-Priority header value.
  168. *
  169. * @var int 1-5
  170. */
  171. public $priority = 3; // Default priority (1 - 5)
  172.  
  173. /**
  174. * Newline character sequence.
  175. * Use "\r\n" to comply with RFC 822.
  176. *
  177. * @link http://www.ietf.org/rfc/rfc822.txt
  178. * @var string "\r\n" or "\n"
  179. */
  180. public $newline = "\n"; // Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
  181.  
  182. /**
  183. * CRLF character sequence
  184. *
  185. * RFC 2045 specifies that for 'quoted-printable' encoding,
  186. * "\r\n" must be used. However, it appears that some servers
  187. * (even on the receiving end) don't handle it properly and
  188. * switching to "\n", while improper, is the only solution
  189. * that seems to work for all environments.
  190. *
  191. * @link http://www.ietf.org/rfc/rfc822.txt
  192. * @var string
  193. */
  194. public $crlf = "\n";
  195.  
  196. /**
  197. * Whether to use Delivery Status Notification.
  198. *
  199. * @var bool
  200. */
  201. public $dsn = FALSE;
  202.  
  203. /**
  204. * Whether to send multipart alternatives.
  205. * Yahoo! doesn't seem to like these.
  206. *
  207. * @var bool
  208. */
  209. public $send_multipart = TRUE;
  210.  
  211. /**
  212. * Whether to send messages to BCC recipients in batches.
  213. *
  214. * @var bool
  215. */
  216. public $bcc_batch_mode = FALSE;
  217.  
  218. /**
  219. * BCC Batch max number size.
  220. *
  221. * @see CI_Email::$bcc_batch_mode
  222. * @var int
  223. */
  224. public $bcc_batch_size = 200;
  225.  
  226. // --------------------------------------------------------------------
  227.  
  228. /**
  229. * Whether PHP is running in safe mode. Initialized by the class constructor.
  230. *
  231. * @var bool
  232. */
  233. protected $_safe_mode = FALSE;
  234.  
  235. /**
  236. * Subject header
  237. *
  238. * @var string
  239. */
  240. protected $_subject = '';
  241.  
  242. /**
  243. * Message body
  244. *
  245. * @var string
  246. */
  247. protected $_body = '';
  248.  
  249. /**
  250. * Final message body to be sent.
  251. *
  252. * @var string
  253. */
  254. protected $_finalbody = '';
  255.  
  256. /**
  257. * Final headers to send
  258. *
  259. * @var string
  260. */
  261. protected $_header_str = '';
  262.  
  263. /**
  264. * SMTP Connection socket placeholder
  265. *
  266. * @var resource
  267. */
  268. protected $_smtp_connect = '';
  269.  
  270. /**
  271. * Mail encoding
  272. *
  273. * @var string '8bit' or '7bit'
  274. */
  275. protected $_encoding = '8bit';
  276.  
  277. /**
  278. * Whether to perform SMTP authentication
  279. *
  280. * @var bool
  281. */
  282. protected $_smtp_auth = FALSE;
  283.  
  284. /**
  285. * Whether to send a Reply-To header
  286. *
  287. * @var bool
  288. */
  289. protected $_replyto_flag = FALSE;
  290.  
  291. /**
  292. * Debug messages
  293. *
  294. * @see CI_Email::print_debugger()
  295. * @var string
  296. */
  297. protected $_debug_msg = array();
  298.  
  299. /**
  300. * Recipients
  301. *
  302. * @var string[]
  303. */
  304. protected $_recipients = array();
  305.  
  306. /**
  307. * CC Recipients
  308. *
  309. * @var string[]
  310. */
  311. protected $_cc_array = array();
  312.  
  313. /**
  314. * BCC Recipients
  315. *
  316. * @var string[]
  317. */
  318. protected $_bcc_array = array();
  319.  
  320. /**
  321. * Message headers
  322. *
  323. * @var string[]
  324. */
  325. protected $_headers = array();
  326.  
  327. /**
  328. * Attachment data
  329. *
  330. * @var array
  331. */
  332. protected $_attachments = array();
  333.  
  334. /**
  335. * Valid $protocol values
  336. *
  337. * @see CI_Email::$protocol
  338. * @var string[]
  339. */
  340. protected $_protocols = array('mail', 'sendmail', 'smtp');
  341.  
  342. /**
  343. * Base charsets
  344. *
  345. * Character sets valid for 7-bit encoding,
  346. * excluding language suffix.
  347. *
  348. * @var string[]
  349. */
  350. protected $_base_charsets = array('us-ascii', 'iso-2022-');
  351.  
  352. /**
  353. * Bit depths
  354. *
  355. * Valid mail encodings
  356. *
  357. * @see CI_Email::$_encoding
  358. * @var string[]
  359. */
  360. protected $_bit_depths = array('7bit', '8bit');
  361.  
  362. /**
  363. * $priority translations
  364. *
  365. * Actual values to send with the X-Priority header
  366. *
  367. * @var string[]
  368. */
  369. protected $_priorities = array(
  370. 1 => '1 (Highest)',
  371. 2 => '2 (High)',
  372. 3 => '3 (Normal)',
  373. 4 => '4 (Low)',
  374. 5 => '5 (Lowest)'
  375. );
  376.  
  377. /**
  378. * mbstring.func_overload flag
  379. *
  380. * @var bool
  381. */
  382. protected static $func_overload;
  383.  
  384. // --------------------------------------------------------------------
  385.  
  386. /**
  387. * Constructor - Sets Email Preferences
  388. *
  389. * The constructor can be passed an array of config values
  390. *
  391. * @param array $config = array()
  392. * @return void
  393. */
  394. public function __construct(array $config = array())
  395. {
  396. $this->charset = config_item('charset');
  397. $this->initialize($config);
  398. $this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode'));
  399.  
  400. isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
  401.  
  402. log_message('info', 'Email Class Initialized');
  403. }
  404.  
  405. // --------------------------------------------------------------------
  406.  
  407. /**
  408. * Initialize preferences
  409. *
  410. * @param array $config
  411. * @return CI_Email
  412. */
  413. public function initialize(array $config = array())
  414. {
  415. $this->clear();
  416.  
  417. foreach ($config as $key => $val)
  418. {
  419. if (isset($this->$key))
  420. {
  421. $method = 'set_'.$key;
  422.  
  423. if (method_exists($this, $method))
  424. {
  425. $this->$method($val);
  426. }
  427. else
  428. {
  429. $this->$key = $val;
  430. }
  431. }
  432. }
  433.  
  434. $this->charset = strtoupper($this->charset);
  435. $this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]);
  436.  
  437. return $this;
  438. }
  439.  
  440. // --------------------------------------------------------------------
  441.  
  442. /**
  443. * Initialize the Email Data
  444. *
  445. * @param bool
  446. * @return CI_Email
  447. */
  448. public function clear($clear_attachments = FALSE)
  449. {
  450. $this->_subject = '';
  451. $this->_body = '';
  452. $this->_finalbody = '';
  453. $this->_header_str = '';
  454. $this->_replyto_flag = FALSE;
  455. $this->_recipients = array();
  456. $this->_cc_array = array();
  457. $this->_bcc_array = array();
  458. $this->_headers = array();
  459. $this->_debug_msg = array();
  460.  
  461. $this->set_header('Date', $this->_set_date());
  462.  
  463. if ($clear_attachments !== FALSE)
  464. {
  465. $this->_attachments = array();
  466. }
  467.  
  468. return $this;
  469. }
  470.  
  471. // --------------------------------------------------------------------
  472.  
  473. /**
  474. * Set FROM
  475. *
  476. * @param string $from
  477. * @param string $name
  478. * @param string $return_path = NULL Return-Path
  479. * @return CI_Email
  480. */
  481. public function from($from, $name = '', $return_path = NULL)
  482. {
  483. if (preg_match('/\<(.*)\>/', $from, $match))
  484. {
  485. $from = $match[1];
  486. }
  487.  
  488. if ($this->validate)
  489. {
  490. $this->validate_email($this->_str_to_array($from));
  491. if ($return_path)
  492. {
  493. $this->validate_email($this->_str_to_array($return_path));
  494. }
  495. }
  496.  
  497. // prepare the display name
  498. if ($name !== '')
  499. {
  500. // only use Q encoding if there are characters that would require it
  501. if ( ! preg_match('/[\200-\377]/', $name))
  502. {
  503. // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
  504. $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
  505. }
  506. else
  507. {
  508. $name = $this->_prep_q_encoding($name);
  509. }
  510. }
  511.  
  512. $this->set_header('From', $name.' <'.$from.'>');
  513.  
  514. isset($return_path) OR $return_path = $from;
  515. $this->set_header('Return-Path', '<'.$return_path.'>');
  516.  
  517. return $this;
  518. }
  519.  
  520. // --------------------------------------------------------------------
  521.  
  522. /**
  523. * Set Reply-to
  524. *
  525. * @param string
  526. * @param string
  527. * @return CI_Email
  528. */
  529. public function reply_to($replyto, $name = '')
  530. {
  531. if (preg_match('/\<(.*)\>/', $replyto, $match))
  532. {
  533. $replyto = $match[1];
  534. }
  535.  
  536. if ($this->validate)
  537. {
  538. $this->validate_email($this->_str_to_array($replyto));
  539. }
  540.  
  541. if ($name !== '')
  542. {
  543. // only use Q encoding if there are characters that would require it
  544. if ( ! preg_match('/[\200-\377]/', $name))
  545. {
  546. // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
  547. $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
  548. }
  549. else
  550. {
  551. $name = $this->_prep_q_encoding($name);
  552. }
  553. }
  554.  
  555. $this->set_header('Reply-To', $name.' <'.$replyto.'>');
  556. $this->_replyto_flag = TRUE;
  557.  
  558. return $this;
  559. }
  560.  
  561. // --------------------------------------------------------------------
  562.  
  563. /**
  564. * Set Recipients
  565. *
  566. * @param string
  567. * @return CI_Email
  568. */
  569. public function to($to)
  570. {
  571. $to = $this->_str_to_array($to);
  572. $to = $this->clean_email($to);
  573.  
  574. if ($this->validate)
  575. {
  576. $this->validate_email($to);
  577. }
  578.  
  579. if ($this->_get_protocol() !== 'mail')
  580. {
  581. $this->set_header('To', implode(', ', $to));
  582. }
  583.  
  584. $this->_recipients = $to;
  585.  
  586. return $this;
  587. }
  588.  
  589. // --------------------------------------------------------------------
  590.  
  591. /**
  592. * Set CC
  593. *
  594. * @param string
  595. * @return CI_Email
  596. */
  597. public function cc($cc)
  598. {
  599. $cc = $this->clean_email($this->_str_to_array($cc));
  600.  
  601. if ($this->validate)
  602. {
  603. $this->validate_email($cc);
  604. }
  605.  
  606. $this->set_header('Cc', implode(', ', $cc));
  607.  
  608. if ($this->_get_protocol() === 'smtp')
  609. {
  610. $this->_cc_array = $cc;
  611. }
  612.  
  613. return $this;
  614. }
  615.  
  616. // --------------------------------------------------------------------
  617.  
  618. /**
  619. * Set BCC
  620. *
  621. * @param string
  622. * @param string
  623. * @return CI_Email
  624. */
  625. public function bcc($bcc, $limit = '')
  626. {
  627. if ($limit !== '' && is_numeric($limit))
  628. {
  629. $this->bcc_batch_mode = TRUE;
  630. $this->bcc_batch_size = $limit;
  631. }
  632.  
  633. $bcc = $this->clean_email($this->_str_to_array($bcc));
  634.  
  635. if ($this->validate)
  636. {
  637. $this->validate_email($bcc);
  638. }
  639.  
  640. if ($this->_get_protocol() === 'smtp' OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
  641. {
  642. $this->_bcc_array = $bcc;
  643. }
  644. else
  645. {
  646. $this->set_header('Bcc', implode(', ', $bcc));
  647. }
  648.  
  649. return $this;
  650. }
  651.  
  652. // --------------------------------------------------------------------
  653.  
  654. /**
  655. * Set Email Subject
  656. *
  657. * @param string
  658. * @return CI_Email
  659. */
  660. public function subject($subject)
  661. {
  662. $subject = $this->_prep_q_encoding($subject);
  663. $this->set_header('Subject', $subject);
  664. return $this;
  665. }
  666.  
  667. // --------------------------------------------------------------------
  668.  
  669. /**
  670. * Set Body
  671. *
  672. * @param string
  673. * @return CI_Email
  674. */
  675. public function message($body)
  676. {
  677. $this->_body = rtrim(str_replace("\r", '', $body));
  678.  
  679. /* strip slashes only if magic quotes is ON
  680. if we do it with magic quotes OFF, it strips real, user-inputted chars.
  681.  
  682. NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
  683. it will probably not exist in future versions at all.
  684. */
  685. if ( ! is_php('5.4') && get_magic_quotes_gpc())
  686. {
  687. $this->_body = stripslashes($this->_body);
  688. }
  689.  
  690. return $this;
  691. }
  692.  
  693. // --------------------------------------------------------------------
  694.  
  695. /**
  696. * Assign file attachments
  697. *
  698. * @param string $file Can be local path, URL or buffered content
  699. * @param string $disposition = 'attachment'
  700. * @param string $newname = NULL
  701. * @param string $mime = ''
  702. * @return CI_Email
  703. */
  704. public function attach($file, $disposition = '', $newname = NULL, $mime = '')
  705. {
  706. if ($mime === '')
  707. {
  708. if (strpos($file, '://') === FALSE && ! file_exists($file))
  709. {
  710. $this->_set_error_message('lang:email_attachment_missing', $file);
  711. return FALSE;
  712. }
  713.  
  714. if ( ! $fp = @fopen($file, 'rb'))
  715. {
  716. $this->_set_error_message('lang:email_attachment_unreadable', $file);
  717. return FALSE;
  718. }
  719.  
  720. $file_content = stream_get_contents($fp);
  721. $mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION));
  722. fclose($fp);
  723. }
  724. else
  725. {
  726. $file_content =& $file; // buffered file
  727. }
  728.  
  729. $this->_attachments[] = array(
  730. 'name' => array($file, $newname),
  731. 'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters
  732. 'type' => $mime,
  733. 'content' => chunk_split(base64_encode($file_content)),
  734. 'multipart' => 'mixed'
  735. );
  736.  
  737. return $this;
  738. }
  739.  
  740. // --------------------------------------------------------------------
  741.  
  742. /**
  743. * Set and return attachment Content-ID
  744. *
  745. * Useful for attached inline pictures
  746. *
  747. * @param string $filename
  748. * @return string
  749. */
  750. public function attachment_cid($filename)
  751. {
  752. for ($i = 0, $c = count($this->_attachments); $i < $c; $i++)
  753. {
  754. if ($this->_attachments[$i]['name'][0] === $filename)
  755. {
  756. $this->_attachments[$i]['multipart'] = 'related';
  757. $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@');
  758. return $this->_attachments[$i]['cid'];
  759. }
  760. }
  761.  
  762. return FALSE;
  763. }
  764.  
  765. // --------------------------------------------------------------------
  766.  
  767. /**
  768. * Add a Header Item
  769. *
  770. * @param string
  771. * @param string
  772. * @return CI_Email
  773. */
  774. public function set_header($header, $value)
  775. {
  776. $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value);
  777. return $this;
  778. }
  779.  
  780. // --------------------------------------------------------------------
  781.  
  782. /**
  783. * Convert a String to an Array
  784. *
  785. * @param string
  786. * @return array
  787. */
  788. protected function _str_to_array($email)
  789. {
  790. if ( ! is_array($email))
  791. {
  792. return (strpos($email, ',') !== FALSE)
  793. ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY)
  794. : (array) trim($email);
  795. }
  796.  
  797. return $email;
  798. }
  799.  
  800. // --------------------------------------------------------------------
  801.  
  802. /**
  803. * Set Multipart Value
  804. *
  805. * @param string
  806. * @return CI_Email
  807. */
  808. public function set_alt_message($str)
  809. {
  810. $this->alt_message = (string) $str;
  811. return $this;
  812. }
  813.  
  814. // --------------------------------------------------------------------
  815.  
  816. /**
  817. * Set Mailtype
  818. *
  819. * @param string
  820. * @return CI_Email
  821. */
  822. public function set_mailtype($type = 'text')
  823. {
  824. $this->mailtype = ($type === 'html') ? 'html' : 'text';
  825. return $this;
  826. }
  827.  
  828. // --------------------------------------------------------------------
  829.  
  830. /**
  831. * Set Wordwrap
  832. *
  833. * @param bool
  834. * @return CI_Email
  835. */
  836. public function set_wordwrap($wordwrap = TRUE)
  837. {
  838. $this->wordwrap = (bool) $wordwrap;
  839. return $this;
  840. }
  841.  
  842. // --------------------------------------------------------------------
  843.  
  844. /**
  845. * Set Protocol
  846. *
  847. * @param string
  848. * @return CI_Email
  849. */
  850. public function set_protocol($protocol = 'mail')
  851. {
  852. $this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail';
  853. return $this;
  854. }
  855.  
  856. // --------------------------------------------------------------------
  857.  
  858. /**
  859. * Set Priority
  860. *
  861. * @param int
  862. * @return CI_Email
  863. */
  864. public function set_priority($n = 3)
  865. {
  866. $this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3;
  867. return $this;
  868. }
  869.  
  870. // --------------------------------------------------------------------
  871.  
  872. /**
  873. * Set Newline Character
  874. *
  875. * @param string
  876. * @return CI_Email
  877. */
  878. public function set_newline($newline = "\n")
  879. {
  880. $this->newline = in_array($newline, array("\n", "\r\n", "\r")) ? $newline : "\n";
  881. return $this;
  882. }
  883.  
  884. // --------------------------------------------------------------------
  885.  
  886. /**
  887. * Set CRLF
  888. *
  889. * @param string
  890. * @return CI_Email
  891. */
  892. public function set_crlf($crlf = "\n")
  893. {
  894. $this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf;
  895. return $this;
  896. }
  897.  
  898. // --------------------------------------------------------------------
  899.  
  900. /**
  901. * Get the Message ID
  902. *
  903. * @return string
  904. */
  905. protected function _get_message_id()
  906. {
  907. $from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']);
  908. return '<'.uniqid('').strstr($from, '@').'>';
  909. }
  910.  
  911. // --------------------------------------------------------------------
  912.  
  913. /**
  914. * Get Mail Protocol
  915. *
  916. * @return mixed
  917. */
  918. protected function _get_protocol()
  919. {
  920. $this->protocol = strtolower($this->protocol);
  921. in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';
  922. return $this->protocol;
  923. }
  924.  
  925. // --------------------------------------------------------------------
  926.  
  927. /**
  928. * Get Mail Encoding
  929. *
  930. * @return string
  931. */
  932. protected function _get_encoding()
  933. {
  934. in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit';
  935.  
  936. foreach ($this->_base_charsets as $charset)
  937. {
  938. if (strpos($this->charset, $charset) === 0)
  939. {
  940. $this->_encoding = '7bit';
  941. }
  942. }
  943.  
  944. return $this->_encoding;
  945. }
  946.  
  947. // --------------------------------------------------------------------
  948.  
  949. /**
  950. * Get content type (text/html/attachment)
  951. *
  952. * @return string
  953. */
  954. protected function _get_content_type()
  955. {
  956. if ($this->mailtype === 'html')
  957. {
  958. return empty($this->_attachments) ? 'html' : 'html-attach';
  959. }
  960. elseif ($this->mailtype === 'text' && ! empty($this->_attachments))
  961. {
  962. return 'plain-attach';
  963. }
  964. else
  965. {
  966. return 'plain';
  967. }
  968. }
  969.  
  970. // --------------------------------------------------------------------
  971.  
  972. /**
  973. * Set RFC 822 Date
  974. *
  975. * @return string
  976. */
  977. protected function _set_date()
  978. {
  979. $timezone = date('Z');
  980. $operator = ($timezone[0] === '-') ? '-' : '+';
  981. $timezone = abs($timezone);
  982. $timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60;
  983.  
  984. return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone);
  985. }
  986.  
  987. // --------------------------------------------------------------------
  988.  
  989. /**
  990. * Mime message
  991. *
  992. * @return string
  993. */
  994. protected function _get_mime_message()
  995. {
  996. return 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.';
  997. }
  998.  
  999. // --------------------------------------------------------------------
  1000.  
  1001. /**
  1002. * Validate Email Address
  1003. *
  1004. * @param string
  1005. * @return bool
  1006. */
  1007. public function validate_email($email)
  1008. {
  1009. if ( ! is_array($email))
  1010. {
  1011. $this->_set_error_message('lang:email_must_be_array');
  1012. return FALSE;
  1013. }
  1014.  
  1015. foreach ($email as $val)
  1016. {
  1017. if ( ! $this->valid_email($val))
  1018. {
  1019. $this->_set_error_message('lang:email_invalid_address', $val);
  1020. return FALSE;
  1021. }
  1022. }
  1023.  
  1024. return TRUE;
  1025. }
  1026.  
  1027. // --------------------------------------------------------------------
  1028.  
  1029. /**
  1030. * Email Validation
  1031. *
  1032. * @param string
  1033. * @return bool
  1034. */
  1035. public function valid_email($email)
  1036. {
  1037. if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))
  1038. {
  1039. $email = self::substr($email, 0, ++$atpos).idn_to_ascii(self::substr($email, $atpos));
  1040. }
  1041.  
  1042. return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
  1043. }
  1044.  
  1045. // --------------------------------------------------------------------
  1046.  
  1047. /**
  1048. * Clean Extended Email Address: Joe Smith <joe@smith.com>
  1049. *
  1050. * @param string
  1051. * @return string
  1052. */
  1053. public function clean_email($email)
  1054. {
  1055. if ( ! is_array($email))
  1056. {
  1057. return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email;
  1058. }
  1059.  
  1060. $clean_email = array();
  1061.  
  1062. foreach ($email as $addy)
  1063. {
  1064. $clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy;
  1065. }
  1066.  
  1067. return $clean_email;
  1068. }
  1069.  
  1070. // --------------------------------------------------------------------
  1071.  
  1072. /**
  1073. * Build alternative plain text message
  1074. *
  1075. * Provides the raw message for use in plain-text headers of
  1076. * HTML-formatted emails.
  1077. * If the user hasn't specified his own alternative message
  1078. * it creates one by stripping the HTML
  1079. *
  1080. * @return string
  1081. */
  1082. protected function _get_alt_message()
  1083. {
  1084. if ( ! empty($this->alt_message))
  1085. {
  1086. return ($this->wordwrap)
  1087. ? $this->word_wrap($this->alt_message, 76)
  1088. : $this->alt_message;
  1089. }
  1090.  
  1091. $body = preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body;
  1092. $body = str_replace("\t", '', preg_replace('#<!--(.*)--\>#', '', trim(strip_tags($body))));
  1093.  
  1094. for ($i = 20; $i >= 3; $i--)
  1095. {
  1096. $body = str_replace(str_repeat("\n", $i), "\n\n", $body);
  1097. }
  1098.  
  1099. // Reduce multiple spaces
  1100. $body = preg_replace('| +|', ' ', $body);
  1101.  
  1102. return ($this->wordwrap)
  1103. ? $this->word_wrap($body, 76)
  1104. : $body;
  1105. }
  1106.  
  1107. // --------------------------------------------------------------------
  1108.  
  1109. /**
  1110. * Word Wrap
  1111. *
  1112. * @param string
  1113. * @param int line-length limit
  1114. * @return string
  1115. */
  1116. public function word_wrap($str, $charlim = NULL)
  1117. {
  1118. // Set the character limit, if not already present
  1119. if (empty($charlim))
  1120. {
  1121. $charlim = empty($this->wrapchars) ? 76 : $this->wrapchars;
  1122. }
  1123.  
  1124. // Standardize newlines
  1125. if (strpos($str, "\r") !== FALSE)
  1126. {
  1127. $str = str_replace(array("\r\n", "\r"), "\n", $str);
  1128. }
  1129.  
  1130. // Reduce multiple spaces at end of line
  1131. $str = preg_replace('| +\n|', "\n", $str);
  1132.  
  1133. // If the current word is surrounded by {unwrap} tags we'll
  1134. // strip the entire chunk and replace it with a marker.
  1135. $unwrap = array();
  1136. if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches))
  1137. {
  1138. for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
  1139. {
  1140. $unwrap[] = $matches[1][$i];
  1141. $str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str);
  1142. }
  1143. }
  1144.  
  1145. // Use PHP's native function to do the initial wordwrap.
  1146. // We set the cut flag to FALSE so that any individual words that are
  1147. // too long get left alone. In the next step we'll deal with them.
  1148. $str = wordwrap($str, $charlim, "\n", FALSE);
  1149.  
  1150. // Split the string into individual lines of text and cycle through them
  1151. $output = '';
  1152. foreach (explode("\n", $str) as $line)
  1153. {
  1154. // Is the line within the allowed character count?
  1155. // If so we'll join it to the output and continue
  1156. if (self::strlen($line) <= $charlim)
  1157. {
  1158. $output .= $line.$this->newline;
  1159. continue;
  1160. }
  1161.  
  1162. $temp = '';
  1163. do
  1164. {
  1165. // If the over-length word is a URL we won't wrap it
  1166. if (preg_match('!\[url.+\]|://|www\.!', $line))
  1167. {
  1168. break;
  1169. }
  1170.  
  1171. // Trim the word down
  1172. $temp .= self::substr($line, 0, $charlim - 1);
  1173. $line = self::substr($line, $charlim - 1);
  1174. }
  1175. while (self::strlen($line) > $charlim);
  1176.  
  1177. // If $temp contains data it means we had to split up an over-length
  1178. // word into smaller chunks so we'll add it back to our current line
  1179. if ($temp !== '')
  1180. {
  1181. $output .= $temp.$this->newline;
  1182. }
  1183.  
  1184. $output .= $line.$this->newline;
  1185. }
  1186.  
  1187. // Put our markers back
  1188. if (count($unwrap) > 0)
  1189. {
  1190. foreach ($unwrap as $key => $val)
  1191. {
  1192. $output = str_replace('{{unwrapped'.$key.'}}', $val, $output);
  1193. }
  1194. }
  1195.  
  1196. return $output;
  1197. }
  1198.  
  1199. // --------------------------------------------------------------------
  1200.  
  1201. /**
  1202. * Build final headers
  1203. *
  1204. * @return void
  1205. */
  1206. protected function _build_headers()
  1207. {
  1208. $this->set_header('User-Agent', $this->useragent);
  1209. $this->set_header('X-Sender', $this->clean_email($this->_headers['From']));
  1210. $this->set_header('X-Mailer', $this->useragent);
  1211. $this->set_header('X-Priority', $this->_priorities[$this->priority]);
  1212. $this->set_header('Message-ID', $this->_get_message_id());
  1213. $this->set_header('Mime-Version', '1.0');
  1214. }
  1215.  
  1216. // --------------------------------------------------------------------
  1217.  
  1218. /**
  1219. * Write Headers as a string
  1220. *
  1221. * @return void
  1222. */
  1223. protected function _write_headers()
  1224. {
  1225. if ($this->protocol === 'mail')
  1226. {
  1227. if (isset($this->_headers['Subject']))
  1228. {
  1229. $this->_subject = $this->_headers['Subject'];
  1230. unset($this->_headers['Subject']);
  1231. }
  1232. }
  1233.  
  1234. reset($this->_headers);
  1235. $this->_header_str = '';
  1236.  
  1237. foreach ($this->_headers as $key => $val)
  1238. {
  1239. $val = trim($val);
  1240.  
  1241. if ($val !== '')
  1242. {
  1243. $this->_header_str .= $key.': '.$val.$this->newline;
  1244. }
  1245. }
  1246.  
  1247. if ($this->_get_protocol() === 'mail')
  1248. {
  1249. $this->_header_str = rtrim($this->_header_str);
  1250. }
  1251. }
  1252.  
  1253. // --------------------------------------------------------------------
  1254.  
  1255. /**
  1256. * Build Final Body and attachments
  1257. *
  1258. * @return bool
  1259. */
  1260. protected function _build_message()
  1261. {
  1262. if ($this->wordwrap === TRUE && $this->mailtype !== 'html')
  1263. {
  1264. $this->_body = $this->word_wrap($this->_body);
  1265. }
  1266.  
  1267. $this->_write_headers();
  1268.  
  1269. $hdr = ($this->_get_protocol() === 'mail') ? $this->newline : '';
  1270. $body = '';
  1271.  
  1272. switch ($this->_get_content_type())
  1273. {
  1274. case 'plain':
  1275.  
  1276. $hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline
  1277. .'Content-Transfer-Encoding: '.$this->_get_encoding();
  1278.  
  1279. if ($this->_get_protocol() === 'mail')
  1280. {
  1281. $this->_header_str .= $hdr;
  1282. $this->_finalbody = $this->_body;
  1283. }
  1284. else
  1285. {
  1286. $this->_finalbody = $hdr.$this->newline.$this->newline.$this->_body;
  1287. }
  1288.  
  1289. return;
  1290.  
  1291. case 'html':
  1292.  
  1293. if ($this->send_multipart === FALSE)
  1294. {
  1295. $hdr .= 'Content-Type: text/html; charset='.$this->charset.$this->newline
  1296. .'Content-Transfer-Encoding: quoted-printable';
  1297. }
  1298. else
  1299. {
  1300. $boundary = uniqid('B_ALT_');
  1301. $hdr .= 'Content-Type: multipart/alternative; boundary="'.$boundary.'"';
  1302.  
  1303. $body .= $this->_get_mime_message().$this->newline.$this->newline
  1304. .'--'.$boundary.$this->newline
  1305.  
  1306. .'Content-Type: text/plain; charset='.$this->charset.$this->newline
  1307. .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline
  1308. .$this->_get_alt_message().$this->newline.$this->newline
  1309. .'--'.$boundary.$this->newline
  1310.  
  1311. .'Content-Type: text/html; charset='.$this->charset.$this->newline
  1312. .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline;
  1313. }
  1314.  
  1315. $this->_finalbody = $body.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline;
  1316.  
  1317. if ($this->_get_protocol() === 'mail')
  1318. {
  1319. $this->_header_str .= $hdr;
  1320. }
  1321. else
  1322. {
  1323. $this->_finalbody = $hdr.$this->newline.$this->newline.$this->_finalbody;
  1324. }
  1325.  
  1326. if ($this->send_multipart !== FALSE)
  1327. {
  1328. $this->_finalbody .= '--'.$boundary.'--';
  1329. }
  1330.  
  1331. return;
  1332.  
  1333. case 'plain-attach':
  1334.  
  1335. $boundary = uniqid('B_ATC_');
  1336. $hdr .= 'Content-Type: multipart/mixed; boundary="'.$boundary.'"';
  1337.  
  1338. if ($this->_get_protocol() === 'mail')
  1339. {
  1340. $this->_header_str .= $hdr;
  1341. }
  1342.  
  1343. $body .= $this->_get_mime_message().$this->newline
  1344. .$this->newline
  1345. .'--'.$boundary.$this->newline
  1346. .'Content-Type: text/plain; charset='.$this->charset.$this->newline
  1347. .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline
  1348. .$this->newline
  1349. .$this->_body.$this->newline.$this->newline;
  1350.  
  1351. $this->_append_attachments($body, $boundary);
  1352.  
  1353. break;
  1354. case 'html-attach':
  1355.  
  1356. $alt_boundary = uniqid('B_ALT_');
  1357. $last_boundary = NULL;
  1358.  
  1359. if ($this->_attachments_have_multipart('mixed'))
  1360. {
  1361. $atc_boundary = uniqid('B_ATC_');
  1362. $hdr .= 'Content-Type: multipart/mixed; boundary="'.$atc_boundary.'"';
  1363. $last_boundary = $atc_boundary;
  1364. }
  1365.  
  1366. if ($this->_attachments_have_multipart('related'))
  1367. {
  1368. $rel_boundary = uniqid('B_REL_');
  1369. $rel_boundary_header = 'Content-Type: multipart/related; boundary="'.$rel_boundary.'"';
  1370.  
  1371. if (isset($last_boundary))
  1372. {
  1373. $body .= '--'.$last_boundary.$this->newline.$rel_boundary_header;
  1374. }
  1375. else
  1376. {
  1377. $hdr .= $rel_boundary_header;
  1378. }
  1379.  
  1380. $last_boundary = $rel_boundary;
  1381. }
  1382.  
  1383. if ($this->_get_protocol() === 'mail')
  1384. {
  1385. $this->_header_str .= $hdr;
  1386. }
  1387.  
  1388. self::strlen($body) && $body .= $this->newline.$this->newline;
  1389. $body .= $this->_get_mime_message().$this->newline.$this->newline
  1390. .'--'.$last_boundary.$this->newline
  1391.  
  1392. .'Content-Type: multipart/alternative; boundary="'.$alt_boundary.'"'.$this->newline.$this->newline
  1393. .'--'.$alt_boundary.$this->newline
  1394.  
  1395. .'Content-Type: text/plain; charset='.$this->charset.$this->newline
  1396. .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline
  1397. .$this->_get_alt_message().$this->newline.$this->newline
  1398. .'--'.$alt_boundary.$this->newline
  1399.  
  1400. .'Content-Type: text/html; charset='.$this->charset.$this->newline
  1401. .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline
  1402.  
  1403. .$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline
  1404. .'--'.$alt_boundary.'--'.$this->newline.$this->newline;
  1405.  
  1406. if ( ! empty($rel_boundary))
  1407. {
  1408. $body .= $this->newline.$this->newline;
  1409. $this->_append_attachments($body, $rel_boundary, 'related');
  1410. }
  1411.  
  1412. // multipart/mixed attachments
  1413. if ( ! empty($atc_boundary))
  1414. {
  1415. $body .= $this->newline.$this->newline;
  1416. $this->_append_attachments($body, $atc_boundary, 'mixed');
  1417. }
  1418.  
  1419. break;
  1420. }
  1421.  
  1422. $this->_finalbody = ($this->_get_protocol() === 'mail')
  1423. ? $body
  1424. : $hdr.$this->newline.$this->newline.$body;
  1425.  
  1426. return TRUE;
  1427. }
  1428.  
  1429. // --------------------------------------------------------------------
  1430.  
  1431. protected function _attachments_have_multipart($type)
  1432. {
  1433. foreach ($this->_attachments as &$attachment)
  1434. {
  1435. if ($attachment['multipart'] === $type)
  1436. {
  1437. return TRUE;
  1438. }
  1439. }
  1440.  
  1441. return FALSE;
  1442. }
  1443.  
  1444. // --------------------------------------------------------------------
  1445.  
  1446. /**
  1447. * Prepares attachment string
  1448. *
  1449. * @param string $body Message body to append to
  1450. * @param string $boundary Multipart boundary
  1451. * @param string $multipart When provided, only attachments of this type will be processed
  1452. * @return string
  1453. */
  1454. protected function _append_attachments(&$body, $boundary, $multipart = null)
  1455. {
  1456. for ($i = 0, $c = count($this->_attachments); $i < $c; $i++)
  1457. {
  1458. if (isset($multipart) && $this->_attachments[$i]['multipart'] !== $multipart)
  1459. {
  1460. continue;
  1461. }
  1462.  
  1463. $name = isset($this->_attachments[$i]['name'][1])
  1464. ? $this->_attachments[$i]['name'][1]
  1465. : basename($this->_attachments[$i]['name'][0]);
  1466.  
  1467. $body .= '--'.$boundary.$this->newline
  1468. .'Content-Type: '.$this->_attachments[$i]['type'].'; name="'.$name.'"'.$this->newline
  1469. .'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline
  1470. .'Content-Transfer-Encoding: base64'.$this->newline
  1471. .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline)
  1472. .$this->newline
  1473. .$this->_attachments[$i]['content'].$this->newline;
  1474. }
  1475.  
  1476. // $name won't be set if no attachments were appended,
  1477. // and therefore a boundary wouldn't be necessary
  1478. empty($name) OR $body .= '--'.$boundary.'--';
  1479. }
  1480.  
  1481. // --------------------------------------------------------------------
  1482.  
  1483. /**
  1484. * Prep Quoted Printable
  1485. *
  1486. * Prepares string for Quoted-Printable Content-Transfer-Encoding
  1487. * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
  1488. *
  1489. * @param string
  1490. * @return string
  1491. */
  1492. protected function _prep_quoted_printable($str)
  1493. {
  1494. // ASCII code numbers for "safe" characters that can always be
  1495. // used literally, without encoding, as described in RFC 2049.
  1496. // http://www.ietf.org/rfc/rfc2049.txt
  1497. static $ascii_safe_chars = array(
  1498. // ' ( ) + , - . / : = ?
  1499. 39, 40, 41, 43, 44, 45, 46, 47, 58, 61, 63,
  1500. // numbers
  1501. 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
  1502. // upper-case letters
  1503. 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
  1504. // lower-case letters
  1505. 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122
  1506. );
  1507.  
  1508. // We are intentionally wrapping so mail servers will encode characters
  1509. // properly and MUAs will behave, so {unwrap} must go!
  1510. $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
  1511.  
  1512. // RFC 2045 specifies CRLF as "\r\n".
  1513. // However, many developers choose to override that and violate
  1514. // the RFC rules due to (apparently) a bug in MS Exchange,
  1515. // which only works with "\n".
  1516. if ($this->crlf === "\r\n")
  1517. {
  1518. return quoted_printable_encode($str);
  1519. }
  1520.  
  1521. // Reduce multiple spaces & remove nulls
  1522. $str = preg_replace(array('| +|', '/\x00+/'), array(' ', ''), $str);
  1523.  
  1524. // Standardize newlines
  1525. if (strpos($str, "\r") !== FALSE)
  1526. {
  1527. $str = str_replace(array("\r\n", "\r"), "\n", $str);
  1528. }
  1529.  
  1530. $escape = '=';
  1531. $output = '';
  1532.  
  1533. foreach (explode("\n", $str) as $line)
  1534. {
  1535. $length = self::strlen($line);
  1536. $temp = '';
  1537.  
  1538. // Loop through each character in the line to add soft-wrap
  1539. // characters at the end of a line " =\r\n" and add the newly
  1540. // processed line(s) to the output (see comment on $crlf class property)
  1541. for ($i = 0; $i < $length; $i++)
  1542. {
  1543. // Grab the next character
  1544. $char = $line[$i];
  1545. $ascii = ord($char);
  1546.  
  1547. // Convert spaces and tabs but only if it's the end of the line
  1548. if ($ascii === 32 OR $ascii === 9)
  1549. {
  1550. if ($i === ($length - 1))
  1551. {
  1552. $char = $escape.sprintf('%02s', dechex($ascii));
  1553. }
  1554. }
  1555. // DO NOT move this below the $ascii_safe_chars line!
  1556. //
  1557. // = (equals) signs are allowed by RFC2049, but must be encoded
  1558. // as they are the encoding delimiter!
  1559. elseif ($ascii === 61)
  1560. {
  1561. $char = $escape.strtoupper(sprintf('%02s', dechex($ascii))); // =3D
  1562. }
  1563. elseif ( ! in_array($ascii, $ascii_safe_chars, TRUE))
  1564. {
  1565. $char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));
  1566. }
  1567.  
  1568. // If we're at the character limit, add the line to the output,
  1569. // reset our temp variable, and keep on chuggin'
  1570. if ((self::strlen($temp) + self::strlen($char)) >= 76)
  1571. {
  1572. $output .= $temp.$escape.$this->crlf;
  1573. $temp = '';
  1574. }
  1575.  
  1576. // Add the character to our temporary line
  1577. $temp .= $char;
  1578. }
  1579.  
  1580. // Add our completed line to the output
  1581. $output .= $temp.$this->crlf;
  1582. }
  1583.  
  1584. // get rid of extra CRLF tacked onto the end
  1585. return self::substr($output, 0, self::strlen($this->crlf) * -1);
  1586. }
  1587.  
  1588. // --------------------------------------------------------------------
  1589.  
  1590. /**
  1591. * Prep Q Encoding
  1592. *
  1593. * Performs "Q Encoding" on a string for use in email headers.
  1594. * It's related but not identical to quoted-printable, so it has its
  1595. * own method.
  1596. *
  1597. * @param string
  1598. * @return string
  1599. */
  1600. protected function _prep_q_encoding($str)
  1601. {
  1602. $str = str_replace(array("\r", "\n"), '', $str);
  1603.  
  1604. if ($this->charset === 'UTF-8')
  1605. {
  1606. // Note: We used to have mb_encode_mimeheader() as the first choice
  1607. // here, but it turned out to be buggy and unreliable. DO NOT
  1608. // re-add it! -- Narf
  1609. if (ICONV_ENABLED === TRUE)
  1610. {
  1611. $output = @iconv_mime_encode('', $str,
  1612. array(
  1613. 'scheme' => 'Q',
  1614. 'line-length' => 76,
  1615. 'input-charset' => $this->charset,
  1616. 'output-charset' => $this->charset,
  1617. 'line-break-chars' => $this->crlf
  1618. )
  1619. );
  1620.  
  1621. // There are reports that iconv_mime_encode() might fail and return FALSE
  1622. if ($output !== FALSE)
  1623. {
  1624. // iconv_mime_encode() will always put a header field name.
  1625. // We've passed it an empty one, but it still prepends our
  1626. // encoded string with ': ', so we need to strip it.
  1627. return self::substr($output, 2);
  1628. }
  1629.  
  1630. $chars = iconv_strlen($str, 'UTF-8');
  1631. }
  1632. elseif (MB_ENABLED === TRUE)
  1633. {
  1634. $chars = mb_strlen($str, 'UTF-8');
  1635. }
  1636. }
  1637.  
  1638. // We might already have this set for UTF-8
  1639. isset($chars) OR $chars = self::strlen($str);
  1640.  
  1641. $output = '=?'.$this->charset.'?Q?';
  1642. for ($i = 0, $length = self::strlen($output); $i < $chars; $i++)
  1643. {
  1644. $chr = ($this->charset === 'UTF-8' && ICONV_ENABLED === TRUE)
  1645. ? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2))
  1646. : '='.strtoupper(bin2hex($str[$i]));
  1647.  
  1648. // RFC 2045 sets a limit of 76 characters per line.
  1649. // We'll append ?= to the end of each line though.
  1650. if ($length + ($l = self::strlen($chr)) > 74)
  1651. {
  1652. $output .= '?='.$this->crlf // EOL
  1653. .' =?'.$this->charset.'?Q?'.$chr; // New line
  1654. $length = 6 + self::strlen($this->charset) + $l; // Reset the length for the new line
  1655. }
  1656. else
  1657. {
  1658. $output .= $chr;
  1659. $length += $l;
  1660. }
  1661. }
  1662.  
  1663. // End the header
  1664. return $output.'?=';
  1665. }
  1666.  
  1667. // --------------------------------------------------------------------
  1668.  
  1669. /**
  1670. * Send Email
  1671. *
  1672. * @param bool $auto_clear = TRUE
  1673. * @return bool
  1674. */
  1675. public function send($auto_clear = TRUE)
  1676. {
  1677. if ( ! isset($this->_headers['From']))
  1678. {
  1679. $this->_set_error_message('lang:email_no_from');
  1680. return FALSE;
  1681. }
  1682.  
  1683. if ($this->_replyto_flag === FALSE)
  1684. {
  1685. $this->reply_to($this->_headers['From']);
  1686. }
  1687.  
  1688. if ( ! isset($this->_recipients) && ! isset($this->_headers['To'])
  1689. && ! isset($this->_bcc_array) && ! isset($this->_headers['Bcc'])
  1690. && ! isset($this->_headers['Cc']))
  1691. {
  1692. $this->_set_error_message('lang:email_no_recipients');
  1693. return FALSE;
  1694. }
  1695.  
  1696. $this->_build_headers();
  1697.  
  1698. if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size)
  1699. {
  1700. $result = $this->batch_bcc_send();
  1701.  
  1702. if ($result && $auto_clear)
  1703. {
  1704. $this->clear();
  1705. }
  1706.  
  1707. return $result;
  1708. }
  1709.  
  1710. if ($this->_build_message() === FALSE)
  1711. {
  1712. return FALSE;
  1713. }
  1714.  
  1715. $result = $this->_spool_email();
  1716.  
  1717. if ($result && $auto_clear)
  1718. {
  1719. $this->clear();
  1720. }
  1721.  
  1722. return $result;
  1723. }
  1724.  
  1725. // --------------------------------------------------------------------
  1726.  
  1727. /**
  1728. * Batch Bcc Send. Sends groups of BCCs in batches
  1729. *
  1730. * @return void
  1731. */
  1732. public function batch_bcc_send()
  1733. {
  1734. $float = $this->bcc_batch_size - 1;
  1735. $set = '';
  1736. $chunk = array();
  1737.  
  1738. for ($i = 0, $c = count($this->_bcc_array); $i < $c; $i++)
  1739. {
  1740. if (isset($this->_bcc_array[$i]))
  1741. {
  1742. $set .= ', '.$this->_bcc_array[$i];
  1743. }
  1744.  
  1745. if ($i === $float)
  1746. {
  1747. $chunk[] = self::substr($set, 1);
  1748. $float += $this->bcc_batch_size;
  1749. $set = '';
  1750. }
  1751.  
  1752. if ($i === $c-1)
  1753. {
  1754. $chunk[] = self::substr($set, 1);
  1755. }
  1756. }
  1757.  
  1758. for ($i = 0, $c = count($chunk); $i < $c; $i++)
  1759. {
  1760. unset($this->_headers['Bcc']);
  1761.  
  1762. $bcc = $this->clean_email($this->_str_to_array($chunk[$i]));
  1763.  
  1764. if ($this->protocol !== 'smtp')
  1765. {
  1766. $this->set_header('Bcc', implode(', ', $bcc));
  1767. }
  1768. else
  1769. {
  1770. $this->_bcc_array = $bcc;
  1771. }
  1772.  
  1773. if ($this->_build_message() === FALSE)
  1774. {
  1775. return FALSE;
  1776. }
  1777.  
  1778. $this->_spool_email();
  1779. }
  1780. }
  1781.  
  1782. // --------------------------------------------------------------------
  1783.  
  1784. /**
  1785. * Unwrap special elements
  1786. *
  1787. * @return void
  1788. */
  1789. protected function _unwrap_specials()
  1790. {
  1791. $this->_finalbody = preg_replace_callback('/\{unwrap\}(.*?)\{\/unwrap\}/si', array($this, '_remove_nl_callback'), $this->_finalbody);
  1792. }
  1793.  
  1794. // --------------------------------------------------------------------
  1795.  
  1796. /**
  1797. * Strip line-breaks via callback
  1798. *
  1799. * @param string $matches
  1800. * @return string
  1801. */
  1802. protected function _remove_nl_callback($matches)
  1803. {
  1804. if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
  1805. {
  1806. $matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);
  1807. }
  1808.  
  1809. return $matches[1];
  1810. }
  1811.  
  1812. // --------------------------------------------------------------------
  1813.  
  1814. /**
  1815. * Spool mail to the mail server
  1816. *
  1817. * @return bool
  1818. */
  1819. protected function _spool_email()
  1820. {
  1821. $this->_unwrap_specials();
  1822.  
  1823. $protocol = $this->_get_protocol();
  1824. $method = '_send_with_'.$protocol;
  1825. if ( ! $this->$method())
  1826. {
  1827. $this->_set_error_message('lang:email_send_failure_'.($protocol === 'mail' ? 'phpmail' : $protocol));
  1828. return FALSE;
  1829. }
  1830.  
  1831. $this->_set_error_message('lang:email_sent', $protocol);
  1832. return TRUE;
  1833. }
  1834.  
  1835. // --------------------------------------------------------------------
  1836.  
  1837. /**
  1838. * Validate email for shell
  1839. *
  1840. * Applies stricter, shell-safe validation to email addresses.
  1841. * Introduced to prevent RCE via sendmail's -f option.
  1842. *
  1843. * @see https://github.com/bcit-ci/CodeIgniter/issues/4963
  1844. * @see https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36
  1845. * @license https://creativecommons.org/publicdomain/zero/1.0/ CC0 1.0, Public Domain
  1846. *
  1847. * Credits for the base concept go to Paul Buonopane <paul@namepros.com>
  1848. *
  1849. * @param string $email
  1850. * @return bool
  1851. */
  1852. protected function _validate_email_for_shell(&$email)
  1853. {
  1854. if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))
  1855. {
  1856. $email = self::substr($email, 0, ++$atpos).idn_to_ascii(self::substr($email, $atpos));
  1857. }
  1858.  
  1859. return (filter_var($email, FILTER_VALIDATE_EMAIL) === $email && preg_match('#\A[a-z0-9._+-]+@[a-z0-9.-]{1,253}\z#i', $email));
  1860. }
  1861.  
  1862. // --------------------------------------------------------------------
  1863.  
  1864. /**
  1865. * Send using mail()
  1866. *
  1867. * @return bool
  1868. */
  1869. protected function _send_with_mail()
  1870. {
  1871. if (is_array($this->_recipients))
  1872. {
  1873. $this->_recipients = implode(', ', $this->_recipients);
  1874. }
  1875.  
  1876. // _validate_email_for_shell() below accepts by reference,
  1877. // so this needs to be assigned to a variable
  1878. $from = $this->clean_email($this->_headers['Return-Path']);
  1879.  
  1880. if ($this->_safe_mode === TRUE || ! $this->_validate_email_for_shell($from))
  1881. {
  1882. return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);
  1883. }
  1884. else
  1885. {
  1886. // most documentation of sendmail using the "-f" flag lacks a space after it, however
  1887. // we've encountered servers that seem to require it to be in place.
  1888. return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$from);
  1889. }
  1890. }
  1891.  
  1892. // --------------------------------------------------------------------
  1893.  
  1894. /**
  1895. * Send using Sendmail
  1896. *
  1897. * @return bool
  1898. */
  1899. protected function _send_with_sendmail()
  1900. {
  1901. // _validate_email_for_shell() below accepts by reference,
  1902. // so this needs to be assigned to a variable
  1903. $from = $this->clean_email($this->_headers['From']);
  1904. if ($this->_validate_email_for_shell($from))
  1905. {
  1906. $from = '-f '.$from;
  1907. }
  1908. else
  1909. {
  1910. $from = '';
  1911. }
  1912.  
  1913. // is popen() enabled?
  1914. if ( ! function_usable('popen') OR FALSE === ($fp = @popen($this->mailpath.' -oi '.$from.' -t', 'w')))
  1915. {
  1916. // server probably has popen disabled, so nothing we can do to get a verbose error.
  1917. return FALSE;
  1918. }
  1919.  
  1920. fputs($fp, $this->_header_str);
  1921. fputs($fp, $this->_finalbody);
  1922.  
  1923. $status = pclose($fp);
  1924.  
  1925. if ($status !== 0)
  1926. {
  1927. $this->_set_error_message('lang:email_exit_status', $status);
  1928. $this->_set_error_message('lang:email_no_socket');
  1929. return FALSE;
  1930. }
  1931.  
  1932. return TRUE;
  1933. }
  1934.  
  1935. // --------------------------------------------------------------------
  1936.  
  1937. /**
  1938. * Send using SMTP
  1939. *
  1940. * @return bool
  1941. */
  1942. protected function _send_with_smtp()
  1943. {
  1944. if ($this->smtp_host === '')
  1945. {
  1946. $this->_set_error_message('lang:email_no_hostname');
  1947. return FALSE;
  1948. }
  1949.  
  1950. if ( ! $this->_smtp_connect() OR ! $this->_smtp_authenticate())
  1951. {
  1952. return FALSE;
  1953. }
  1954.  
  1955. if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From'])))
  1956. {
  1957. $this->_smtp_end();
  1958. return FALSE;
  1959. }
  1960.  
  1961. foreach ($this->_recipients as $val)
  1962. {
  1963. if ( ! $this->_send_command('to', $val))
  1964. {
  1965. $this->_smtp_end();
  1966. return FALSE;
  1967. }
  1968. }
  1969.  
  1970. if (count($this->_cc_array) > 0)
  1971. {
  1972. foreach ($this->_cc_array as $val)
  1973. {
  1974. if ($val !== '' && ! $this->_send_command('to', $val))
  1975. {
  1976. $this->_smtp_end();
  1977. return FALSE;
  1978. }
  1979. }
  1980. }
  1981.  
  1982. if (count($this->_bcc_array) > 0)
  1983. {
  1984. foreach ($this->_bcc_array as $val)
  1985. {
  1986. if ($val !== '' && ! $this->_send_command('to', $val))
  1987. {
  1988. $this->_smtp_end();
  1989. return FALSE;
  1990. }
  1991. }
  1992. }
  1993.  
  1994. if ( ! $this->_send_command('data'))
  1995. {
  1996. $this->_smtp_end();
  1997. return FALSE;
  1998. }
  1999.  
  2000. // perform dot transformation on any lines that begin with a dot
  2001. $this->_send_data($this->_header_str.preg_replace('/^\./m', '..$1', $this->_finalbody));
  2002.  
  2003. $this->_send_data('.');
  2004.  
  2005. $reply = $this->_get_smtp_data();
  2006. $this->_set_error_message($reply);
  2007.  
  2008. $this->_smtp_end();
  2009.  
  2010. if (strpos($reply, '250') !== 0)
  2011. {
  2012. $this->_set_error_message('lang:email_smtp_error', $reply);
  2013. return FALSE;
  2014. }
  2015.  
  2016. return TRUE;
  2017. }
  2018.  
  2019. // --------------------------------------------------------------------
  2020.  
  2021. /**
  2022. * SMTP End
  2023. *
  2024. * Shortcut to send RSET or QUIT depending on keep-alive
  2025. *
  2026. * @return void
  2027. */
  2028. protected function _smtp_end()
  2029. {
  2030. ($this->smtp_keepalive)
  2031. ? $this->_send_command('reset')
  2032. : $this->_send_command('quit');
  2033. }
  2034.  
  2035. // --------------------------------------------------------------------
  2036.  
  2037. /**
  2038. * SMTP Connect
  2039. *
  2040. * @return string
  2041. */
  2042. protected function _smtp_connect()
  2043. {
  2044. if (is_resource($this->_smtp_connect))
  2045. {
  2046. return TRUE;
  2047. }
  2048.  
  2049. $ssl = ($this->smtp_crypto === 'ssl') ? 'ssl://' : '';
  2050.  
  2051. $this->_smtp_connect = fsockopen($ssl.$this->smtp_host,
  2052. $this->smtp_port,
  2053. $errno,
  2054. $errstr,
  2055. $this->smtp_timeout);
  2056.  
  2057. if ( ! is_resource($this->_smtp_connect))
  2058. {
  2059. $this->_set_error_message('lang:email_smtp_error', $errno.' '.$errstr);
  2060. return FALSE;
  2061. }
  2062.  
  2063. stream_set_timeout($this->_smtp_connect, $this->smtp_timeout);
  2064. $this->_set_error_message($this->_get_smtp_data());
  2065.  
  2066. if ($this->smtp_crypto === 'tls')
  2067. {
  2068. $this->_send_command('hello');
  2069. $this->_send_command('starttls');
  2070.  
  2071. $crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
  2072.  
  2073. if ($crypto !== TRUE)
  2074. {
  2075. $this->_set_error_message('lang:email_smtp_error', $this->_get_smtp_data());
  2076. return FALSE;
  2077. }
  2078. }
  2079.  
  2080. return $this->_send_command('hello');
  2081. }
  2082.  
  2083. // --------------------------------------------------------------------
  2084.  
  2085. /**
  2086. * Send SMTP command
  2087. *
  2088. * @param string
  2089. * @param string
  2090. * @return bool
  2091. */
  2092. protected function _send_command($cmd, $data = '')
  2093. {
  2094. switch ($cmd)
  2095. {
  2096. case 'hello' :
  2097.  
  2098. if ($this->_smtp_auth OR $this->_get_encoding() === '8bit')
  2099. {
  2100. $this->_send_data('EHLO '.$this->_get_hostname());
  2101. }
  2102. else
  2103. {
  2104. $this->_send_data('HELO '.$this->_get_hostname());
  2105. }
  2106.  
  2107. $resp = 250;
  2108. break;
  2109. case 'starttls' :
  2110.  
  2111. $this->_send_data('STARTTLS');
  2112. $resp = 220;
  2113. break;
  2114. case 'from' :
  2115.  
  2116. $this->_send_data('MAIL FROM:<'.$data.'>');
  2117. $resp = 250;
  2118. break;
  2119. case 'to' :
  2120.  
  2121. if ($this->dsn)
  2122. {
  2123. $this->_send_data('RCPT TO:<'.$data.'> NOTIFY=SUCCESS,DELAY,FAILURE ORCPT=rfc822;'.$data);
  2124. }
  2125. else
  2126. {
  2127. $this->_send_data('RCPT TO:<'.$data.'>');
  2128. }
  2129.  
  2130. $resp = 250;
  2131. break;
  2132. case 'data' :
  2133.  
  2134. $this->_send_data('DATA');
  2135. $resp = 354;
  2136. break;
  2137. case 'reset':
  2138.  
  2139. $this->_send_data('RSET');
  2140. $resp = 250;
  2141. break;
  2142. case 'quit' :
  2143.  
  2144. $this->_send_data('QUIT');
  2145. $resp = 221;
  2146. break;
  2147. }
  2148.  
  2149. $reply = $this->_get_smtp_data();
  2150.  
  2151. $this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';
  2152.  
  2153. if ((int) self::substr($reply, 0, 3) !== $resp)
  2154. {
  2155. $this->_set_error_message('lang:email_smtp_error', $reply);
  2156. return FALSE;
  2157. }
  2158.  
  2159. if ($cmd === 'quit')
  2160. {
  2161. fclose($this->_smtp_connect);
  2162. }
  2163.  
  2164. return TRUE;
  2165. }
  2166.  
  2167. // --------------------------------------------------------------------
  2168.  
  2169. /**
  2170. * SMTP Authenticate
  2171. *
  2172. * @return bool
  2173. */
  2174. protected function _smtp_authenticate()
  2175. {
  2176. if ( ! $this->_smtp_auth)
  2177. {
  2178. return TRUE;
  2179. }
  2180.  
  2181. if ($this->smtp_user === '' && $this->smtp_pass === '')
  2182. {
  2183. $this->_set_error_message('lang:email_no_smtp_unpw');
  2184. return FALSE;
  2185. }
  2186.  
  2187. $this->_send_data('AUTH LOGIN');
  2188.  
  2189. $reply = $this->_get_smtp_data();
  2190.  
  2191. if (strpos($reply, '503') === 0) // Already authenticated
  2192. {
  2193. return TRUE;
  2194. }
  2195. elseif (strpos($reply, '334') !== 0)
  2196. {
  2197. $this->_set_error_message('lang:email_failed_smtp_login', $reply);
  2198. return FALSE;
  2199. }
  2200.  
  2201. $this->_send_data(base64_encode($this->smtp_user));
  2202.  
  2203. $reply = $this->_get_smtp_data();
  2204.  
  2205. if (strpos($reply, '334') !== 0)
  2206. {
  2207. $this->_set_error_message('lang:email_smtp_auth_un', $reply);
  2208. return FALSE;
  2209. }
  2210.  
  2211. $this->_send_data(base64_encode($this->smtp_pass));
  2212.  
  2213. $reply = $this->_get_smtp_data();
  2214.  
  2215. if (strpos($reply, '235') !== 0)
  2216. {
  2217. $this->_set_error_message('lang:email_smtp_auth_pw', $reply);
  2218. return FALSE;
  2219. }
  2220.  
  2221. if ($this->smtp_keepalive)
  2222. {
  2223. $this->_smtp_auth = FALSE;
  2224. }
  2225.  
  2226. return TRUE;
  2227. }
  2228.  
  2229. // --------------------------------------------------------------------
  2230.  
  2231. /**
  2232. * Send SMTP data
  2233. *
  2234. * @param string $data
  2235. * @return bool
  2236. */
  2237. protected function _send_data($data)
  2238. {
  2239. $data .= $this->newline;
  2240. for ($written = $timestamp = 0, $length = self::strlen($data); $written < $length; $written += $result)
  2241. {
  2242. if (($result = fwrite($this->_smtp_connect, self::substr($data, $written))) === FALSE)
  2243. {
  2244. break;
  2245. }
  2246. // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951
  2247. elseif ($result === 0)
  2248. {
  2249. if ($timestamp === 0)
  2250. {
  2251. $timestamp = time();
  2252. }
  2253. elseif ($timestamp < (time() - $this->smtp_timeout))
  2254. {
  2255. $result = FALSE;
  2256. break;
  2257. }
  2258.  
  2259. usleep(250000);
  2260. continue;
  2261. }
  2262. else
  2263. {
  2264. $timestamp = 0;
  2265. }
  2266. }
  2267.  
  2268. if ($result === FALSE)
  2269. {
  2270. $this->_set_error_message('lang:email_smtp_data_failure', $data);
  2271. return FALSE;
  2272. }
  2273.  
  2274. return TRUE;
  2275. }
  2276.  
  2277. // --------------------------------------------------------------------
  2278.  
  2279. /**
  2280. * Get SMTP data
  2281. *
  2282. * @return string
  2283. */
  2284. protected function _get_smtp_data()
  2285. {
  2286. $data = '';
  2287.  
  2288. while ($str = fgets($this->_smtp_connect, 512))
  2289. {
  2290. $data .= $str;
  2291.  
  2292. if ($str[3] === ' ')
  2293. {
  2294. break;
  2295. }
  2296. }
  2297.  
  2298. return $data;
  2299. }
  2300.  
  2301. // --------------------------------------------------------------------
  2302.  
  2303. /**
  2304. * Get Hostname
  2305. *
  2306. * There are only two legal types of hostname - either a fully
  2307. * qualified domain name (eg: "mail.example.com") or an IP literal
  2308. * (eg: "[1.2.3.4]").
  2309. *
  2310. * @link https://tools.ietf.org/html/rfc5321#section-2.3.5
  2311. * @link http://cbl.abuseat.org/namingproblems.html
  2312. * @return string
  2313. */
  2314. protected function _get_hostname()
  2315. {
  2316. if (isset($_SERVER['SERVER_NAME']))
  2317. {
  2318. return $_SERVER['SERVER_NAME'];
  2319. }
  2320.  
  2321. return isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]';
  2322. }
  2323.  
  2324. // --------------------------------------------------------------------
  2325.  
  2326. /**
  2327. * Get Debug Message
  2328. *
  2329. * @param array $include List of raw data chunks to include in the output
  2330. * Valid options are: 'headers', 'subject', 'body'
  2331. * @return string
  2332. */
  2333. public function print_debugger($include = array('headers', 'subject', 'body'))
  2334. {
  2335. $msg = '';
  2336.  
  2337. if (count($this->_debug_msg) > 0)
  2338. {
  2339. foreach ($this->_debug_msg as $val)
  2340. {
  2341. $msg .= $val;
  2342. }
  2343. }
  2344.  
  2345. // Determine which parts of our raw data needs to be printed
  2346. $raw_data = '';
  2347. is_array($include) OR $include = array($include);
  2348.  
  2349. if (in_array('headers', $include, TRUE))
  2350. {
  2351. $raw_data = htmlspecialchars($this->_header_str)."\n";
  2352. }
  2353.  
  2354. if (in_array('subject', $include, TRUE))
  2355. {
  2356. $raw_data .= htmlspecialchars($this->_subject)."\n";
  2357. }
  2358.  
  2359. if (in_array('body', $include, TRUE))
  2360. {
  2361. $raw_data .= htmlspecialchars($this->_finalbody);
  2362. }
  2363.  
  2364. return $msg.($raw_data === '' ? '' : '<pre>'.$raw_data.'</pre>');
  2365. }
  2366.  
  2367. // --------------------------------------------------------------------
  2368.  
  2369. /**
  2370. * Set Message
  2371. *
  2372. * @param string $msg
  2373. * @param string $val = ''
  2374. * @return void
  2375. */
  2376. protected function _set_error_message($msg, $val = '')
  2377. {
  2378. $CI =& get_instance();
  2379. $CI->lang->load('email');
  2380.  
  2381. if (sscanf($msg, 'lang:%s', $line) !== 1 OR FALSE === ($line = $CI->lang->line($line)))
  2382. {
  2383. $this->_debug_msg[] = str_replace('%s', $val, $msg).'<br />';
  2384. }
  2385. else
  2386. {
  2387. $this->_debug_msg[] = str_replace('%s', $val, $line).'<br />';
  2388. }
  2389. }
  2390.  
  2391. // --------------------------------------------------------------------
  2392.  
  2393. /**
  2394. * Mime Types
  2395. *
  2396. * @param string
  2397. * @return string
  2398. */
  2399. protected function _mime_types($ext = '')
  2400. {
  2401. $ext = strtolower($ext);
  2402.  
  2403. $mimes =& get_mimes();
  2404.  
  2405. if (isset($mimes[$ext]))
  2406. {
  2407. return is_array($mimes[$ext])
  2408. ? current($mimes[$ext])
  2409. : $mimes[$ext];
  2410. }
  2411.  
  2412. return 'application/x-unknown-content-type';
  2413. }
  2414.  
  2415. // --------------------------------------------------------------------
  2416.  
  2417. /**
  2418. * Destructor
  2419. *
  2420. * @return void
  2421. */
  2422. public function __destruct()
  2423. {
  2424. is_resource($this->_smtp_connect) && $this->_send_command('quit');
  2425. }
  2426.  
  2427. // --------------------------------------------------------------------
  2428.  
  2429. /**
  2430. * Byte-safe strlen()
  2431. *
  2432. * @param string $str
  2433. * @return int
  2434. */
  2435. protected static function strlen($str)
  2436. {
  2437. return (self::$func_overload)
  2438. ? mb_strlen($str, '8bit')
  2439. : strlen($str);
  2440. }
  2441.  
  2442. // --------------------------------------------------------------------
  2443.  
  2444. /**
  2445. * Byte-safe substr()
  2446. *
  2447. * @param string $str
  2448. * @param int $start
  2449. * @param int $length
  2450. * @return string
  2451. */
  2452. protected static function substr($str, $start, $length = NULL)
  2453. {
  2454. if (self::$func_overload)
  2455. {
  2456. // mb_substr($str, $start, null, '8bit') returns an empty
  2457. // string on PHP 5.3
  2458. isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
  2459. return mb_substr($str, $start, $length, '8bit');
  2460. }
  2461.  
  2462. return isset($length)
  2463. ? substr($str, $start, $length)
  2464. : substr($str, $start);
  2465. }
  2466. }
Add Comment
Please, Sign In to add comment