PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/wolf/helpers/Email.php

http://wolfcms.googlecode.com/
PHP | 1399 lines | 830 code | 213 blank | 356 comment | 194 complexity | 7ee734d81f58ac3765bff9777ddcdcdd MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /*
  3. * Wolf CMS - Content Management Simplified. <http://www.wolfcms.org>
  4. * Copyright (C) 2009-2010 Martijn van der Kleijn <martijn.niji@gmail.com>
  5. * Copyright (C) 2008 Philippe Archambault <philippe.archambault@gmail.com>
  6. *
  7. * Please see license.txt for the full license text.
  8. */
  9. /**
  10. * Simple Email library
  11. *
  12. * Permits email to be sent using Mail, Sendmail, or SMTP.
  13. *
  14. * LICENSE: see license.txt and exception.txt for the full license texts.
  15. *
  16. * @package wolf
  17. * @subpackage helpers
  18. * @author Philippe Archambault <philippe.archambault@gmail.com>
  19. * @author Martijn van der Kleijn <martijn.niji@gmail.com>
  20. * @copyright 2008-2010 Martijn van der Kleijn, Philippe Archambault
  21. * @license http://www.wolfcms.org/about/wolf-cms-licensing.html
  22. * @version SVN: $Id: Email.php 226 2010-08-17 09:33:31Z djreimer $
  23. * @since Available since release 0.0.1
  24. */
  25. /**
  26. * Permits email to be sent using Mail, Sendmail, or SMTP.
  27. *
  28. * Note: some values are hardcoded at the moment.
  29. */
  30. class Email {
  31. public $useragent = "Wolf framework";
  32. public $mailpath = "/usr/sbin/sendmail"; // Sendmail path
  33. public $protocol = "mail"; // mail/sendmail/smtp
  34. public $smtp_host = ""; // SMTP Server. Example: mail.earthlink.net
  35. public $smtp_user = ""; // SMTP Username
  36. public $smtp_pass = ""; // SMTP Password
  37. public $smtp_port = "25"; // SMTP Port
  38. public $smtp_timeout = 5; // SMTP Timeout in seconds
  39. public $wordwrap = true; // true/false Turns word-wrap on/off
  40. public $wrapchars = "76"; // Number of characters to wrap at.
  41. public $mailtype = "text"; // text/html Defines email formatting
  42. public $charset = "utf-8"; // Default char set: iso-8859-1 or us-ascii
  43. public $multipart = "mixed"; // "mixed" (in the body) or "related" (separate)
  44. public $alt_message = ''; // Alternative message for HTML emails
  45. public $validate = false; // true/false. Enables email validation
  46. public $priority = "3"; // Default priority (1 - 5)
  47. public $newline = "\n"; // Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
  48. public $bcc_batch_mode = false; // true/false Turns on/off Bcc batch feature
  49. public $bcc_batch_size = 200; // If bcc_batch_mode = true, sets max number of Bccs in each batch
  50. private $_subject = "";
  51. private $_body = "";
  52. private $_finalbody = "";
  53. private $_alt_boundary = "";
  54. private $_atc_boundary = "";
  55. private $_header_str = "";
  56. private $_smtp_connect = "";
  57. private $_encoding = "8bit";
  58. private $_safe_mode = false;
  59. private $_IP = false;
  60. private $_smtp_auth = false;
  61. private $_replyto_flag = false;
  62. private $_debug_msg = array();
  63. private $_recipients = array();
  64. private $_cc_array = array();
  65. private $_bcc_array = array();
  66. private $_headers = array();
  67. private $_attach_name = array();
  68. private $_attach_type = array();
  69. private $_attach_disp = array();
  70. private $_protocols = array('mail', 'sendmail', 'smtp');
  71. private $_base_charsets = array('iso-8859-1', 'us-ascii');
  72. private $_bit_depths = array('7bit', '8bit');
  73. private $_priorities = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
  74. /**
  75. * Constructor - Sets Email Preferences
  76. *
  77. * The constructor can be passed an array of config values
  78. */
  79. public function __construct($config = array()) {
  80. if (count($config) > 0) {
  81. $this->initialize($config);
  82. }
  83. }
  84. /**
  85. * Initialize preferences
  86. *
  87. * @param array configs
  88. *
  89. * @return void
  90. */
  91. public function initialize($configs = array()) {
  92. $this->clear();
  93. foreach ($configs as $key => $val) {
  94. if (isset($this->$key)) {
  95. $method = 'set_'.$key;
  96. if (method_exists($this, $method)) {
  97. $this->$method($val);
  98. } else {
  99. $this->$key = $val;
  100. }
  101. }
  102. }
  103. $this->_smtp_auth = ($this->smtp_user == '' && $this->smtp_pass == '') ? false : true;
  104. $this->_safe_mode = (@ini_get("safe_mode") == 0) ? false : true;
  105. }
  106. /**
  107. * Initialize the Email Data
  108. *
  109. * @param boolean clear attachment
  110. *
  111. * @return void
  112. */
  113. public function clear($clear_attachments = false) {
  114. $this->_subject = "";
  115. $this->_body = "";
  116. $this->_finalbody = "";
  117. $this->_header_str = "";
  118. $this->_replyto_flag = false;
  119. $this->_recipients = array();
  120. $this->_headers = array();
  121. $this->_debug_msg = array();
  122. $this->_setHeader('User-Agent', $this->useragent);
  123. $this->_setHeader('Date', $this->_setDate());
  124. if ($clear_attachments !== false) {
  125. $this->_attach_name = array();
  126. $this->_attach_type = array();
  127. $this->_attach_disp = array();
  128. }
  129. }
  130. /**
  131. * Set FROM
  132. *
  133. * @param string
  134. * @param string
  135. *
  136. * @return void
  137. */
  138. public function from($from, $name = '') {
  139. if (preg_match( '/\<(.*)\>/', $from, $match))
  140. $from = $match['1'];
  141. if ($this->validate) {
  142. $this->validateEmail($this->_str2array($from));
  143. }
  144. if ($name != '' && substr($name, 0, 1) != '"') {
  145. $name = '"'.$name.'"';
  146. }
  147. $this->_setHeader('From', $name.' <'.$from.'>');
  148. $this->_setHeader('Return-Path', '<'.$from.'>');
  149. }
  150. /**
  151. * Set Reply-to
  152. *
  153. * @param string
  154. * @param string
  155. * @return void
  156. */
  157. public function replyTo($replyto, $name = '') {
  158. if (preg_match( '/\<(.*)\>/', $replyto, $match))
  159. $replyto = $match['1'];
  160. if ($this->validate)
  161. $this->validateEmail($this->_str2array($replyto));
  162. if ($name == '') {
  163. $name = $replyto;
  164. }
  165. if (substr($name, 0, 1) != '"') {
  166. $name = '"'.$name.'"';
  167. }
  168. $this->_setHeader('Reply-To', $name.' <'.$replyto.'>');
  169. $this->_replyto_flag = true;
  170. }
  171. /**
  172. * Set Recipients
  173. *
  174. * @param string
  175. *
  176. * @return void
  177. */
  178. public function to($to) {
  179. $to = $this->_str2array($to);
  180. $to = $this->cleanEmail($to);
  181. if ($this->validate) {
  182. $this->validateEmail($to);
  183. }
  184. if ($this->_getProtocol() != 'mail') {
  185. $this->_setHeader('To', implode(", ", $to));
  186. }
  187. switch ($this->_getProtocol()) {
  188. case 'smtp':
  189. $this->_recipients = $to;
  190. break;
  191. case 'sendmail':
  192. $this->_recipients = implode(", ", $to);
  193. break;
  194. case 'mail':
  195. $this->_recipients = implode(", ", $to);
  196. break;
  197. }
  198. }
  199. /**
  200. * Set CC
  201. *
  202. * @param string
  203. * @return void
  204. */
  205. function cc($cc) {
  206. $cc = $this->_str2array($cc);
  207. $cc = $this->cleanEmail($cc);
  208. if ($this->validate) {
  209. $this->validateEmail($cc);
  210. }
  211. $this->_setHeader('Cc', implode(", ", $cc));
  212. if ($this->_getProtocol() == "smtp") {
  213. $this->_cc_array = $cc;
  214. }
  215. }
  216. /**
  217. * Set BCC
  218. *
  219. * @param string bcc
  220. * @param string limit
  221. *
  222. * @return void
  223. */
  224. public function bcc($bcc, $limit = '') {
  225. if ($limit != '' && is_numeric($limit)) {
  226. $this->bcc_batch_mode = true;
  227. $this->bcc_batch_size = $limit;
  228. }
  229. $bcc = $this->_str2array($bcc);
  230. $bcc = $this->cleanEmail($bcc);
  231. if ($this->validate) {
  232. $this->validateEmail($bcc);
  233. }
  234. if (($this->_getProtocol() == "smtp") || ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size)) {
  235. $this->_bcc_array = $bcc;
  236. } else {
  237. $this->_setHeader('Bcc', implode(", ", $bcc));
  238. }
  239. }
  240. /**
  241. * Set Email Subject
  242. *
  243. * @param string subject
  244. *
  245. * @return void
  246. */
  247. public function subject($subject) {
  248. $subject = preg_replace("/(\r\n)|(\r)|(\n)/", "", $subject);
  249. $subject = preg_replace("/(\t)/", " ", $subject);
  250. $this->_setHeader('Subject', trim($subject));
  251. }
  252. /**
  253. * Set Body
  254. *
  255. * @param string
  256. * @return void
  257. */
  258. public function message($body) {
  259. $this->_body = stripslashes(rtrim(str_replace("\r", "", $body)));
  260. }
  261. /**
  262. * Assign file attachments
  263. *
  264. * @param string filename
  265. * @param string disposition
  266. *
  267. * @return void
  268. */
  269. public function attach($filename, $disposition = 'attachment') {
  270. $this->_attach_name[] = $filename;
  271. $this->_attach_type[] = $this->_mimeTypes(next(explode('.', basename($filename))));
  272. $this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters
  273. }
  274. /**
  275. * Add a Header Item
  276. *
  277. * @param string
  278. * @param string
  279. *
  280. * @return void
  281. */
  282. private function _setHeader($header, $value) {
  283. $this->_headers[$header] = $value;
  284. }
  285. /**
  286. * Convert a String to an Array
  287. *
  288. * @param string
  289. *
  290. * @return array
  291. */
  292. private function _str2array($email) {
  293. if ( ! is_array($email)) {
  294. if (ereg(',$', $email)) {
  295. $email = substr($email, 0, -1);
  296. }
  297. if (ereg('^,', $email)) {
  298. $email = substr($email, 1);
  299. }
  300. if (ereg(',', $email)) {
  301. $x = explode(',', $email);
  302. $email = array();
  303. for ($i = 0; $i < count($x); $i ++)
  304. $email[] = trim($x[$i]);
  305. } else {
  306. $email = trim($email);
  307. settype($email, "array");
  308. }
  309. }
  310. return $email;
  311. }
  312. /**
  313. * Set Multipart Value
  314. *
  315. * @param string
  316. *
  317. * @return void
  318. */
  319. public function setAltMessage($str = '') {
  320. $this->alt_message = ($str == '') ? '' : $str;
  321. }
  322. /**
  323. * Set Mailtype
  324. *
  325. * @param string
  326. *
  327. * @return void
  328. */
  329. public function setMailtype($type = 'text') {
  330. $this->mailtype = ($type == 'html') ? 'html' : 'text';
  331. }
  332. /**
  333. * Set Wordwrap
  334. *
  335. * @param string
  336. *
  337. * @return void
  338. */
  339. public function setWordwrap($wordwrap = true) {
  340. $this->wordwrap = ($wordwrap === false) ? false : true;
  341. }
  342. /**
  343. * Set Protocol
  344. *
  345. * @param string
  346. * @return void
  347. */
  348. function setProtocol($protocol = 'mail') {
  349. $this->protocol = ( ! in_array($protocol, $this->_protocols, true)) ? 'mail' : strtolower($protocol);
  350. }
  351. /**
  352. * Set Priority
  353. *
  354. * @param integer
  355. *
  356. * @return void
  357. */
  358. function setPriority($n = 3) {
  359. if ( ! is_numeric($n)) {
  360. $this->priority = 3;
  361. return;
  362. }
  363. if ($n < 1 || $n > 5) {
  364. $this->priority = 3;
  365. return;
  366. }
  367. $this->priority = $n;
  368. }
  369. /**
  370. * Set Newline Character
  371. *
  372. * @param string
  373. *
  374. * @return void
  375. */
  376. function setNewline($newline = "\n") {
  377. if ($newline != "\n" || $newline != "\r\n" || $newline != "\r") {
  378. $this->newline = "\n";
  379. return;
  380. }
  381. $this->newline = $newline;
  382. }
  383. /**
  384. * Set Message Boundary
  385. *
  386. * @return void
  387. */
  388. private function _setBoundaries() {
  389. $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
  390. $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
  391. }
  392. /**
  393. * Get the Message ID
  394. *
  395. * @return string
  396. */
  397. private function _getMessageId() {
  398. $from = $this->_headers['Return-Path'];
  399. $from = str_replace(">", "", $from);
  400. $from = str_replace("<", "", $from);
  401. return "<".uniqid('').strstr($from, '@').">";
  402. }
  403. /**
  404. * Get Mail Protocol
  405. *
  406. * @param bool
  407. * @return string
  408. */
  409. private function _getProtocol($return = true) {
  410. $this->protocol = strtolower($this->protocol);
  411. $this->protocol = ( ! in_array($this->protocol, $this->_protocols, true)) ? 'mail' : $this->protocol;
  412. if ($return == true) {
  413. return $this->protocol;
  414. }
  415. }
  416. /**
  417. * Get Mail Encoding
  418. *
  419. * @param bool
  420. * @return string
  421. */
  422. private function _getEncoding($return = true) {
  423. $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '7bit' : $this->_encoding;
  424. if ( ! in_array($this->charset, $this->_base_charsets, true)) {
  425. $this->_encoding = "8bit";
  426. }
  427. if ($return == true) {
  428. return $this->_encoding;
  429. }
  430. }
  431. /**
  432. * Get content type (text/html/attachment)
  433. *
  434. * @return string
  435. */
  436. private function _getContentType() {
  437. if ($this->mailtype == 'html' && count($this->_attach_name) == 0) {
  438. return 'html';
  439. } else if ($this->mailtype == 'html' && count($this->_attach_name) > 0) {
  440. return 'html-attach';
  441. } else if ($this->mailtype == 'text' && count($this->_attach_name) > 0) {
  442. return 'plain-attach';
  443. } else {
  444. return 'plain';
  445. }
  446. }
  447. /**
  448. * Set RFC 822 Date
  449. *
  450. * @return string
  451. */
  452. private function _setDate() {
  453. $timezone = date("Z");
  454. $operator = (substr($timezone, 0, 1) == '-') ? '-' : '+';
  455. $timezone = abs($timezone);
  456. $timezone = ($timezone/3600) * 100 + ($timezone % 3600) /60;
  457. return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
  458. }
  459. /**
  460. * Mime message
  461. *
  462. * @return string
  463. */
  464. private function _getMimeMessage() {
  465. return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
  466. }
  467. /**
  468. * Validate Email Address
  469. *
  470. * @param string
  471. * @return bool
  472. */
  473. public function validateEmail($email) {
  474. if ( ! is_array($email)) {
  475. $email = array($email);
  476. }
  477. foreach ($email as $val) {
  478. if ( ! $this->validEmail($val)) {
  479. log_error('Email address invalid: "'.$val.'"');
  480. return false;
  481. }
  482. }
  483. }
  484. /**
  485. * Email Validation
  486. *
  487. * @param string
  488. * @return bool
  489. */
  490. function validEmail($address) {
  491. return (bool) preg_match(EMAIL_FORMAT, $address);
  492. }
  493. /**
  494. * Clean Extended Email Address: Joe Smith <joe@smith.com>
  495. *
  496. * @param string
  497. * @return string
  498. */
  499. function cleanEmail($email) {
  500. if ( ! is_array($email)) {
  501. if (preg_match('/\<(.*)\>/', $email, $match))
  502. return $match['1'];
  503. else
  504. return $email;
  505. }
  506. $clean_email = array();
  507. for ($i=0; $i < count($email); $i++) {
  508. if (preg_match( '/\<(.*)\>/', $email[$i], $match))
  509. $clean_email[] = $match['1'];
  510. else
  511. $clean_email[] = $email[$i];
  512. }
  513. return $clean_email;
  514. }
  515. /**
  516. * Build alternative plain text message
  517. *
  518. * This function provides the raw message for use
  519. * in plain-text headers of HTML-formatted emails.
  520. * If the user hasn't specified his own alternative message
  521. * it creates one by stripping the HTML
  522. *
  523. * @return string
  524. */
  525. private function _getAltMessage() {
  526. if ($this->alt_message != "") {
  527. return $this->_wordwrap($this->alt_message, '76');
  528. }
  529. if (eregi( '\<body(.*)\</body\>', $this->_body, $match)) {
  530. $body = $match['1'];
  531. $body = substr($body, strpos($body, ">") + 1);
  532. } else {
  533. $body = $this->_body;
  534. }
  535. $body = trim(strip_tags($body));
  536. $body = preg_replace( '#<!--(.*)--\>#', "", $body);
  537. $body = str_replace("\t", "", $body);
  538. for ($i = 20; $i >= 3; $i--) {
  539. $n = "";
  540. for ($x = 1; $x <= $i; $x ++) {
  541. $n .= "\n";
  542. }
  543. $body = str_replace($n, "\n\n", $body);
  544. }
  545. return $this->_wordwrap($body, '76');
  546. }
  547. /**
  548. * Word Wrap
  549. *
  550. * @param string
  551. * @param integer
  552. * @return string
  553. */
  554. private function _wordwrap($str, $charlim = '') {
  555. // Se the character limit
  556. if ($charlim == '') {
  557. $charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;
  558. }
  559. // Reduce multiple spaces
  560. $str = preg_replace("| +|", " ", $str);
  561. // Standardize newlines
  562. $str = preg_replace("/\r\n|\r/", "\n", $str);
  563. // If the current word is surrounded by {unwrap} tags we'll
  564. // strip the entire chunk and replace it with a marker.
  565. $unwrap = array();
  566. if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches)) {
  567. for ($i = 0; $i < count($matches['0']); $i++) {
  568. $unwrap[] = $matches['1'][$i];
  569. $str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
  570. }
  571. }
  572. // Use PHP's native function to do the initial wordwrap.
  573. // We set the cut flag to false so that any individual words that are
  574. // too long get left alone. In the next step we'll deal with them.
  575. $str = wordwrap($str, $charlim, "\n", false);
  576. // Split the string into individual lines of text and cycle through them
  577. $output = "";
  578. foreach (explode("\n", $str) as $line) {
  579. // Is the line within the allowed character count?
  580. // If so we'll join it to the output and continue
  581. if (strlen($line) <= $charlim) {
  582. $output .= $line.$this->newline;
  583. continue;
  584. }
  585. $temp = '';
  586. while((strlen($line)) > $charlim) {
  587. // If the over-length word is a URL we won't wrap it
  588. if (preg_match("!\[url.+\]|://|wwww.!", $line)) {
  589. break;
  590. }
  591. // Trim the word down
  592. $temp .= substr($line, 0, $charlim-1);
  593. $line = substr($line, $charlim-1);
  594. }
  595. // If $temp contains data it means we had to split up an over-length
  596. // word into smaller chunks so we'll add it back to our current line
  597. if ($temp != '') {
  598. $output .= $temp.$this->newline.$line;
  599. } else {
  600. $output .= $line;
  601. }
  602. $output .= $this->newline;
  603. }
  604. // Put our markers back
  605. if (count($unwrap) > 0) {
  606. foreach ($unwrap as $key => $val) {
  607. $output = str_replace("{{unwrapped".$key."}}", $val, $output);
  608. }
  609. }
  610. return $output;
  611. }
  612. /**
  613. * Build final headers
  614. *
  615. * @param string
  616. * @return string
  617. */
  618. private function _buildHeaders() {
  619. $this->_setHeader('X-Sender', $this->cleanEmail($this->_headers['From']));
  620. $this->_setHeader('X-Mailer', $this->useragent);
  621. $this->_setHeader('X-Priority', $this->_priorities[$this->priority - 1]);
  622. $this->_setHeader('Message-ID', $this->_getMessageId());
  623. $this->_setHeader('Mime-Version', '1.0');
  624. }
  625. /**
  626. * Write Headers as a string
  627. *
  628. * @return void
  629. */
  630. private function _writeHeaders() {
  631. if ($this->protocol == 'mail') {
  632. $this->_subject = $this->_headers['Subject'];
  633. unset($this->_headers['Subject']);
  634. }
  635. reset($this->_headers);
  636. $this->_header_str = "";
  637. foreach($this->_headers as $key => $val) {
  638. $val = trim($val);
  639. if ($val != "") {
  640. $this->_header_str .= $key.": ".$val.$this->newline;
  641. }
  642. }
  643. if ($this->_getProtocol() == 'mail') {
  644. $this->_header_str = substr($this->_header_str, 0, -1);
  645. }
  646. }
  647. /**
  648. * Build Final Body and attachments
  649. *
  650. * @return void
  651. */
  652. private function _buildMessage() {
  653. if ($this->wordwrap === true && $this->mailtype != 'html') {
  654. $this->_body = $this->_wordwrap($this->_body);
  655. }
  656. $this->_setBoundaries();
  657. $this->_writeHeaders();
  658. $hdr = ($this->_getProtocol() == 'mail') ? $this->newline : '';
  659. switch ($this->_getContentType()) {
  660. case 'plain':
  661. $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
  662. $hdr .= "Content-Transfer-Encoding: " . $this->_getEncoding();
  663. if ($this->_getProtocol() == 'mail') {
  664. $this->_header_str .= $hdr;
  665. $this->_finalbody = $this->_body;
  666. return;
  667. }
  668. $hdr .= $this->newline . $this->newline . $this->_body;
  669. $this->_finalbody = $hdr;
  670. return;
  671. break;
  672. case 'html' :
  673. $hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline;
  674. $hdr .= $this->_getMimeMessage() . $this->newline . $this->newline;
  675. $hdr .= "--" . $this->_alt_boundary . $this->newline;
  676. $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
  677. $hdr .= "Content-Transfer-Encoding: " . $this->_getEncoding() . $this->newline . $this->newline;
  678. $hdr .= $this->_getAltMessage() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
  679. $hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
  680. $hdr .= "Content-Transfer-Encoding: quoted/printable";
  681. if ($this->_getProtocol() == 'mail') {
  682. $this->_header_str .= $hdr;
  683. $this->_finalbody = $this->_body . $this->newline . $this->newline . "--" . $this->_alt_boundary . "--";
  684. return;
  685. }
  686. $hdr .= $this->newline . $this->newline;
  687. $hdr .= $this->_body . $this->newline . $this->newline . "--" . $this->_alt_boundary . "--";
  688. $this->_finalbody = $hdr;
  689. return;
  690. break;
  691. case 'plain-attach' :
  692. $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline;
  693. $hdr .= $this->_getMimeMessage() . $this->newline . $this->newline;
  694. $hdr .= "--" . $this->_atc_boundary . $this->newline;
  695. $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
  696. $hdr .= "Content-Transfer-Encoding: " . $this->_getEncoding();
  697. if ($this->_getProtocol() == 'mail') {
  698. $this->_header_str .= $hdr;
  699. $body = $this->_body . $this->newline . $this->newline;
  700. }
  701. $hdr .= $this->newline . $this->newline;
  702. $hdr .= $this->_body . $this->newline . $this->newline;
  703. break;
  704. case 'html-attach' :
  705. $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline;
  706. $hdr .= $this->_getMimeMessage() . $this->newline . $this->newline;
  707. $hdr .= "--" . $this->_atc_boundary . $this->newline;
  708. $hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;
  709. $hdr .= "--" . $this->_alt_boundary . $this->newline;
  710. $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
  711. $hdr .= "Content-Transfer-Encoding: " . $this->_getEncoding() . $this->newline . $this->newline;
  712. $hdr .= $this->_getAltMessage() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
  713. $hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
  714. $hdr .= "Content-Transfer-Encoding: quoted/printable";
  715. if ($this->_getProtocol() == 'mail') {
  716. $this->_header_str .= $hdr;
  717. $body = $this->_body . $this->newline . $this->newline;
  718. $body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
  719. }
  720. $hdr .= $this->newline . $this->newline;
  721. $hdr .= $this->_body . $this->newline . $this->newline;
  722. $hdr .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
  723. break;
  724. }
  725. $attachment = array();
  726. $z = 0;
  727. for ($i=0; $i < count($this->_attach_name); $i++) {
  728. $filename = $this->_attach_name[$i];
  729. $basename = basename($filename);
  730. $ctype = $this->_attach_type[$i];
  731. if ( ! file_exists($filename)) {
  732. return;
  733. }
  734. $h = "--".$this->_atc_boundary.$this->newline;
  735. $h .= "Content-type: ".$ctype."; ";
  736. $h .= "name=\"".$basename."\"".$this->newline;
  737. $h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;
  738. $h .= "Content-Transfer-Encoding: base64".$this->newline;
  739. $attachment[$z++] = $h;
  740. $file = filesize($filename) +1;
  741. if ( ! $fp = fopen($filename, 'r')) {
  742. return;
  743. }
  744. $attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));
  745. fclose($fp);
  746. }
  747. if ($this->_getProtocol() == 'mail') {
  748. $this->_finalbody = $body . implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
  749. return;
  750. }
  751. $this->_finalbody = $hdr.implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
  752. }
  753. /**
  754. * Send Email
  755. *
  756. * @return bool
  757. */
  758. function send() {
  759. if ($this->_replyto_flag == false) {
  760. $this->replyTo($this->_headers['From']);
  761. }
  762. if (( ! isset($this->_recipients) && ! isset($this->_headers['To'])) &&
  763. ( ! isset($this->_bcc_array) && ! isset($this->_headers['Bcc'])) &&
  764. ( ! isset($this->_headers['Cc']))) {
  765. return false;
  766. }
  767. $this->_buildHeaders();
  768. if ($this->bcc_batch_mode && count($this->_bcc_array) > 0) {
  769. if (count($this->_bcc_array) > $this->bcc_batch_size)
  770. return $this->batchBccSend();
  771. }
  772. $this->_buildMessage();
  773. if ( ! $this->_spoolEmail())
  774. return false;
  775. else
  776. return true;
  777. }
  778. /**
  779. * Batch Bcc Send. Sends groups of BCCs in batches
  780. *
  781. * @access public
  782. * @return bool
  783. */
  784. function batchBccSend() {
  785. $float = $this->bcc_batch_size -1;
  786. $flag = 0;
  787. $set = "";
  788. $chunk = array();
  789. for ($i = 0; $i < count($this->_bcc_array); $i++) {
  790. if (isset($this->_bcc_array[$i]))
  791. $set .= ", ".$this->_bcc_array[$i];
  792. if ($i == $float) {
  793. $chunk[] = substr($set, 1);
  794. $float = $float + $this->bcc_batch_size;
  795. $set = "";
  796. }
  797. if ($i == count($this->_bcc_array)-1)
  798. $chunk[] = substr($set, 1);
  799. }
  800. for ($i = 0; $i < count($chunk); $i++) {
  801. unset($this->_headers['Bcc']);
  802. unset($bcc);
  803. $bcc = $this->_str2array($chunk[$i]);
  804. $bcc = $this->cleanEmail($bcc);
  805. if ($this->protocol != 'smtp')
  806. $this->_setHeader('Bcc', implode(", ", $bcc));
  807. else
  808. $this->_bcc_array = $bcc;
  809. $this->_buildMessage();
  810. $this->_spoolEmail();
  811. }
  812. }
  813. /**
  814. * Unwrap special elements
  815. *
  816. * @access private
  817. * @return void
  818. */
  819. function _unwrapSpecials() {
  820. $this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_removeNlCallback'), $this->_finalbody);
  821. }
  822. /**
  823. * Strip line-breaks via callback
  824. *
  825. * @access private
  826. * @return string
  827. */
  828. function _removeNlCallback($matches) {
  829. return preg_replace("/(\r\n)|(\r)|(\n)/", "", $matches['1']);
  830. }
  831. /**
  832. * Spool mail to the mail server
  833. *
  834. * @return bool
  835. */
  836. private function _spoolEmail() {
  837. $this->_unwrapSpecials();
  838. switch ($this->_getProtocol()) {
  839. case 'mail':
  840. if ( ! $this->_sendWithMail()) {
  841. return false;
  842. }
  843. break;
  844. case 'sendmail':
  845. if ( ! $this->_sendWithSendmail()) {
  846. return false;
  847. }
  848. break;
  849. case 'smtp':
  850. if ( ! $this->_sendWithSmtp()) {
  851. return false;
  852. }
  853. break;
  854. }
  855. return true;
  856. }
  857. /**
  858. * Send using mail()
  859. *
  860. * @return bool
  861. */
  862. private function _sendWithMail() {
  863. if ($this->_safe_mode == true) {
  864. if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
  865. return false;
  866. else
  867. return true;
  868. } else {
  869. if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f".$this->cleanEmail($this->_headers['From'])))
  870. return false;
  871. else
  872. return true;
  873. }
  874. }
  875. /**
  876. * Send using Sendmail
  877. *
  878. * @return bool
  879. */
  880. private function _sendWithSendmail() {
  881. $fp = @popen($this->mailpath . " -oi -f ".$this->cleanEmail($this->_headers['From'])." -t", 'w');
  882. if ( ! is_resource($fp)) {
  883. return false;
  884. }
  885. fputs($fp, $this->_header_str);
  886. fputs($fp, $this->_finalbody);
  887. pclose($fp) >> 8 & 0xFF;
  888. return true;
  889. }
  890. /**
  891. * Send using SMTP
  892. *
  893. * @return bool
  894. */
  895. private function _sendWithSmtp() {
  896. if ($this->smtp_host == '') {
  897. return false;
  898. }
  899. $this->_smtpConnect();
  900. $this->_smtpAuthenticate();
  901. $this->_sendCommand('from', $this->cleanEmail($this->_headers['From']));
  902. foreach($this->_recipients as $val)
  903. $this->_sendCommand('to', $val);
  904. if (count($this->_cc_array) > 0) {
  905. foreach($this->_cc_array as $val) {
  906. if ($val != "")
  907. $this->_sendCommand('to', $val);
  908. }
  909. }
  910. if (count($this->_bcc_array) > 0) {
  911. foreach($this->_bcc_array as $val) {
  912. if ($val != "")
  913. $this->_sendCommand('to', $val);
  914. }
  915. }
  916. $this->_sendCommand('data');
  917. $this->_sendData($this->_header_str . $this->_finalbody);
  918. $this->_send_data('.');
  919. $reply = $this->_getSmtpData();
  920. log_error($reply);
  921. if (substr($reply, 0, 3) != '250') {
  922. return false;
  923. }
  924. $this->_sendCommand('quit');
  925. return true;
  926. }
  927. /**
  928. * SMTP Connect
  929. *
  930. * @param string
  931. * @return string
  932. */
  933. private function _smtpConnect() {
  934. $this->_smtp_connect = fsockopen($this->smtp_host,
  935. $this->smtp_port,
  936. $errno,
  937. $errstr,
  938. $this->smtp_timeout);
  939. if( ! is_resource($this->_smtp_connect)) {
  940. return false;
  941. }
  942. return $this->_sendCommand('hello');
  943. }
  944. /**
  945. * Send SMTP command
  946. *
  947. * @param string
  948. * @param string
  949. * @return string
  950. */
  951. private function _sendCommand($cmd, $data = '') {
  952. switch ($cmd) {
  953. case 'hello':
  954. if ($this->_smtp_auth || $this->_getEncoding() == '8bit')
  955. $this->_sendData('EHLO '.$this->_getHostname());
  956. else
  957. $this->_sendData('HELO '.$this->_getHostname());
  958. $resp = 250;
  959. break;
  960. case 'from':
  961. $this->_sendData('MAIL FROM:<'.$data.'>');
  962. $resp = 250;
  963. break;
  964. case 'to':
  965. $this->_sendData('RCPT TO:<'.$data.'>');
  966. $resp = 250;
  967. break;
  968. case 'data' :
  969. $this->_sendData('DATA');
  970. $resp = 354;
  971. break;
  972. case 'quit' :
  973. $this->_sendData('QUIT');
  974. $resp = 221;
  975. break;
  976. }
  977. $reply = $this->_getSmtpData();
  978. $this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";
  979. if (substr($reply, 0, 3) != $resp) {
  980. return false;
  981. }
  982. if ($cmd == 'quit')
  983. fclose($this->_smtp_connect);
  984. return true;
  985. }
  986. /**
  987. * SMTP Authenticate
  988. *
  989. * @return bool
  990. */
  991. private function _smtpAuthenticate() {
  992. if ( ! $this->_smtp_auth)
  993. return true;
  994. if ($this->smtp_user == "" && $this->smtp_pass == "") {
  995. return false;
  996. }
  997. $this->_sendData('AUTH LOGIN');
  998. $reply = $this->_getSmtpData();
  999. if (substr($reply, 0, 3) != '334') {
  1000. return false;
  1001. }
  1002. $this->_sendData(base64_encode($this->smtp_user));
  1003. $reply = $this->_getSmtpData();
  1004. if (substr($reply, 0, 3) != '334') {
  1005. return false;
  1006. }
  1007. $this->_sendData(base64_encode($this->smtp_pass));
  1008. $reply = $this->_getSmtpData();
  1009. if (substr($reply, 0, 3) != '235') {
  1010. return false;
  1011. }
  1012. return true;
  1013. }
  1014. /**
  1015. * Send SMTP data
  1016. *
  1017. * @return bool
  1018. */
  1019. private function _sendData($data) {
  1020. if ( ! fwrite($this->_smtp_connect, $data . $this->newline)) {
  1021. return false;
  1022. } else
  1023. return true;
  1024. }
  1025. /**
  1026. * Get SMTP data
  1027. *
  1028. * @return string
  1029. */
  1030. private function _getSmtpData() {
  1031. $data = "";
  1032. while ($str = fgets($this->_smtp_connect, 512)) {
  1033. $data .= $str;
  1034. if (substr($str, 3, 1) == " ")
  1035. break;
  1036. }
  1037. return $data;
  1038. }
  1039. /**
  1040. * Get Hostname
  1041. *
  1042. * @return string
  1043. */
  1044. private function _getHostname() {
  1045. return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
  1046. }
  1047. /**
  1048. * Get IP
  1049. *
  1050. * @return string
  1051. */
  1052. private function _getIp() {
  1053. if ($this->_IP !== false) {
  1054. return $this->_IP;
  1055. }
  1056. $cip = (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : false;
  1057. $rip = (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : false;
  1058. $fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : false;
  1059. if ($cip && $rip) $this->_IP = $cip;
  1060. elseif ($rip) $this->_IP = $rip;
  1061. elseif ($cip) $this->_IP = $cip;
  1062. elseif ($fip) $this->_IP = $fip;
  1063. if (strstr($this->_IP, ',')) {
  1064. $x = explode(',', $this->_IP);
  1065. $this->_IP = end($x);
  1066. }
  1067. if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
  1068. $this->_IP = '0.0.0.0';
  1069. unset($cip);
  1070. unset($rip);
  1071. unset($fip);
  1072. return $this->_IP;
  1073. }
  1074. /**
  1075. * Get Debug Message
  1076. *
  1077. * @return string
  1078. */
  1079. function printDebugger() {
  1080. $msg = '';
  1081. if (count($this->_debug_msg) > 0) {
  1082. foreach ($this->_debug_msg as $val) {
  1083. $msg .= $val;
  1084. }
  1085. }
  1086. $msg .= "<pre>".$this->_header_str."\n".$this->_subject."\n".$this->_finalbody.'</pre>';
  1087. return $msg;
  1088. }
  1089. /**
  1090. * Mime Types
  1091. *
  1092. * @param string
  1093. * @return string
  1094. */
  1095. private function _mimeTypes($ext = "") {
  1096. $mimes = array( 'hqx' => 'application/mac-binhex40',
  1097. 'cpt' => 'application/mac-compactpro',
  1098. 'doc' => 'application/msword',
  1099. 'bin' => 'application/macbinary',
  1100. 'dms' => 'application/octet-stream',
  1101. 'lha' => 'application/octet-stream',
  1102. 'lzh' => 'application/octet-stream',
  1103. 'exe' => 'application/octet-stream',
  1104. 'class' => 'application/octet-stream',
  1105. 'psd' => 'application/octet-stream',
  1106. 'so' => 'application/octet-stream',
  1107. 'sea' => 'application/octet-stream',
  1108. 'dll' => 'application/octet-stream',
  1109. 'oda' => 'application/oda',
  1110. 'pdf' => 'application/pdf',
  1111. 'ai' => 'application/postscript',
  1112. 'eps' => 'application/postscript',
  1113. 'ps' => 'application/postscript',
  1114. 'smi' => 'application/smil',
  1115. 'smil' => 'application/smil',
  1116. 'mif' => 'application/vnd.mif',
  1117. 'xls' => 'application/vnd.ms-excel',
  1118. 'ppt' => 'application/vnd.ms-powerpoint',
  1119. 'wbxml' => 'application/vnd.wap.wbxml',
  1120. 'wmlc' => 'application/vnd.wap.wmlc',
  1121. 'dcr' => 'application/x-director',
  1122. 'dir' => 'application/x-director',
  1123. 'dxr' => 'application/x-director',
  1124. 'dvi' => 'application/x-dvi',
  1125. 'gtar' => 'application/x-gtar',
  1126. 'php' => 'application/x-httpd-php',
  1127. 'php4' => 'application/x-httpd-php',
  1128. 'php3' => 'application/x-httpd-php',
  1129. 'phtml' => 'application/x-httpd-php',
  1130. 'phps' => 'application/x-httpd-php-source',
  1131. 'js' => 'application/x-javascript',
  1132. 'swf' => 'application/x-shockwave-flash',
  1133. 'sit' => 'application/x-stuffit',
  1134. 'tar' => 'application/x-tar',
  1135. 'tgz' => 'application/x-tar',
  1136. 'xhtml' => 'application/xhtml+xml',
  1137. 'xht' => 'application/xhtml+xml',
  1138. 'zip' => 'application/zip',
  1139. 'mid' => 'audio/midi',
  1140. 'midi' => 'audio/midi',
  1141. 'mpga' => 'audio/mpeg',
  1142. 'mp2' => 'audio/mpeg',
  1143. 'mp3' => 'audio/mpeg',
  1144. 'aif' => 'audio/x-aiff',
  1145. 'aiff' => 'audio/x-aiff',
  1146. 'aifc' => 'audio/x-aiff',
  1147. 'ram' => 'audio/x-pn-realaudio',
  1148. 'rm' => 'audio/x-pn-realaudio',
  1149. 'rpm' => 'audio/x-pn-realaudio-plugin',
  1150. 'ra' => 'audio/x-realaudio',
  1151. 'rv' => 'video/vnd.rn-realvideo',
  1152. 'wav' => 'audio/x-wav',
  1153. 'bmp' => 'image/bmp',
  1154. 'gif' => 'image/gif',
  1155. 'jpeg' => 'image/jpeg',
  1156. 'jpg' => 'image/jpeg',
  1157. 'jpe' => 'image/jpeg',
  1158. 'png' => 'image/png',
  1159. 'tiff' => 'image/tiff',
  1160. 'tif' => 'image/tiff',
  1161. 'css' => 'text/css',
  1162. 'html' => 'text/html',
  1163. 'htm' => 'text/html',
  1164. 'shtml' => 'text/html',
  1165. 'txt' => 'text/plain',
  1166. 'text' => 'text/plain',
  1167. 'log' => 'text/plain',
  1168. 'rtx' => 'text/richtext',
  1169. 'rtf' => 'text/rtf',
  1170. 'xml' => 'text/xml',
  1171. 'xsl' => 'text/xml',
  1172. 'mpeg' => 'video/mpeg',
  1173. 'mpg' => 'video/mpeg',
  1174. 'mpe' => 'video/mpeg',
  1175. 'qt' => 'video/quicktime',
  1176. 'mov' => 'video/quicktime',
  1177. 'avi' => 'video/x-msvideo',
  1178. 'movie' => 'video/x-sgi-movie',
  1179. 'doc' => 'application/msword',
  1180. 'word' => 'application/msword',
  1181. 'xl' => 'application/excel',
  1182. 'eml' => 'message/rfc822'
  1183. );
  1184. return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
  1185. }
  1186. } // End Email class