Email.php 53 KB

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