* @license PHP License * @package Wombat * @subpackage base */ /** * Send emails * * Simple interface to send emails * * @version 0.4.1 * @package Wombat * @subpackage base */ class WBMailer extends WBStdClass { protected $header = array( 'From' => 'test@example.com', 'X-Mailer' => 'Wombat Framework Mailer', 'X-Sender' => '__usefrom', 'MIME-Version' => '1.0', 'Content-Transfer-Encoding' => '8bit', 'Content-Type' => 'text/plain; charset="utf-8"', 'Date' => '' ); /** * mailer backend * @var Mail */ protected $backend; /** * template engine * @var patTemplate */ protected $tmpl; /** * Mime Mail as alternative for e-mails based on Templates * @var WBMail_Mime */ private $mailMime; /** * config loader * @var WBConfig */ protected $config; /** * list of recipients * @var array */ protected $rcpt = array(); /** * additional data to fill email templates * @var array */ protected $data = array(); const ADDR_TO = 1; const ADDR_CC = 2; const ADDR_BCC = 3; const ADDR_REPLYTO = 4; /** * list of addresses to be added to email-header * @var array */ protected $address = array( self::ADDR_TO => array(), self::ADDR_CC => array() ); /** * constructor * * Load header config and start template engine */ public function __construct() { $this->config = WBClass::create('WBConfig'); $this->config->load('config'); // overwrite header config $header = $this->config->get('network/mail/header'); if (is_array($header)) { $this->header = array_merge($this->header, $header); } $this->tmpl = WBClass::create('patTemplate'); } /** * Tell 2 Use Mime Mail Object * * By default, use template engine for mail body and subject * * @param WBMail_Mime */ public function setMimeMail(WBMail_Mime $mm) { $this->mailMime = $mm; } /** * set template file * * Set template file that holds at least templates: * - email_subject * - email_plain * Optionally, use add template: email_html for HTML email * * Global template variables are also supported. Those templates will be used to * build email's subject as well as html and plain text body * * @param string $tmpl */ public function setTemplate($tmpl) { // start from scratch $this->tmpl->freeAllTemplates(); $result = $this->tmpl->readTemplatesFromInput('Mailer/' . $tmpl . '.tmpl'); if (patErrorManager::isError($result)) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Could not load template', 1, __CLASS__); } } /** * add one or more recipient * * add list ot internal list of recipients * * @param string|array $rcpt email address(es) */ public function addRcpt($rcpt) { if (!is_array($rcpt)) { $rcpt = array($rcpt); } $this->rcpt = array_merge($this->rcpt, $rcpt); $this->rcpt = array_unique($this->rcpt); } /** * set from address * * encode address add it to From-header * * @param string $address */ public function setFrom($address) { $this->header['From'] = $this->encodeAddress($address); } /** * Add address to To * * Encode address and prepare to add it to email header * * @param string $address */ public function addTo($address) { $this->address[self::ADDR_TO][] = $this->encodeAddress($address); } /** * Add address to CC * * @see addTo() * @param string $address */ public function addCc($address) { $this->address[self::ADDR_CC][] = $this->encodeAddress($address); } /** * Add address to CC * * @see addTo() * @param string $address */ public function addBcc($address) { $this->address[self::ADDR_BCC][] = $this->encodeAddress($address); } /** * Add address to Reply-To * * @see addTo() * @param string $address */ public function addReplyTo($address) { $this->address[self::ADDR_REPLYTO][] = $this->encodeAddress($address); } /** * base64 encode email address * * See whether an email address contains name and the actual address * and encode it accordingly * * @param string $address * @return string */ protected function encodeAddress($address) { // base 64 encode if required if (strstr($address, '<')) { $address = explode('<', $address, 2); $address = '=?UTF-8?B?' . base64_encode($address[0]) . '?= <' . $address[1]; } return $address; } /** * set data to fill placeholders * * The given array will be passed to patTemplate to be added * as global variables * * @param array $data * @param string prefix */ public function setData(Array $data, $prefix = '') { $this->data = array(); $this->addData($data, $prefix); } /** * add data to fill placeholders * * @see setData() * @param array $data * @param string prefix */ public function addData(Array $data, $prefix = '') { if (empty($prefix)) { $this->data = array_merge($this->data, $data); return; } foreach ($data as $k => $v) { $this->data[$prefix . $k] = $v; } } private function getMailBody4Template() { if (!$this->tmpl->exists('email_plain')) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Template "email_plain" is missing, probably because the template file was not set, yet.', 3, __CLASS__); } if (!$this->tmpl->exists('email_subject')) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Template "email_subject" is missing, probably because the template file was not set, yet.', 4, __CLASS__); } // mail body WBClass::load('WBString'); $this->tmpl->clearAllTemplates(); // URL path $config = WBClass::create('WBConfig'); $config->load('user/page'); $tmp = $config->get('path'); $this->tmpl->addGlobalVars($tmp, 'URL_PATH_'); $this->addData2Template(); // plain text email body $plain = $this->tmpl->getParsedTemplate('email_plain'); $plain = WBString::replaceSuperPlaceholders($plain); // load subject from template and make sure there is only one line $subj = $this->tmpl->getParsedTemplate('email_subject'); $subj = WBString::replaceSuperPlaceholders($subj); $subj = trim($subj); $subj = explode("\n", $subj); $subj = $subj[0]; $subj = '=?UTF-8?B?' . base64_encode($subj) . '?='; $this->setHeader('Subject', $subj); return $plain; } private function getMailBody4Mime() { /** @var WBMail_Mime_Storage_String */ $ms = WBClass::create('WBMail_Mime_Storage_String'); $ms->setMimeMail($this->mailMime); $header = array(); $body = $ms->saveToString($header); foreach ($header as $k => $v) { $this->setHeader($k, $v); } return $body; } /** * send email * * Build email body and complete email headers. If everything is OK, send email * * @todo add support for HTML mime emails and attachements * @throws WBException_Argument * @throws WBException_Pear */ public function send() { if (empty($this->rcpt)) { WBClass::load('WBException_Argument'); throw new WBException_Argument('At least on recipient is required to send an email, still the list of recipients is empty', 2, __CLASS__); } if (empty($this->mailMime)) { $body = $this->getMailBody4Template(); } else { $body = $this->getMailBody4Mime(); } // mail header $header = $this->header; if (!empty($this->address[self::ADDR_TO])) { $header['To'] = implode(', ', $this->address[self::ADDR_TO]); $this->address[self::ADDR_TO] = array(); } if (!empty($this->address[self::ADDR_CC])) { $header['Cc'] = implode(', ', $this->address[self::ADDR_CC]); $this->address[self::ADDR_CC] = array(); } if (!empty($this->address[self::ADDR_BCC])) { $header['Bcc'] = implode(', ', $this->address[self::ADDR_BCC]); $this->address[self::ADDR_BCC] = array(); } if (!empty($this->address[self::ADDR_REPLYTO])) { $header['Reply-To'] = implode(', ', $this->address[self::ADDR_REPLYTO]); $this->address[self::ADDR_REPLYTO] = array(); } // initialize actual email backend $backend = $this->config->get('network/mail/backend', 'mail'); $param = $this->config->get('network/mail/parameter', array()); if (!is_array($param)) { $param = array(); } // override with WBParam $backend = WBParam::get('wb/network/mail/backend', $backend); $param = WBParam::get('wb/network/mail/parameter', $param); // sender parameter $sender = ''; if ('__usefrom' == $header['X-Sender']) { $header['X-Sender'] = $header['From']; } if(!empty($header['X-Sender']) && preg_match('/([\\w\\d-]+)@(([\\w\\d-]+\\.)+\w+)/', $header['X-Sender'], $match)) { $sender = $match[1] . '@' . $match[2]; } if (!empty($sender)) { $param[] = '-f ' . $sender; } // configure mock backend if ('mock' == $backend) { $mock = WBClass::create('WBMail_Mock', $param); $param['preSendCallback'] = array($mock, 'preSend'); $param['postSendCallback'] = array($mock, 'postSend'); } $this->rcpt = implode(', ', $this->rcpt); WBClass::load('Mail'); // suppress error messages, because PEAR classes throw E_STRICT warnings $mailer = @Mail::factory($backend, $param); // set date $header['X-Date'] = gmdate('r'); if (empty($header['Date'])) { $header['Date'] = gmdate('r'); } $result = $mailer->send($this->rcpt, $header, $body); $this->rcpt = array(); if (@PEAR::isError($result)) { WBClass::load('WBException_Pear'); throw new WBException_PEAR($result->getMessage(), $result->getCode(), __CLASS__); } } /** * Set custom e-mail header * * @var string $header * @var string $value */ public function setHeader($header, $value) { $this->header[$header] = $value; } /** * Add mixed data to template * * Devide scalar variables and lists. Try to find matching templates and add list entries */ private function addData2Template() { $scalar = array(); foreach ($this->data as $k => $v) { if (!is_array($v)) { $scalar[$k] = $v; continue; } if(empty($v)) { $scalar[$k] = ''; continue; } $scalar[$k . '_count'] = count($v); if (!$this->tmpl->exists($k . '_list_entry')) { $scalar[$k] = implode(', ', $v); continue; } if (isset($v[0])) { $this->tmpl->addRows($k . '_list_entry', $v); } else { $this->tmpl->addVars($k . '_list_entry', $v); } } $this->tmpl->addGlobalVars($scalar); } }