baseline
[omp.pkp.sfu.ca.git] / lib / pkp / classes / mail / Mail.inc.php
blob2263df3e699bd24e6c1a6563f517e21703a44260
1 <?php
3 /**
4 * @defgroup mail
5 */
7 /**
8 * @file classes/mail/Mail.inc.php
10 * Copyright (c) 2000-2009 John Willinsky
11 * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
13 * @class Mail
14 * @ingroup mail
16 * @brief Class defining basic operations for handling and sending emails.
19 // $Id: Mail.inc.php,v 1.10 2009/09/19 00:12:05 asmecher Exp $
22 define('MAIL_EOL', Core::isWindows() ? "\r\n" : "\n");
23 define('MAIL_WRAP', 76);
25 class Mail extends DataObject {
26 /** @var array List of key => value private parameters for this message */
27 var $privateParams;
29 /**
30 * Constructor.
32 function Mail() {
33 parent::DataObject();
34 $this->privateParams = array();
35 if (Config::getVar('email', 'allow_envelope_sender')) {
36 $defaultEnvelopeSender = Config::getVar('email', 'default_envelope_sender');
37 if (!empty($defaultEnvelopeSender)) $this->setEnvelopeSender($defaultEnvelopeSender);
41 /**
42 * Add a private parameter to this email. Private parameters are replaced
43 * just before sending and are never available via getBody etc.
45 function addPrivateParam($name, $value) {
46 $this->privateParams[$name] = $value;
49 /**
50 * Set the entire list of private parameters.
51 * @see addPrivateParam
53 function setPrivateParams($privateParams) {
54 $this->privateParams = $privateParams;
57 function addRecipient($email, $name = '') {
58 if (($recipients = $this->getData('recipients')) == null) {
59 $recipients = array();
61 array_push($recipients, array('name' => $name, 'email' => $email));
63 return $this->setData('recipients', $recipients);
66 function setEnvelopeSender($envelopeSender) {
67 $this->setData('envelopeSender', $envelopeSender);
70 function getEnvelopeSender() {
71 return $this->getData('envelopeSender');
74 function getContentType() {
75 return $this->getData('content_type');
78 function setContentType($contentType) {
79 return $this->setData('content_type', $contentType);
82 function getRecipients() {
83 return $this->getData('recipients');
86 function setRecipients($recipients) {
87 return $this->setData('recipients', $recipients);
90 function addCc($email, $name = '') {
91 if (($ccs = $this->getData('ccs')) == null) {
92 $ccs = array();
94 array_push($ccs, array('name' => $name, 'email' => $email));
96 return $this->setData('ccs', $ccs);
99 function getCcs() {
100 return $this->getData('ccs');
103 function setCcs($ccs) {
104 return $this->setData('ccs', $ccs);
107 function addBcc($email, $name = '') {
108 if (($bccs = $this->getData('bccs')) == null) {
109 $bccs = array();
111 array_push($bccs, array('name' => $name, 'email' => $email));
113 return $this->setData('bccs', $bccs);
116 function getBccs() {
117 return $this->getData('bccs');
120 function setBccs($bccs) {
121 return $this->setData('bccs', $bccs);
125 * If no recipients for this message, promote CC'd accounts to
126 * recipients. If recipients exist, no effect.
127 * @return boolean true iff CCs were promoted
129 function promoteCcsIfNoRecipients() {
130 $ccs = $this->getCcs();
131 $recipients = $this->getRecipients();
132 if (empty($recipients)) {
133 $this->setRecipients($ccs);
134 $this->setCcs(array());
135 return true;
137 return false;
141 * Clear all recipients for this message (To, CC, and BCC).
143 function clearAllRecipients() {
144 $this->setRecipients(array());
145 $this->setCcs(array());
146 $this->setBccs(array());
149 function addHeader($name, $content) {
150 $updated = false;
152 if (($headers = $this->getData('headers')) == null) {
153 $headers = array();
156 foreach ($headers as $key => $value) {
157 if ($headers[$key]['name'] == $name) {
158 $headers[$key]['content'] = $content;
159 $updated = true;
163 if (!$updated) {
164 array_push($headers, array('name' => $name,'content' => $content));
167 return $this->setData('headers', $headers);
170 function getHeaders() {
171 return $this->getData('headers');
174 function setHeaders(&$headers) {
175 return $this->setData('headers', $headers);
179 * Adds a file attachment to the email.
180 * @param $filePath string complete path to the file to attach
181 * @param $fileName string attachment file name (optional)
182 * @param $contentType string attachment content type (optional)
183 * @param $contentDisposition string attachment content disposition, inline or attachment (optional, default attachment)
185 function addAttachment($filePath, $fileName = '', $contentType = '', $contentDisposition = 'attachment') {
186 if ($attachments =& $this->getData('attachments') == null) {
187 $attachments = array();
190 /* If the arguments $fileName and $contentType are not specified,
191 then try and determine them automatically. */
192 if (empty($fileName)) {
193 $fileName = basename($filePath);
196 if (empty($contentType)) {
197 $contentType = String::mime_content_type($filePath);
198 if (empty($contentType)) $contentType = 'application/x-unknown-content-type';
201 // Open the file and read contents into $attachment
202 if (is_readable($filePath) && is_file($filePath)) {
203 $fp = fopen($filePath, 'rb');
204 if ($fp) {
205 $content = '';
206 while (!feof($fp)) {
207 $content .= fread($fp, 4096);
209 fclose($fp);
213 if (isset($content)) {
214 /* Encode the contents in base64. */
215 $content = chunk_split(base64_encode($content), MAIL_WRAP, MAIL_EOL);
216 array_push($attachments, array('filename' => $fileName, 'content-type' => $contentType, 'disposition' => $contentDisposition, 'content' => $content));
218 return $this->setData('attachments', $attachments);
219 } else {
220 return false;
224 function &getAttachments() {
225 $attachments =& $this->getData('attachments');
226 return $attachments;
229 function hasAttachments() {
230 $attachments =& $this->getAttachments();
231 return ($attachments != null && count($attachments) != 0);
234 function setFrom($email, $name = '') {
235 return $this->setData('from', array('name' => $name, 'email' => $email));
238 function getFrom() {
239 return $this->getData('from');
242 function setSubject($subject) {
243 return $this->setData('subject', $subject);
246 function getSubject() {
247 return $this->getData('subject');
250 function setBody($body) {
251 return $this->setData('body', $body);
254 function getBody() {
255 return $this->getData('body');
259 * Return a string containing the from address.
260 * @return string
262 function getFromString() {
263 $from = $this->getFrom();
264 if ($from == null) {
265 return null;
266 } else {
267 return Mail::encodeDisplayName($from['name']) . ' <'.$from['email'].'>';
272 * Return a string from an array of (name, email) pairs.
273 * @param $includeNames boolean
274 * @return string;
276 function getAddressArrayString($addresses, $includeNames = true) {
277 if ($addresses == null) {
278 return null;
280 } else {
281 $addressString = '';
283 foreach ($addresses as $address) {
284 if (!empty($addressString)) {
285 $addressString .= ', ';
288 if (Core::isWindows() || empty($address['name']) || !$includeNames) {
289 $addressString .= $address['email'];
291 } else {
292 $addressString .= Mail::encodeDisplayName($address['name']) . ' <'.$address['email'].'>';
296 return $addressString;
301 * Return a string containing the recipients.
302 * @return string
304 function getRecipientString() {
305 return $this->getAddressArrayString($this->getRecipients());
309 * Return a string containing the Cc recipients.
310 * @return string
312 function getCcString() {
313 return $this->getAddressArrayString($this->getCcs());
317 * Return a string containing the Bcc recipients.
318 * @return string
320 function getBccString() {
321 return $this->getAddressArrayString($this->getBccs(), false);
326 * Send the email.
327 * @return boolean
329 function send() {
330 $recipients = $this->getRecipientString();
331 $from = $this->getFromString();
333 $subject = String::encode_mime_header($this->getSubject());
334 $body = $this->getBody();
336 // FIXME Some *nix mailers won't work with CRLFs
337 if (Core::isWindows()) {
338 // Convert LFs to CRLFs for Windows
339 $body = String::regexp_replace("/([^\r]|^)\n/", "\$1\r\n", $body);
340 } else {
341 // Convert CRLFs to LFs for *nix
342 $body = String::regexp_replace("/\r\n/", "\n", $body);
345 if ($this->getContentType() != null) {
346 $this->addHeader('Content-Type', $this->getContentType());
347 } elseif ($this->hasAttachments()) {
348 // Only add MIME headers if sending an attachment
349 $mimeBoundary = '==boundary_'.md5(microtime());
351 /* Add MIME-Version and Content-Type as headers. */
352 $this->addHeader('MIME-Version', '1.0');
353 $this->addHeader('Content-Type', 'multipart/mixed; boundary="'.$mimeBoundary.'"');
355 } else {
356 $this->addHeader('Content-Type', 'text/plain; charset="'.Config::getVar('i18n', 'client_charset').'"');
359 $this->addHeader('X-Mailer', 'Public Knowledge Project Suite v2');
361 $remoteAddr = Request::getRemoteAddr();
362 if ($remoteAddr != '') $this->addHeader('X-Originating-IP', $remoteAddr);
364 $this->addHeader('Date', date('D, d M Y H:i:s O'));
366 /* Add $from, $ccs, and $bccs as headers. */
367 if ($from != null) {
368 $this->addHeader('From', $from);
371 $ccs = $this->getCcString();
372 if ($ccs != null) {
373 $this->addHeader('Cc', $ccs);
376 $bccs = $this->getBccString();
377 if ($bccs != null) {
378 $this->addHeader('Bcc', $bccs);
381 $headers = '';
382 foreach ($this->getHeaders() as $header) {
383 if (!empty($headers)) {
384 $headers .= MAIL_EOL;
386 $headers .= $header['name'].': '. str_replace(array("\r", "\n"), '', $header['content']);
389 if ($this->hasAttachments()) {
390 // Add the body
391 $mailBody = 'This message is in MIME format and requires a MIME-capable mail client to view.'.MAIL_EOL.MAIL_EOL;
392 $mailBody .= '--'.$mimeBoundary.MAIL_EOL;
393 $mailBody .= sprintf('Content-Type: text/plain; charset=%s', Config::getVar('i18n', 'client_charset')) . MAIL_EOL.MAIL_EOL;
394 $mailBody .= wordwrap($body, MAIL_WRAP, MAIL_EOL).MAIL_EOL.MAIL_EOL;
396 // Add the attachments
397 $attachments = $this->getAttachments();
398 foreach ($attachments as $attachment) {
399 $mailBody .= '--'.$mimeBoundary.MAIL_EOL;
400 $mailBody .= 'Content-Type: '.$attachment['content-type'].'; name="'.$attachment['filename'].'"'.MAIL_EOL;
401 $mailBody .= 'Content-transfer-encoding: base64'.MAIL_EOL;
402 $mailBody .= 'Content-disposition: '.$attachment['disposition'].MAIL_EOL.MAIL_EOL;
403 $mailBody .= $attachment['content'].MAIL_EOL.MAIL_EOL;
406 $mailBody .= '--'.$mimeBoundary.'--';
408 } else {
409 // Just add the body
410 $mailBody = wordwrap($body, MAIL_WRAP, MAIL_EOL);
413 if ($this->getEnvelopeSender() != null) {
414 $additionalParameters = '-f ' . $this->getEnvelopeSender();
415 } else {
416 $additionalParameters = null;
419 if (HookRegistry::call('Mail::send', array(&$this, &$recipients, &$subject, &$mailBody, &$headers, &$additionalParameters))) return;
421 // Replace all the private parameters for this message.
422 if (is_array($this->privateParams)) {
423 foreach ($this->privateParams as $name => $value) {
424 $mailBody = str_replace($name, $value, $mailBody);
428 if (Config::getVar('email', 'smtp')) {
429 $smtp =& Registry::get('smtpMailer', true, null);
430 if ($smtp === null) {
431 import('mail.SMTPMailer');
432 $smtp = new SMTPMailer();
434 $sent = $smtp->mail($this, $recipients, $subject, $mailBody, $headers);
435 } else {
436 $sent = String::mail($recipients, $subject, $mailBody, $headers, $additionalParameters);
439 if (!$sent) {
440 if (Config::getVar('debug', 'display_errors')) {
441 if (Config::getVar('email', 'smtp')) {
442 fatalError("There was an error sending this email. Please check your PHP error log for more information.");
443 return false;
444 } else {
445 fatalError("There was an error sending this email. Please check your mail log (/var/log/maillog).");
446 return false;
448 } else return false;
449 } else return true;
452 function encodeDisplayName($displayName) {
453 if (String::regexp_match('!^[-A-Za-z0-9\!#\$%&\'\*\+\/=\?\^_\`\{\|\}~]+$!', $displayName)) return $displayName;
454 return ('"' . str_replace(
455 array('"', '\\'),
457 $displayName
458 ) . '"');