vlibMimeMail.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2002 Active Fish Group |
  7. // +----------------------------------------------------------------------+
  8. // | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk> |
  9. // +----------------------------------------------------------------------+
  10. //
  11. // $Id: vlibMimeMail.php 1438 2006-10-29 13:26:42Z b4rt $
  12. // check to avoid multiple including of class
  13. if (!defined('vlibMimeMailClassLoaded')) {
  14. define('vlibMimeMailClassLoaded', 1);
  15. // get the functionality of the vlibCommon class.
  16. include_once(dirname(__FILE__).'/vlibCommon/common.php');
  17. // get error reporting functionality.
  18. include_once(dirname(__FILE__).'/vlibMimeMail/error.php');
  19. /**
  20. * vlibMimeMail Class is used send mime encoded mail messages.
  21. * For instructions on how to use vlibMimeMail, see the
  22. * vlibMimeMail.html file, located in the 'docs' directory.
  23. *
  24. * @since 22/04/2002
  25. * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
  26. * @package vLIB
  27. * @access public
  28. * @see vlibMimeMail.html
  29. */
  30. class vlibMimeMail {
  31. /*-----------------------------------------------------------------------------\
  32. | ATTENTION |
  33. | Do not touch the following variables. vlibTemplate will not work otherwise. |
  34. \-----------------------------------------------------------------------------*/
  35. /** lists To addresses */
  36. var $sendto = array();
  37. /** lists Cc addresses */
  38. var $sendcc = array();
  39. /** lists Bcc addresses */
  40. var $sendbcc = array();
  41. /** paths of attached files */
  42. var $attachments = array();
  43. /** attachment mime-types */
  44. var $mimetypes = array();
  45. /** attachment dispositions */
  46. var $dispositions = array();
  47. /** attachment content IDs */
  48. var $contentIDs = array();
  49. /** list of message headers */
  50. var $xheaders = array();
  51. /** message priorities */
  52. var $priorities = array( '1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)' );
  53. /** character set of plain text message */
  54. var $charset = "us-ascii";
  55. var $ctencoding = "7bit";
  56. /** character set of html message */
  57. var $htmlcharset = "us-ascii";
  58. var $htmlctencoding = "7bit";
  59. /** Request receipt?? */
  60. var $receipt = 0;
  61. /** Do we need to encode when we send?? */
  62. var $doencoding = 0;
  63. /** whether to try and fix the windows bug fix */
  64. var $apply_windows_bugfix = false;
  65. var $all_emails = array(); // if above is true, will contain all addies
  66. /** whether to verify all email addresses (by regex) */
  67. var $checkAddress = true;
  68. /*-----------------------------------------------------------------------------\
  69. | public functions |
  70. \-----------------------------------------------------------------------------*/
  71. /**
  72. * FUNCTION: autoCheck
  73. *
  74. * Sets auto checking to the value of $bool.
  75. *
  76. * @param bool $bool true/false
  77. * @access public
  78. */
  79. function autoCheck ($bool) {
  80. $this->checkAddress = ($bool);
  81. }
  82. /**
  83. * FUNCTION: to
  84. *
  85. * Sets the To header for the message.
  86. *
  87. * @param string $toEmail email address
  88. * @param string $toName Name of recipient [optional]
  89. * @access public
  90. */
  91. function to ($toEmail, $toName=null) {
  92. if (!$toEmail) return false;
  93. if ($this->checkAddress && !$this->validateEmail($toEmail)) vlibMimeMailError::raiseError('VM_ERROR_BADEMAIL', FATAL, 'To: '.$toEmail);
  94. if ($this->apply_windows_bugfix) array_push($this->all_emails, $toEmail);
  95. $this->sendto[] = ($toName == null) ? $toEmail : '"'.$toName.'" <'.$toEmail.'>';
  96. }
  97. /**
  98. * FUNCTION: clearTo
  99. *
  100. * Clears all to headers set.
  101. *
  102. * @access public
  103. */
  104. function clearTo () {
  105. $this->sendto = array();
  106. $this->all_emails = array();
  107. }
  108. /**
  109. * FUNCTION: cc
  110. *
  111. * Sets the Cc header for the message.
  112. *
  113. * @param string $ccEmail email address
  114. * @param string $ccName Name of recipient [optional]
  115. * @access public
  116. */
  117. function cc ($ccEmail, $ccName=null) {
  118. if (!$ccEmail) return false;
  119. if ($this->checkAddress && !$this->validateEmail($ccEmail)) vlibMimeMailError::raiseError('VM_ERROR_BADEMAIL', FATAL, 'Cc: '.$ccEmail);
  120. if ($this->apply_windows_bugfix) array_push($this->all_emails, $ccEmail);
  121. $this->sendcc[] = ($ccName == null) ? $ccEmail : '"'.$ccName.'" <'.$ccEmail.'>';
  122. }
  123. /**
  124. * FUNCTION: clearCc
  125. *
  126. * Clears all cc headers set.
  127. *
  128. * @access public
  129. */
  130. function clearCc () {
  131. $this->sendcc = array();
  132. $this->all_emails = array();
  133. }
  134. /**
  135. * FUNCTION: bcc
  136. *
  137. * Sets the Bcc header for the message.
  138. *
  139. * @param string $bccEmail email address
  140. * @param string $bccName Name of recipient [optional]
  141. * @access public
  142. */
  143. function bcc ($bccEmail, $bccName=null) {
  144. if (!$bccEmail) return false;
  145. if ($this->checkAddress && !$this->validateEmail($bccEmail)) vlibMimeMailError::raiseError('VM_ERROR_BADEMAIL', FATAL, 'Bcc: '.$bccEmail);
  146. if ($this->apply_windows_bugfix) array_push($this->all_emails, $bccEmail);
  147. $this->sendbcc[] = ($bccName == null) ? $bccEmail : '"'.$bccName.'" <'.$bccEmail.'>';
  148. }
  149. /**
  150. * FUNCTION: clearBcc
  151. *
  152. * Clears all bcc headers set.
  153. *
  154. * @access public
  155. */
  156. function clearBcc () {
  157. $this->sendbcc = array();
  158. $this->all_emails = array();
  159. }
  160. /**
  161. * FUNCTION: clearAll
  162. *
  163. * Clears all To, Bcc and Cc headers set.
  164. *
  165. * @access public
  166. */
  167. function clearAll () {
  168. $this->clearTo();
  169. $this->clearCc();
  170. $this->clearBcc();
  171. $this->all_emails = array();
  172. }
  173. /**
  174. * FUNCTION: from
  175. *
  176. * Sets the From header for the message.
  177. *
  178. * @param string $fromEmail email address
  179. * @param string $fromName Name of recipient [optional]
  180. * @access public
  181. */
  182. function from ($fromEmail, $fromName=null) {
  183. if (!$fromEmail) return false;
  184. if ($this->checkAddress && !$this->validateEmail($fromEmail)) vlibMimeMailError::raiseError('VM_ERROR_BADEMAIL', FATAL, 'From: '.$fromEmail);
  185. $this->xheaders['From'] = ($fromName == null) ? $fromEmail : '"'.$fromName.'" <'.$fromEmail.'>';
  186. }
  187. /**
  188. * FUNCTION: replyTo
  189. *
  190. * Sets the ReplyTo header for the message.
  191. *
  192. * @param string $replytoEmail email address
  193. * @param string $replytoName Name of recipient [optional]
  194. * @access public
  195. */
  196. function replyTo ($replytoEmail, $replytoName=null) {
  197. if (!$replytoEmail) return false;
  198. if ($this->checkAddress && !$this->validateEmail($replytoEmail)) vlibMimeMailError::raiseError('VM_ERROR_BADEMAIL', FATAL, 'Reply-To: '.$replytoEmail);
  199. $this->xheaders['Reply-To'] = ($replytoName == null) ? $replytoEmail : '"'.$replytoName.'" <'.$replytoEmail.'>';
  200. }
  201. /**
  202. * FUNCTION: subject
  203. *
  204. * Sets the subject for this message.
  205. *
  206. * @param string $subject
  207. * @access public
  208. */
  209. function subject ($subject) {
  210. $this->xheaders['Subject'] = strtr($subject, "\r\n", ' ');
  211. }
  212. /**
  213. * FUNCTION: body
  214. *
  215. * Sets the Body of the message.
  216. * If you're sending a mail with special characters, be sure to define the
  217. * charset.
  218. * i.e. $mail->body('ce message est en français.', 'iso-8859-1');
  219. *
  220. * @param string $body plain text as the body
  221. * @param string $charset
  222. * @access public
  223. */
  224. function body ($body, $charset='') {
  225. $this->body = $body;
  226. if($charset != '') {
  227. $this->charset = strtolower($charset);
  228. if($this->charset != 'us-ascii') $this->ctencoding = '8bit';
  229. }
  230. }
  231. /**
  232. * FUNCTION: htmlBody
  233. *
  234. * Sets the HTML Body of the message.
  235. * You can you the body() function or htmlBody() or both just to be certain that
  236. * the user will be able to see the stuff you're sending.
  237. *
  238. * If you're sending a mail with special characters, be sure to define the
  239. * charset.
  240. * i.e. $mail->htmlbody('ce message est en français.', 'iso-8859-1');
  241. *
  242. * @param string $htmlbody html text as the body
  243. * @param string $charset
  244. * @access public
  245. */
  246. function htmlBody ($htmlbody, $charset='') {
  247. $this->htmlbody = $htmlbody;
  248. if($charset != '') {
  249. $this->htmlcharset = strtolower($charset);
  250. if($this->htmlcharset != 'us-ascii') $this->htmlctencoding = '8bit';
  251. }
  252. }
  253. /**
  254. * FUNCTION: attach
  255. *
  256. * Attach a file to the message. Defaults the disposition to 'attachment',
  257. * you can also use 'inline' which the client will try to show in the message.
  258. * Mime-types can be handled by vlibMimeMail by the list found in
  259. * vlibCommon/mime_types.php.
  260. *
  261. * @param string $filename full path of the file to attach
  262. * @param string $disposition inline or attachment
  263. * @param string $mimetype MIME-type of the file. defaults to 'application/octet-stream'
  264. * @access public
  265. */
  266. function attach ($filename, $disposition = 'attachment', $mimetype=null, $cid=null) {
  267. if($mimetype == null) $mimetype = vlibCommon::getMimeType($filename);
  268. $this->attachments[] = $filename;
  269. $this->mimetypes[] = $mimetype;
  270. $this->dispositions[] = $disposition;
  271. if (!$cid) {
  272. srand((double)microtime()*96545624);
  273. $cid = md5(uniqid(rand())).'@vlib.mimemail';
  274. }
  275. $this->contentIDs[] = $cid;
  276. return $cid;
  277. }
  278. /**
  279. * FUNCTION: organization
  280. *
  281. * Sets the Organization header for the message.
  282. *
  283. * @param string $org organization name
  284. * @access public
  285. */
  286. function organization ($org) {
  287. if(!empty($org)) $this->xheaders['Organization'] = $org;
  288. }
  289. /**
  290. * FUNCTION: receipt
  291. *
  292. * To request a receipt you must call this function with a true value.
  293. * And you must call $this->from() or $this->replyTo() before sending the mail.
  294. *
  295. * @param bool $bool true/false
  296. * @access public
  297. */
  298. function receipt ($bool=true) {
  299. $this->receipt = ($bool);
  300. }
  301. /**
  302. * FUNCTION: priority
  303. *
  304. * Sets the Priority header for the message.
  305. * usage: $mail->priority(1); // highest setting
  306. * view documentation for priority levels.
  307. *
  308. * @param string $priority
  309. * @access public
  310. */
  311. function priority ($priority) {
  312. if(!is_int($priority)) $priority = settype($priority, 'integer');
  313. if(!isset($this->priorities[$priority-1])) return false;
  314. $this->xheaders['X-Priority'] = $this->priorities[$priority-1];
  315. return true;
  316. }
  317. /**
  318. * FUNCTION: validateEmail
  319. *
  320. * Validates an email address and return true or false.
  321. *
  322. * @param string $address email address
  323. * @return bool true/false
  324. * @access public
  325. */
  326. function validateEmail ($address) {
  327. return preg_match('/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9_-]+(\.[_a-z0-9-]+)+$/i',$address);
  328. }
  329. /**
  330. * FUNCTION: send
  331. *
  332. * Sends the mail.
  333. *
  334. * @return boolean true on success, false on failure
  335. * @access public
  336. */
  337. function send () {
  338. $this->_buildMail();
  339. $to_str = ($this->apply_windows_bugfix) ? implode(',', $this->all_emails) : $this->xheaders['To'];
  340. return mail($to_str, $this->xheaders['Subject'], $this->fullBody, $this->headers);
  341. }
  342. /**
  343. * FUNCTION: get
  344. *
  345. * Returns the whole e-mail, headers and message. Can be used to display the
  346. * message in pain text or for debugging.
  347. *
  348. * @return string message
  349. * @access public
  350. */
  351. function get () {
  352. $this->_buildMail();
  353. $mail = 'To: '.$this->xheaders['To']."\n";
  354. $mail .= 'Subject: '.$this->xheaders['Subject']."\n";
  355. $mail .= $this->headers . "\n";
  356. $mail .= $this->fullBody;
  357. return $mail;
  358. }
  359. /*-----------------------------------------------------------------------------\
  360. | private functions |
  361. \-----------------------------------------------------------------------------*/
  362. /**
  363. * FUNCTION: vlibMimeMail [contsructor]
  364. *
  365. * Enables auto checking by default.
  366. * Also creates the unique boundary id for this message.
  367. *
  368. * @param bool $apply_windows_bugfix tells us whether to apply bug fix for windows Cc: and Bcc: headers.
  369. * @access private
  370. */
  371. function vlibMimeMail ($apply_windows_bugfix=false) {
  372. $this->apply_windows_bugfix = ($apply_windows_bugfix);
  373. $this->boundary = "--" . md5(uniqid('vlibseediganoofahilfaman'));
  374. $this->boundary_alt = "--" . md5(uniqid('vlibseedvatefairechier'));
  375. }
  376. /**
  377. * FUNCTION: _buildMail
  378. *
  379. * Proccesses all headers and attachments ready for sending.
  380. *
  381. * @access private
  382. */
  383. function _buildMail () {
  384. if (empty($this->sendto) && (empty($this->body) && empty($this->htmlbody))) {
  385. vlibMimeMailError::raiseError('VM_ERROR_CANNOT_SEND', FATAL);
  386. }
  387. // build the headers
  388. $this->headers = "";
  389. $this->xheaders['To'] = implode(',', $this->sendto);
  390. $cc_header_name = ($this->apply_windows_bugfix) ? 'cc': 'Cc';
  391. if (!empty($this->sendcc)) $this->xheaders[$cc_header_name] = implode(',', $this->sendcc);
  392. if (!empty($this->sendbcc)) $this->xheaders['Bcc'] = implode(',', $this->sendbcc);
  393. if($this->receipt) {
  394. if(isset($this->xheaders['Reply-To'])) {
  395. $this->xheaders['Disposition-Notification-To'] = $this->xheaders['Reply-To'];
  396. }
  397. elseif (isset($this->xheaders['From'])) {
  398. $this->xheaders['Disposition-Notification-To'] = $this->xheaders['From'];
  399. }
  400. }
  401. if($this->charset != '') {
  402. $this->xheaders['Mime-Version'] = '1.0';
  403. $this->xheaders['Content-Type'] = 'text/plain; charset='.$this->charset;
  404. $this->xheaders['Content-Transfer-Encoding'] = $this->ctencoding;
  405. }
  406. $this->xheaders['X-Mailer'] = 'vlibMimeMail';
  407. // setup the body ready for sending
  408. $this->_setBody();
  409. foreach ($this->xheaders as $head => $value) {
  410. $rgx = ($this->apply_windows_bugfix) ? 'Subject' : 'Subject|To'; // don't strip out To header for bugfix
  411. if (!preg_match('/^'.$rgx.'$/i', $head)) $this->headers .= $head.': '.strtr($value, "\r\n", ' ')."\n";
  412. }
  413. }
  414. /**
  415. * FUNCTION: _setBody
  416. *
  417. * sets the body to be used in a mail.
  418. *
  419. * @access private
  420. */
  421. function _setBody () {
  422. // do we need to encode??
  423. $encode = (empty($this->htmlbody) && empty($this->attachments)) ? false : true;
  424. // if yes...
  425. if ($encode) {
  426. $this->fullBody = "This is a multi-part message in MIME format.\n\n";
  427. $this->fullBody .= '--'.$this->boundary."\nContent-Type: multipart/alternative;\n\tboundary=\"".$this->boundary_alt."\"\n\n\n";
  428. $body_boundary = $this->boundary_alt;
  429. $this->xheaders['Content-Type'] = "multipart/mixed;\n\tboundary=\"".$this->boundary.'"';
  430. if (!empty($this->body)) {
  431. $this->fullBody .= '--'.$body_boundary."\nContent-Type: text/plain; charset=".$this->charset."\nContent-Transfer-Encoding: ".$this->ctencoding."\n\n".$this->body."\n\n";
  432. }
  433. if (!empty($this->htmlbody)) {
  434. $this->fullBody .= '--'.$body_boundary."\nContent-Type: text/html; charset=".$this->charset."\nContent-Transfer-Encoding: ".$this->ctencoding."\n\n".$this->htmlbody."\n\n";
  435. }
  436. $this->fullBody .= '--'.$body_boundary."--\n\n";
  437. if (!empty($this->attachments)) {
  438. $this->_build_attachments();
  439. }
  440. $this->fullBody .= '--'.$this->boundary.'--'; // ends the last boundary
  441. }
  442. // else we just send plain text.
  443. else {
  444. if (!empty($this->body)) {
  445. $this->fullBody = $this->body;
  446. }
  447. else {
  448. vlibMimeMailError::raiseError('VM_ERROR_NOBODY', FATAL);
  449. }
  450. }
  451. }
  452. /**
  453. * FUNCTION: _build_attachments
  454. *
  455. * Checks and encodes all attachments.
  456. *
  457. * @access private
  458. */
  459. function _build_attachments () {
  460. $sep = chr(13).chr(10);
  461. $ata = array();
  462. $k=0;
  463. for ($i=0; $i < count( $this->attachments); $i++) {
  464. $filename = $this->attachments[$i];
  465. $basename = basename($filename);
  466. $mimetype = $this->mimetypes[$i];
  467. $disposition = $this->dispositions[$i];
  468. $contentID = $this->contentIDs[$i];
  469. if (preg_match('/^[a-zA-Z]+:\/\//', $filename)) { // figure out if local or remote
  470. $upart = parse_url($filename);
  471. $newfilename = $upart['scheme'].'://';
  472. if (!empty($upart['user'])) $newfilename .= $upart['user'].':'.$upart['pass'].'@';
  473. $newfilename .= $upart['host'];
  474. if (!empty($upart['port'])) $newfilename .= ':'.$upart['port'];
  475. $newfilename .= '/';
  476. if (!empty($upart['path'])) {
  477. $upart['path'] = substr($upart['path'], 1);
  478. $newpath = explode('/', $upart['path']);
  479. for($i=0; $i<count($newpath); $i++) $newpath[$i] = rawurlencode($newpath[$i]);
  480. $newfilename .= implode('/',$newpath);
  481. }
  482. if (!empty($upart['query'])) $newfilename .= '?'.urlencode($upart['query']);
  483. if (!empty($upart['fragment'])) $newfilename .= ':'.$upart['fragment'];
  484. $fp = fopen($newfilename, 'rb');
  485. while(!feof($fp)) $data .= fread($fp,1024);
  486. }
  487. else {
  488. if(!file_exists($filename)) {
  489. vlibMimeMailError::raiseError('VM_ERROR_NOFILE', FATAL, $filename);
  490. }
  491. $fp = fopen($filename, 'rb');
  492. $data = fread($fp, filesize($filename));
  493. }
  494. $subhdr = '--'.$this->boundary."\nContent-type: ".$mimetype.";\n\tname=\"".$basename."\"\nContent-Transfer-Encoding: base64\nContent-Disposition: ".$disposition.";\n\tfilename=\"".$basename."\"\n";
  495. if ($contentID) $subhdr .= 'Content-ID: <'.$contentID.">\n";
  496. $ata[$k++] = $subhdr;
  497. $ata[$k++] = chunk_split(base64_encode($data))."\n\n";
  498. fclose($fp);
  499. }
  500. $this->fullBody .= "\n".implode($sep, $ata);
  501. }
  502. } // class vlibMimeMail
  503. } // << end if(!defined())..
  504. ?>