PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/mail/Mail.inc.php

https://github.com/mbehiels/pkp-lib
PHP | 460 lines | 299 code | 81 blank | 80 comment | 71 complexity | adeb91afe7cb834d857f580faa3f1a1a MD5 | raw file
  1. <?php
  2. /**
  3. * @defgroup mail
  4. */
  5. /**
  6. * @file classes/mail/Mail.inc.php
  7. *
  8. * Copyright (c) 2000-2011 John Willinsky
  9. * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
  10. *
  11. * @class Mail
  12. * @ingroup mail
  13. *
  14. * @brief Class defining basic operations for handling and sending emails.
  15. */
  16. define('MAIL_EOL', Core::isWindows() ? "\r\n" : "\n");
  17. define('MAIL_WRAP', 76);
  18. class Mail extends DataObject {
  19. /** @var array List of key => value private parameters for this message */
  20. var $privateParams;
  21. /**
  22. * Constructor.
  23. */
  24. function Mail() {
  25. parent::DataObject();
  26. $this->privateParams = array();
  27. if (Config::getVar('email', 'allow_envelope_sender')) {
  28. $defaultEnvelopeSender = Config::getVar('email', 'default_envelope_sender');
  29. if (!empty($defaultEnvelopeSender)) $this->setEnvelopeSender($defaultEnvelopeSender);
  30. }
  31. }
  32. /**
  33. * Add a private parameter to this email. Private parameters are replaced
  34. * just before sending and are never available via getBody etc.
  35. */
  36. function addPrivateParam($name, $value) {
  37. $this->privateParams[$name] = $value;
  38. }
  39. /**
  40. * Set the entire list of private parameters.
  41. * @see addPrivateParam
  42. */
  43. function setPrivateParams($privateParams) {
  44. $this->privateParams = $privateParams;
  45. }
  46. function addRecipient($email, $name = '') {
  47. if (($recipients = $this->getData('recipients')) == null) {
  48. $recipients = array();
  49. }
  50. array_push($recipients, array('name' => $name, 'email' => $email));
  51. return $this->setData('recipients', $recipients);
  52. }
  53. function setEnvelopeSender($envelopeSender) {
  54. $this->setData('envelopeSender', $envelopeSender);
  55. }
  56. function getEnvelopeSender() {
  57. return $this->getData('envelopeSender');
  58. }
  59. function getContentType() {
  60. return $this->getData('content_type');
  61. }
  62. function setContentType($contentType) {
  63. return $this->setData('content_type', $contentType);
  64. }
  65. function getRecipients() {
  66. return $this->getData('recipients');
  67. }
  68. function setRecipients($recipients) {
  69. return $this->setData('recipients', $recipients);
  70. }
  71. function addCc($email, $name = '') {
  72. if (($ccs = $this->getData('ccs')) == null) {
  73. $ccs = array();
  74. }
  75. array_push($ccs, array('name' => $name, 'email' => $email));
  76. return $this->setData('ccs', $ccs);
  77. }
  78. function getCcs() {
  79. return $this->getData('ccs');
  80. }
  81. function setCcs($ccs) {
  82. return $this->setData('ccs', $ccs);
  83. }
  84. function addBcc($email, $name = '') {
  85. if (($bccs = $this->getData('bccs')) == null) {
  86. $bccs = array();
  87. }
  88. array_push($bccs, array('name' => $name, 'email' => $email));
  89. return $this->setData('bccs', $bccs);
  90. }
  91. function getBccs() {
  92. return $this->getData('bccs');
  93. }
  94. function setBccs($bccs) {
  95. return $this->setData('bccs', $bccs);
  96. }
  97. /**
  98. * If no recipients for this message, promote CC'd accounts to
  99. * recipients. If recipients exist, no effect.
  100. * @return boolean true iff CCs were promoted
  101. */
  102. function promoteCcsIfNoRecipients() {
  103. $ccs = $this->getCcs();
  104. $recipients = $this->getRecipients();
  105. if (empty($recipients)) {
  106. $this->setRecipients($ccs);
  107. $this->setCcs(array());
  108. return true;
  109. }
  110. return false;
  111. }
  112. /**
  113. * Clear all recipients for this message (To, CC, and BCC).
  114. */
  115. function clearAllRecipients() {
  116. $this->setRecipients(array());
  117. $this->setCcs(array());
  118. $this->setBccs(array());
  119. }
  120. function addHeader($name, $content) {
  121. $updated = false;
  122. if (($headers = $this->getData('headers')) == null) {
  123. $headers = array();
  124. }
  125. foreach ($headers as $key => $value) {
  126. if ($headers[$key]['name'] == $name) {
  127. $headers[$key]['content'] = $content;
  128. $updated = true;
  129. }
  130. }
  131. if (!$updated) {
  132. array_push($headers, array('name' => $name,'content' => $content));
  133. }
  134. return $this->setData('headers', $headers);
  135. }
  136. function getHeaders() {
  137. return $this->getData('headers');
  138. }
  139. function setHeaders(&$headers) {
  140. return $this->setData('headers', $headers);
  141. }
  142. /**
  143. * Adds a file attachment to the email.
  144. * @param $filePath string complete path to the file to attach
  145. * @param $fileName string attachment file name (optional)
  146. * @param $contentType string attachment content type (optional)
  147. * @param $contentDisposition string attachment content disposition, inline or attachment (optional, default attachment)
  148. */
  149. function addAttachment($filePath, $fileName = '', $contentType = '', $contentDisposition = 'attachment') {
  150. if ($attachments =& $this->getData('attachments') == null) {
  151. $attachments = array();
  152. }
  153. /* If the arguments $fileName and $contentType are not specified,
  154. then try and determine them automatically. */
  155. if (empty($fileName)) {
  156. $fileName = basename($filePath);
  157. }
  158. if (empty($contentType)) {
  159. $contentType = String::mime_content_type($filePath);
  160. if (empty($contentType)) $contentType = 'application/x-unknown-content-type';
  161. }
  162. // Open the file and read contents into $attachment
  163. if (is_readable($filePath) && is_file($filePath)) {
  164. $fp = fopen($filePath, 'rb');
  165. if ($fp) {
  166. $content = '';
  167. while (!feof($fp)) {
  168. $content .= fread($fp, 4096);
  169. }
  170. fclose($fp);
  171. }
  172. }
  173. if (isset($content)) {
  174. /* Encode the contents in base64. */
  175. $content = chunk_split(base64_encode($content), MAIL_WRAP, MAIL_EOL);
  176. array_push($attachments, array('filename' => $fileName, 'content-type' => $contentType, 'disposition' => $contentDisposition, 'content' => $content));
  177. return $this->setData('attachments', $attachments);
  178. } else {
  179. return false;
  180. }
  181. }
  182. function &getAttachments() {
  183. $attachments =& $this->getData('attachments');
  184. return $attachments;
  185. }
  186. function hasAttachments() {
  187. $attachments =& $this->getAttachments();
  188. return ($attachments != null && count($attachments) != 0);
  189. }
  190. function setFrom($email, $name = '') {
  191. return $this->setData('from', array('name' => $name, 'email' => $email));
  192. }
  193. function getFrom() {
  194. return $this->getData('from');
  195. }
  196. function setSubject($subject) {
  197. return $this->setData('subject', $subject);
  198. }
  199. function getSubject() {
  200. return $this->getData('subject');
  201. }
  202. function setBody($body) {
  203. return $this->setData('body', $body);
  204. }
  205. function getBody() {
  206. return $this->getData('body');
  207. }
  208. /**
  209. * Return a string containing the from address.
  210. * @return string
  211. */
  212. function getFromString() {
  213. $from = $this->getFrom();
  214. if ($from == null) {
  215. return null;
  216. } else {
  217. return Mail::encodeDisplayName($from['name']) . ' <'.$from['email'].'>';
  218. }
  219. }
  220. /**
  221. * Return a string from an array of (name, email) pairs.
  222. * @param $includeNames boolean
  223. * @return string;
  224. */
  225. function getAddressArrayString($addresses, $includeNames = true) {
  226. if ($addresses == null) {
  227. return null;
  228. } else {
  229. $addressString = '';
  230. foreach ($addresses as $address) {
  231. if (!empty($addressString)) {
  232. $addressString .= ', ';
  233. }
  234. if (Core::isWindows() || empty($address['name']) || !$includeNames) {
  235. $addressString .= $address['email'];
  236. } else {
  237. $addressString .= Mail::encodeDisplayName($address['name']) . ' <'.$address['email'].'>';
  238. }
  239. }
  240. return $addressString;
  241. }
  242. }
  243. /**
  244. * Return a string containing the recipients.
  245. * @return string
  246. */
  247. function getRecipientString() {
  248. return $this->getAddressArrayString($this->getRecipients());
  249. }
  250. /**
  251. * Return a string containing the Cc recipients.
  252. * @return string
  253. */
  254. function getCcString() {
  255. return $this->getAddressArrayString($this->getCcs());
  256. }
  257. /**
  258. * Return a string containing the Bcc recipients.
  259. * @return string
  260. */
  261. function getBccString() {
  262. return $this->getAddressArrayString($this->getBccs(), false);
  263. }
  264. /**
  265. * Send the email.
  266. * @return boolean
  267. */
  268. function send() {
  269. $recipients = $this->getRecipientString();
  270. $from = $this->getFromString();
  271. $subject = String::encode_mime_header($this->getSubject());
  272. $body = $this->getBody();
  273. // FIXME Some *nix mailers won't work with CRLFs
  274. if (Core::isWindows()) {
  275. // Convert LFs to CRLFs for Windows
  276. $body = String::regexp_replace("/([^\r]|^)\n/", "\$1\r\n", $body);
  277. } else {
  278. // Convert CRLFs to LFs for *nix
  279. $body = String::regexp_replace("/\r\n/", "\n", $body);
  280. }
  281. if ($this->getContentType() != null) {
  282. $this->addHeader('Content-Type', $this->getContentType());
  283. } elseif ($this->hasAttachments()) {
  284. // Only add MIME headers if sending an attachment
  285. $mimeBoundary = '==boundary_'.md5(microtime());
  286. /* Add MIME-Version and Content-Type as headers. */
  287. $this->addHeader('MIME-Version', '1.0');
  288. $this->addHeader('Content-Type', 'multipart/mixed; boundary="'.$mimeBoundary.'"');
  289. } else {
  290. $this->addHeader('Content-Type', 'text/plain; charset="'.Config::getVar('i18n', 'client_charset').'"');
  291. }
  292. $this->addHeader('X-Mailer', 'Public Knowledge Project Suite v2');
  293. $remoteAddr = Request::getRemoteAddr();
  294. if ($remoteAddr != '') $this->addHeader('X-Originating-IP', $remoteAddr);
  295. $this->addHeader('Date', date('D, d M Y H:i:s O'));
  296. /* Add $from, $ccs, and $bccs as headers. */
  297. if ($from != null) {
  298. $this->addHeader('From', $from);
  299. }
  300. $ccs = $this->getCcString();
  301. if ($ccs != null) {
  302. $this->addHeader('Cc', $ccs);
  303. }
  304. $bccs = $this->getBccString();
  305. if ($bccs != null) {
  306. $this->addHeader('Bcc', $bccs);
  307. }
  308. $headers = '';
  309. foreach ($this->getHeaders() as $header) {
  310. if (!empty($headers)) {
  311. $headers .= MAIL_EOL;
  312. }
  313. $headers .= $header['name'].': '. str_replace(array("\r", "\n"), '', $header['content']);
  314. }
  315. if ($this->hasAttachments()) {
  316. // Add the body
  317. $mailBody = 'This message is in MIME format and requires a MIME-capable mail client to view.'.MAIL_EOL.MAIL_EOL;
  318. $mailBody .= '--'.$mimeBoundary.MAIL_EOL;
  319. $mailBody .= sprintf('Content-Type: text/plain; charset=%s', Config::getVar('i18n', 'client_charset')) . MAIL_EOL.MAIL_EOL;
  320. $mailBody .= wordwrap($body, MAIL_WRAP, MAIL_EOL).MAIL_EOL.MAIL_EOL;
  321. // Add the attachments
  322. $attachments = $this->getAttachments();
  323. foreach ($attachments as $attachment) {
  324. $mailBody .= '--'.$mimeBoundary.MAIL_EOL;
  325. $mailBody .= 'Content-Type: '.$attachment['content-type'].'; name="'.str_replace('"', '', $attachment['filename']).'"'.MAIL_EOL;
  326. $mailBody .= 'Content-transfer-encoding: base64'.MAIL_EOL;
  327. $mailBody .= 'Content-disposition: '.$attachment['disposition'].MAIL_EOL.MAIL_EOL;
  328. $mailBody .= $attachment['content'].MAIL_EOL.MAIL_EOL;
  329. }
  330. $mailBody .= '--'.$mimeBoundary.'--';
  331. } else {
  332. // Just add the body
  333. $mailBody = wordwrap($body, MAIL_WRAP, MAIL_EOL);
  334. }
  335. if ($this->getEnvelopeSender() != null) {
  336. $additionalParameters = '-f ' . $this->getEnvelopeSender();
  337. } else {
  338. $additionalParameters = null;
  339. }
  340. if (HookRegistry::call('Mail::send', array(&$this, &$recipients, &$subject, &$mailBody, &$headers, &$additionalParameters))) return;
  341. // Replace all the private parameters for this message.
  342. if (is_array($this->privateParams)) {
  343. foreach ($this->privateParams as $name => $value) {
  344. $mailBody = str_replace($name, $value, $mailBody);
  345. }
  346. }
  347. if (Config::getVar('email', 'smtp')) {
  348. $smtp =& Registry::get('smtpMailer', true, null);
  349. if ($smtp === null) {
  350. import('lib.pkp.classes.mail.SMTPMailer');
  351. $smtp = new SMTPMailer();
  352. }
  353. $sent = $smtp->mail($this, $recipients, $subject, $mailBody, $headers);
  354. } else {
  355. $sent = String::mail($recipients, $subject, $mailBody, $headers, $additionalParameters);
  356. }
  357. if (!$sent) {
  358. if (Config::getVar('debug', 'display_errors')) {
  359. if (Config::getVar('email', 'smtp')) {
  360. fatalError("There was an error sending this email. Please check your PHP error log for more information.");
  361. return false;
  362. } else {
  363. fatalError("There was an error sending this email. Please check your mail log (/var/log/maillog).");
  364. return false;
  365. }
  366. } else return false;
  367. } else return true;
  368. }
  369. function encodeDisplayName($displayName) {
  370. if (String::regexp_match('!^[-A-Za-z0-9\!#\$%&\'\*\+\/=\?\^_\`\{\|\}~]+$!', $displayName)) return $displayName;
  371. return ('"' . str_replace(
  372. array('"', '\\'),
  373. '',
  374. $displayName
  375. ) . '"');
  376. }
  377. }
  378. ?>