From a36fc3485fd92593133d56e9bb98d739d70ed94f Mon Sep 17 00:00:00 2001 From: Andreas Gohr <andi@splitbrain.org> Date: Sat, 12 Nov 2011 11:01:22 +0100 Subject: [PATCH] clean headers in a separate step this should make it easier to reintroduce a plugin hook compatible with the old one --- inc/Mailer.class.php | 179 +++++++++++++++++++++++++++---------------- 1 file changed, 115 insertions(+), 64 deletions(-) diff --git a/inc/Mailer.class.php b/inc/Mailer.class.php index 420277d9e..8f2992201 100644 --- a/inc/Mailer.class.php +++ b/inc/Mailer.class.php @@ -89,6 +89,8 @@ class Mailer { /** * Add an arbitrary header to the mail * + * If an empy value is passed, the header is removed + * * @param string $header the header name (no trailing colon!) * @param string $value the value of the header * @param bool $clean remove all non-ASCII chars and line feeds? @@ -99,7 +101,14 @@ class Mailer { $header = preg_replace('/[^\w \-\.\+\@]+/','',$header); $value = preg_replace('/[^\w \-\.\+\@]+/','',$value); } - $this->headers[$header] = $value; + + // empty value deletes + $value = trim($value); + if($value === ''){ + if(isset($this->headers[$header])) unset($this->headers[$header]); + }else{ + $this->headers[$header] = $value; + } } /** @@ -128,6 +137,58 @@ class Mailer { $this->text = $text; } + /** + * Add the To: recipients + * + * @see setAddress + * @param string $address Multiple adresses separated by commas + */ + public function to($address){ + $this->setHeader('To', $address, false); + } + + /** + * Add the Cc: recipients + * + * @see setAddress + * @param string $address Multiple adresses separated by commas + */ + public function cc($address){ + $this->setHeader('Cc', $address, false); + } + + /** + * Add the Bcc: recipients + * + * @see setAddress + * @param string $address Multiple adresses separated by commas + */ + public function bcc($address){ + $this->setHeader('Bcc', $address, false); + } + + /** + * Add the From: address + * + * This is set to $conf['mailfrom'] when not specified so you shouldn't need + * to call this function + * + * @see setAddress + * @param string $address from address + */ + public function from($address){ + $this->setHeader('From', $address, false); + } + + /** + * Add the mail's Subject: header + * + * @param string $subject the mail subject + */ + public function subject($subject){ + $this->headers['Subject'] = $subject; + } + /** * Sets an email address header with correct encoding * @@ -138,13 +199,12 @@ class Mailer { * setAddress("föö <foo@bar.com>, me@somewhere.com","TBcc"); * * @param string $address Multiple adresses separated by commas - * @param string $header Name of the header (To,Bcc,Cc,...) + * @param string returns the prepared header (can contain multiple lines) */ - function setAddress($address,$header){ + public function cleanAddress($address){ // No named recipients for To: in Windows (see FS#652) $names = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? false : true; - $header = ucwords(strtolower($header)); // streamline casing $address = preg_replace('/[\r\n\0]+/',' ',$address); // remove attack vectors $headers = ''; @@ -207,72 +267,16 @@ class Mailer { // add to header comma seperated if($headers != ''){ - $headers .= ','; - $headers .= MAILHEADER_EOL.' '; // avoid overlong mail headers + $headers .= ', '; } $headers .= $text.' '.$addr; } if(empty($headers)) return false; - $this->headers[$header] = $headers; return $headers; } - /** - * Add the To: recipients - * - * @see setAddress - * @param string $address Multiple adresses separated by commas - */ - public function to($address){ - $this->setAddress($address, 'To'); - } - - /** - * Add the Cc: recipients - * - * @see setAddress - * @param string $address Multiple adresses separated by commas - */ - public function cc($address){ - $this->setAddress($address, 'Cc'); - } - - /** - * Add the Bcc: recipients - * - * @see setAddress - * @param string $address Multiple adresses separated by commas - */ - public function bcc($address){ - $this->setAddress($address, 'Bcc'); - } - - /** - * Add the From: address - * - * This is set to $conf['mailfrom'] when not specified so you shouldn't need - * to call this function - * - * @see setAddress - * @param string $address from address - */ - public function from($address){ - $this->setAddress($address, 'From'); - } - - /** - * Add the mail's Subject: header - * - * @param string $subject the mail subject - */ - public function subject($subject){ - if(!utf8_isASCII($subject)){ - $subject = '=?UTF-8?B?'.base64_encode($subject).'?='; - } - $this->headers['Subject'] = $subject; - } /** * Prepare the mime multiparts for all attachments @@ -325,7 +329,6 @@ class Mailer { } // add general headers - if(!isset($this->headers['From'])) $this->from($conf['mailfrom']); $this->headers['MIME-Version'] = '1.0'; $body = ''; @@ -342,14 +345,16 @@ class Mailer { // do we have alternative text content? if($this->text && $this->html){ - $this->headers['Content-Type'] = 'multipart/alternative; boundary="'.$this->boundary.'XX"'; + $this->headers['Content-Type'] = 'multipart/alternative;'.MAILHEADER_EOL. + ' boundary="'.$this->boundary.'XX"'; $body .= '--'.$this->boundary.'XX'.MAILHEADER_EOL; $body .= 'Content-Type: text/plain; charset=UTF-8'.MAILHEADER_EOL; $body .= 'Content-Transfer-Encoding: base64'.MAILHEADER_EOL; $body .= MAILHEADER_EOL; $body .= chunk_split(base64_encode($this->text),74,MAILHEADER_EOL); $body .= '--'.$this->boundary.'XX'.MAILHEADER_EOL; - $body .= 'Content-Type: multipart/related; boundary="'.$this->boundary.'"'.MAILHEADER_EOL; + $body .= 'Content-Type: multipart/related;'.MAILHEADER_EOL. + ' boundary="'.$this->boundary.'"'.MAILHEADER_EOL; $body .= MAILHEADER_EOL; } @@ -371,6 +376,47 @@ class Mailer { return $body; } + /** + * Cleanup and encode the headers array + */ + protected function cleanHeaders(){ + global $conf; + + // clean up addresses + if(empty($this->headers['From'])) $this->from($conf['mailfrom']); + $addrs = array('To','From','Cc','Bcc'); + foreach($addrs as $addr){ + if(isset($this->headers[$addr])){ + $this->headers[$addr] = $this->cleanAddress($this->headers[$addr]); + } + } + + if(isset($subject)){ + // add prefix to subject + if($conf['mailprefix']){ + $prefix = '['.$conf['mailprefix'].']'; + $len = strlen($prefix); + if(substr($this->headers['subject'],0,$len) != $prefix){ + $this->headers['subject'] = $prefix.' '.$this->headers['subject']; + } + } + + // encode subject + if(defined('MAILHEADER_ASCIIONLY')){ + $this->headers['subject'] = utf8_deaccent($this->headers['subject']); + $this->headers['subject'] = utf8_strip($this->headers['subject']); + } + if(!utf8_isASCII($this->headers['Subject'])){ + $subject = '=?UTF-8?B?'.base64_encode($this->headers['Subject']).'?='; + } + } + + // wrap headers + foreach($this->headers as $key => $val){ + $this->headers[$key] = wordwrap($val,78,MAILHEADER_EOL.' '); + } + } + /** * Create a string from the headers array * @@ -393,6 +439,7 @@ class Mailer { * @return string the mail, false on errors */ public function dump(){ + $this->cleanHeaders(); $body = $this->prepareBody(); if($body === 'false') return false; $headers = $this->prepareHeaders(); @@ -409,6 +456,10 @@ class Mailer { * @return bool true if the mail was successfully passed to the MTA */ public function send(){ + // FIXME hook here + + $this->cleanHeaders(); + // any recipients? if(trim($this->headers['To']) === '' && trim($this->headers['Cc']) === '' && -- GitLab