1bb01c27cSAndreas Gohr<?php 2bb01c27cSAndreas Gohr/** 31d045709SAndreas Gohr * A class to build and send multi part mails (with HTML content and embedded 41d045709SAndreas Gohr * attachments). All mails are assumed to be in UTF-8 encoding. 51d045709SAndreas Gohr * 61d045709SAndreas Gohr * Attachments are handled in memory so this shouldn't be used to send huge 71d045709SAndreas Gohr * files, but then again mail shouldn't be used to send huge files either. 81d045709SAndreas Gohr * 9bb01c27cSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 10bb01c27cSAndreas Gohr */ 11bb01c27cSAndreas Gohr 12bb01c27cSAndreas Gohr// end of line for mail lines - RFC822 says CRLF but postfix (and other MTAs?) 13bb01c27cSAndreas Gohr// think different 14bb01c27cSAndreas Gohrif(!defined('MAILHEADER_EOL')) define('MAILHEADER_EOL',"\n"); 15bb01c27cSAndreas Gohr#define('MAILHEADER_ASCIIONLY',1); 16bb01c27cSAndreas Gohr 17bb01c27cSAndreas Gohrclass Mailer { 18bb01c27cSAndreas Gohr 19bb01c27cSAndreas Gohr private $headers = array(); 20bb01c27cSAndreas Gohr private $attach = array(); 21bb01c27cSAndreas Gohr private $html = ''; 22bb01c27cSAndreas Gohr private $text = ''; 23bb01c27cSAndreas Gohr 24bb01c27cSAndreas Gohr private $boundary = ''; 25bb01c27cSAndreas Gohr private $partid = ''; 261d045709SAndreas Gohr private $sendparam= null; 27bb01c27cSAndreas Gohr 281d045709SAndreas Gohr private $validator = null; 291d045709SAndreas Gohr 301d045709SAndreas Gohr /** 311d045709SAndreas Gohr * Constructor 321d045709SAndreas Gohr * 331d045709SAndreas Gohr * Initializes the boundary strings and part counters 341d045709SAndreas Gohr */ 351d045709SAndreas Gohr public function __construct(){ 361d045709SAndreas Gohr if(isset($_SERVER['SERVER_NAME'])){ 371d045709SAndreas Gohr $server = $_SERVER['SERVER_NAME']; 381d045709SAndreas Gohr }else{ 391d045709SAndreas Gohr $server = 'localhost'; 401d045709SAndreas Gohr } 411d045709SAndreas Gohr 421d045709SAndreas Gohr $this->partid = md5(uniqid(rand(),true)).'@'.$server; 43bb01c27cSAndreas Gohr $this->boundary = '----------'.md5(uniqid(rand(),true)); 44bb01c27cSAndreas Gohr } 45bb01c27cSAndreas Gohr 46bb01c27cSAndreas Gohr /** 47bb01c27cSAndreas Gohr * Attach a file 48bb01c27cSAndreas Gohr * 49bb01c27cSAndreas Gohr * @param $path Path to the file to attach 50bb01c27cSAndreas Gohr * @param $mime Mimetype of the attached file 51bb01c27cSAndreas Gohr * @param $name The filename to use 52bb01c27cSAndreas Gohr * @param $embed Unique key to reference this file from the HTML part 53bb01c27cSAndreas Gohr */ 54bb01c27cSAndreas Gohr public function attachFile($path,$mime,$name='',$embed=''){ 55bb01c27cSAndreas Gohr if(!$name){ 56bb01c27cSAndreas Gohr $name = basename($path); 57bb01c27cSAndreas Gohr } 58bb01c27cSAndreas Gohr 59bb01c27cSAndreas Gohr $this->attach[] = array( 60bb01c27cSAndreas Gohr 'data' => file_get_contents($path), 61bb01c27cSAndreas Gohr 'mime' => $mime, 62bb01c27cSAndreas Gohr 'name' => $name, 63bb01c27cSAndreas Gohr 'embed' => $embed 64bb01c27cSAndreas Gohr ); 65bb01c27cSAndreas Gohr } 66bb01c27cSAndreas Gohr 67bb01c27cSAndreas Gohr /** 68bb01c27cSAndreas Gohr * Attach a file 69bb01c27cSAndreas Gohr * 70bb01c27cSAndreas Gohr * @param $path The file contents to attach 71bb01c27cSAndreas Gohr * @param $mime Mimetype of the attached file 72bb01c27cSAndreas Gohr * @param $name The filename to use 73bb01c27cSAndreas Gohr * @param $embed Unique key to reference this file from the HTML part 74bb01c27cSAndreas Gohr */ 75bb01c27cSAndreas Gohr public function attachContent($data,$mime,$name='',$embed=''){ 76bb01c27cSAndreas Gohr if(!$name){ 77bb01c27cSAndreas Gohr list($junk,$ext) = split('/',$mime); 78bb01c27cSAndreas Gohr $name = count($this->attach).".$ext"; 79bb01c27cSAndreas Gohr } 80bb01c27cSAndreas Gohr 81bb01c27cSAndreas Gohr $this->attach[] = array( 82bb01c27cSAndreas Gohr 'data' => $data, 83bb01c27cSAndreas Gohr 'mime' => $mime, 84bb01c27cSAndreas Gohr 'name' => $name, 85bb01c27cSAndreas Gohr 'embed' => $embed 86bb01c27cSAndreas Gohr ); 87bb01c27cSAndreas Gohr } 88bb01c27cSAndreas Gohr 89bb01c27cSAndreas Gohr /** 901d045709SAndreas Gohr * Add an arbitrary header to the mail 911d045709SAndreas Gohr * 92a36fc348SAndreas Gohr * If an empy value is passed, the header is removed 93a36fc348SAndreas Gohr * 941d045709SAndreas Gohr * @param string $header the header name (no trailing colon!) 951d045709SAndreas Gohr * @param string $value the value of the header 961d045709SAndreas Gohr * @param bool $clean remove all non-ASCII chars and line feeds? 971d045709SAndreas Gohr */ 981d045709SAndreas Gohr public function setHeader($header,$value,$clean=true){ 991d045709SAndreas Gohr $header = ucwords(strtolower($header)); // streamline casing 1001d045709SAndreas Gohr if($clean){ 1011d045709SAndreas Gohr $header = preg_replace('/[^\w \-\.\+\@]+/','',$header); 1021d045709SAndreas Gohr $value = preg_replace('/[^\w \-\.\+\@]+/','',$value); 1031d045709SAndreas Gohr } 104a36fc348SAndreas Gohr 105a36fc348SAndreas Gohr // empty value deletes 106a36fc348SAndreas Gohr $value = trim($value); 107a36fc348SAndreas Gohr if($value === ''){ 108a36fc348SAndreas Gohr if(isset($this->headers[$header])) unset($this->headers[$header]); 109a36fc348SAndreas Gohr }else{ 1101d045709SAndreas Gohr $this->headers[$header] = $value; 1111d045709SAndreas Gohr } 112a36fc348SAndreas Gohr } 1131d045709SAndreas Gohr 1141d045709SAndreas Gohr /** 1151d045709SAndreas Gohr * Set additional parameters to be passed to sendmail 1161d045709SAndreas Gohr * 1171d045709SAndreas Gohr * Whatever is set here is directly passed to PHP's mail() command as last 1181d045709SAndreas Gohr * parameter. Depending on the PHP setup this might break mailing alltogether 1191d045709SAndreas Gohr */ 1201d045709SAndreas Gohr public function setParameters($param){ 1211d045709SAndreas Gohr $this->sendparam = $param; 1221d045709SAndreas Gohr } 1231d045709SAndreas Gohr 1241d045709SAndreas Gohr /** 125bb01c27cSAndreas Gohr * Set the HTML part of the mail 126bb01c27cSAndreas Gohr * 127bb01c27cSAndreas Gohr * Placeholders can be used to reference embedded attachments 128bb01c27cSAndreas Gohr */ 1291d045709SAndreas Gohr public function setHTML($html){ 130bb01c27cSAndreas Gohr $this->html = $html; 131bb01c27cSAndreas Gohr } 132bb01c27cSAndreas Gohr 133bb01c27cSAndreas Gohr /** 134bb01c27cSAndreas Gohr * Set the plain text part of the mail 135bb01c27cSAndreas Gohr */ 1361d045709SAndreas Gohr public function setText($text){ 137bb01c27cSAndreas Gohr $this->text = $text; 138bb01c27cSAndreas Gohr } 139bb01c27cSAndreas Gohr 140bb01c27cSAndreas Gohr /** 141a36fc348SAndreas Gohr * Add the To: recipients 142a36fc348SAndreas Gohr * 143a36fc348SAndreas Gohr * @see setAddress 144a36fc348SAndreas Gohr * @param string $address Multiple adresses separated by commas 145a36fc348SAndreas Gohr */ 146a36fc348SAndreas Gohr public function to($address){ 147a36fc348SAndreas Gohr $this->setHeader('To', $address, false); 148a36fc348SAndreas Gohr } 149a36fc348SAndreas Gohr 150a36fc348SAndreas Gohr /** 151a36fc348SAndreas Gohr * Add the Cc: recipients 152a36fc348SAndreas Gohr * 153a36fc348SAndreas Gohr * @see setAddress 154a36fc348SAndreas Gohr * @param string $address Multiple adresses separated by commas 155a36fc348SAndreas Gohr */ 156a36fc348SAndreas Gohr public function cc($address){ 157a36fc348SAndreas Gohr $this->setHeader('Cc', $address, false); 158a36fc348SAndreas Gohr } 159a36fc348SAndreas Gohr 160a36fc348SAndreas Gohr /** 161a36fc348SAndreas Gohr * Add the Bcc: recipients 162a36fc348SAndreas Gohr * 163a36fc348SAndreas Gohr * @see setAddress 164a36fc348SAndreas Gohr * @param string $address Multiple adresses separated by commas 165a36fc348SAndreas Gohr */ 166a36fc348SAndreas Gohr public function bcc($address){ 167a36fc348SAndreas Gohr $this->setHeader('Bcc', $address, false); 168a36fc348SAndreas Gohr } 169a36fc348SAndreas Gohr 170a36fc348SAndreas Gohr /** 171a36fc348SAndreas Gohr * Add the From: address 172a36fc348SAndreas Gohr * 173a36fc348SAndreas Gohr * This is set to $conf['mailfrom'] when not specified so you shouldn't need 174a36fc348SAndreas Gohr * to call this function 175a36fc348SAndreas Gohr * 176a36fc348SAndreas Gohr * @see setAddress 177a36fc348SAndreas Gohr * @param string $address from address 178a36fc348SAndreas Gohr */ 179a36fc348SAndreas Gohr public function from($address){ 180a36fc348SAndreas Gohr $this->setHeader('From', $address, false); 181a36fc348SAndreas Gohr } 182a36fc348SAndreas Gohr 183a36fc348SAndreas Gohr /** 184a36fc348SAndreas Gohr * Add the mail's Subject: header 185a36fc348SAndreas Gohr * 186a36fc348SAndreas Gohr * @param string $subject the mail subject 187a36fc348SAndreas Gohr */ 188a36fc348SAndreas Gohr public function subject($subject){ 189a36fc348SAndreas Gohr $this->headers['Subject'] = $subject; 190a36fc348SAndreas Gohr } 191a36fc348SAndreas Gohr 192a36fc348SAndreas Gohr /** 1931d045709SAndreas Gohr * Sets an email address header with correct encoding 194bb01c27cSAndreas Gohr * 195bb01c27cSAndreas Gohr * Unicode characters will be deaccented and encoded base64 196bb01c27cSAndreas Gohr * for headers. Addresses may not contain Non-ASCII data! 197bb01c27cSAndreas Gohr * 198bb01c27cSAndreas Gohr * Example: 199bb01c27cSAndreas Gohr * setAddress("föö <foo@bar.com>, me@somewhere.com","TBcc"); 200bb01c27cSAndreas Gohr * 201bb01c27cSAndreas Gohr * @param string $address Multiple adresses separated by commas 202a36fc348SAndreas Gohr * @param string returns the prepared header (can contain multiple lines) 203bb01c27cSAndreas Gohr */ 204a36fc348SAndreas Gohr public function cleanAddress($address){ 205bb01c27cSAndreas Gohr // No named recipients for To: in Windows (see FS#652) 206bb01c27cSAndreas Gohr $names = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? false : true; 207bb01c27cSAndreas Gohr 2081d045709SAndreas Gohr $address = preg_replace('/[\r\n\0]+/',' ',$address); // remove attack vectors 2091d045709SAndreas Gohr 210bb01c27cSAndreas Gohr $headers = ''; 211bb01c27cSAndreas Gohr $parts = explode(',',$address); 212bb01c27cSAndreas Gohr foreach ($parts as $part){ 213bb01c27cSAndreas Gohr $part = trim($part); 214bb01c27cSAndreas Gohr 215bb01c27cSAndreas Gohr // parse address 216bb01c27cSAndreas Gohr if(preg_match('#(.*?)<(.*?)>#',$part,$matches)){ 217bb01c27cSAndreas Gohr $text = trim($matches[1]); 218bb01c27cSAndreas Gohr $addr = $matches[2]; 219bb01c27cSAndreas Gohr }else{ 220bb01c27cSAndreas Gohr $addr = $part; 221bb01c27cSAndreas Gohr } 222bb01c27cSAndreas Gohr // skip empty ones 223bb01c27cSAndreas Gohr if(empty($addr)){ 224bb01c27cSAndreas Gohr continue; 225bb01c27cSAndreas Gohr } 226bb01c27cSAndreas Gohr 227bb01c27cSAndreas Gohr // FIXME: is there a way to encode the localpart of a emailaddress? 228bb01c27cSAndreas Gohr if(!utf8_isASCII($addr)){ 229bb01c27cSAndreas Gohr msg(htmlspecialchars("E-Mail address <$addr> is not ASCII"),-1); 230bb01c27cSAndreas Gohr continue; 231bb01c27cSAndreas Gohr } 232bb01c27cSAndreas Gohr 2331d045709SAndreas Gohr if(is_null($this->validator)){ 2341d045709SAndreas Gohr $this->validator = new EmailAddressValidator(); 2351d045709SAndreas Gohr $this->validator->allowLocalAddresses = true; 2361d045709SAndreas Gohr } 2371d045709SAndreas Gohr if(!$this->validator->check_email_address($addr)){ 238bb01c27cSAndreas Gohr msg(htmlspecialchars("E-Mail address <$addr> is not valid"),-1); 239bb01c27cSAndreas Gohr continue; 240bb01c27cSAndreas Gohr } 241bb01c27cSAndreas Gohr 242bb01c27cSAndreas Gohr // text was given 243bb01c27cSAndreas Gohr if(!empty($text) && $names){ 244bb01c27cSAndreas Gohr // add address quotes 245bb01c27cSAndreas Gohr $addr = "<$addr>"; 246bb01c27cSAndreas Gohr 247bb01c27cSAndreas Gohr if(defined('MAILHEADER_ASCIIONLY')){ 248bb01c27cSAndreas Gohr $text = utf8_deaccent($text); 249bb01c27cSAndreas Gohr $text = utf8_strip($text); 250bb01c27cSAndreas Gohr } 251bb01c27cSAndreas Gohr 252bb01c27cSAndreas Gohr if(!utf8_isASCII($text)){ 2531d045709SAndreas Gohr //FIXME check if this is needed for base64 too 254bb01c27cSAndreas Gohr // put the quotes outside as in =?UTF-8?Q?"Elan Ruusam=C3=A4e"?= vs "=?UTF-8?Q?Elan Ruusam=C3=A4e?=" 255bb01c27cSAndreas Gohr /* 256bb01c27cSAndreas Gohr if (preg_match('/^"(.+)"$/', $text, $matches)) { 257bb01c27cSAndreas Gohr $text = '"=?UTF-8?Q?'.mail_quotedprintable_encode($matches[1], 0).'?="'; 258bb01c27cSAndreas Gohr } else { 259bb01c27cSAndreas Gohr $text = '=?UTF-8?Q?'.mail_quotedprintable_encode($text, 0).'?='; 260bb01c27cSAndreas Gohr } 261bb01c27cSAndreas Gohr */ 262bb01c27cSAndreas Gohr $text = '=?UTF-8?B?'.base64_encode($text).'?='; 263bb01c27cSAndreas Gohr } 264bb01c27cSAndreas Gohr }else{ 265bb01c27cSAndreas Gohr $text = ''; 266bb01c27cSAndreas Gohr } 267bb01c27cSAndreas Gohr 268bb01c27cSAndreas Gohr // add to header comma seperated 269bb01c27cSAndreas Gohr if($headers != ''){ 270bb01c27cSAndreas Gohr $headers .= ', '; 271bb01c27cSAndreas Gohr } 272bb01c27cSAndreas Gohr $headers .= $text.' '.$addr; 273bb01c27cSAndreas Gohr } 274bb01c27cSAndreas Gohr 275bb01c27cSAndreas Gohr if(empty($headers)) return false; 276bb01c27cSAndreas Gohr 277bb01c27cSAndreas Gohr return $headers; 278bb01c27cSAndreas Gohr } 279bb01c27cSAndreas Gohr 280bb01c27cSAndreas Gohr 281bb01c27cSAndreas Gohr /** 282bb01c27cSAndreas Gohr * Prepare the mime multiparts for all attachments 283bb01c27cSAndreas Gohr * 284bb01c27cSAndreas Gohr * Replaces placeholders in the HTML with the correct CIDs 285bb01c27cSAndreas Gohr */ 286bb01c27cSAndreas Gohr protected function prepareAttachments(){ 287bb01c27cSAndreas Gohr $mime = ''; 288bb01c27cSAndreas Gohr $part = 1; 289bb01c27cSAndreas Gohr // embedded attachments 290bb01c27cSAndreas Gohr foreach($this->attach as $media){ 291bb01c27cSAndreas Gohr // create content id 292bb01c27cSAndreas Gohr $cid = 'part'.$part.'.'.$this->partid; 293bb01c27cSAndreas Gohr 294bb01c27cSAndreas Gohr // replace wildcards 295bb01c27cSAndreas Gohr if($media['embed']){ 296bb01c27cSAndreas Gohr $this->html = str_replace('%%'.$media['embed'].'%%','cid:'.$cid,$this->html); 297bb01c27cSAndreas Gohr } 298bb01c27cSAndreas Gohr 299bb01c27cSAndreas Gohr $mime .= '--'.$this->boundary.MAILHEADER_EOL; 300bb01c27cSAndreas Gohr $mime .= 'Content-Type: '.$media['mime'].';'.MAILHEADER_EOL; 301bb01c27cSAndreas Gohr $mime .= 'Content-Transfer-Encoding: base64'.MAILHEADER_EOL; 302bb01c27cSAndreas Gohr $mime .= "Content-ID: <$cid>".MAILHEADER_EOL; 303bb01c27cSAndreas Gohr if($media['embed']){ 304bb01c27cSAndreas Gohr $mime .= 'Content-Disposition: inline; filename="'.$media['name'].'"'.MAILHEADER_EOL; 305bb01c27cSAndreas Gohr }else{ 306bb01c27cSAndreas Gohr $mime .= 'Content-Disposition: attachment; filename="'.$media['name'].'"'.MAILHEADER_EOL; 307bb01c27cSAndreas Gohr } 308bb01c27cSAndreas Gohr $mime .= MAILHEADER_EOL; //end of headers 309bb01c27cSAndreas Gohr $mime .= chunk_split(base64_encode($media['data']),74,MAILHEADER_EOL); 310bb01c27cSAndreas Gohr 311bb01c27cSAndreas Gohr $part++; 312bb01c27cSAndreas Gohr } 313bb01c27cSAndreas Gohr return $mime; 314bb01c27cSAndreas Gohr } 315bb01c27cSAndreas Gohr 3161d045709SAndreas Gohr /** 3171d045709SAndreas Gohr * Build the body and handles multi part mails 3181d045709SAndreas Gohr * 3191d045709SAndreas Gohr * Needs to be called before prepareHeaders! 3201d045709SAndreas Gohr * 3211d045709SAndreas Gohr * @return string the prepared mail body, false on errors 3221d045709SAndreas Gohr */ 3231d045709SAndreas Gohr protected function prepareBody(){ 3241d045709SAndreas Gohr global $conf; 3251d045709SAndreas Gohr 326bb01c27cSAndreas Gohr // check for body 327bb01c27cSAndreas Gohr if(!$this->text && !$this->html){ 328bb01c27cSAndreas Gohr return false; 329bb01c27cSAndreas Gohr } 330bb01c27cSAndreas Gohr 331bb01c27cSAndreas Gohr // add general headers 332bb01c27cSAndreas Gohr $this->headers['MIME-Version'] = '1.0'; 333bb01c27cSAndreas Gohr 3341d045709SAndreas Gohr $body = ''; 3351d045709SAndreas Gohr 336bb01c27cSAndreas Gohr if(!$this->html && !count($this->attach)){ // we can send a simple single part message 337bb01c27cSAndreas Gohr $this->headers['Content-Type'] = 'text/plain; charset=UTF-8'; 338bb01c27cSAndreas Gohr $this->headers['Content-Transfer-Encoding'] = 'base64'; 3391d045709SAndreas Gohr $body .= chunk_split(base64_encode($this->text),74,MAILHEADER_EOL); 340bb01c27cSAndreas Gohr }else{ // multi part it is 3411d045709SAndreas Gohr $body .= "This is a multi-part message in MIME format.".MAILHEADER_EOL; 342bb01c27cSAndreas Gohr 343bb01c27cSAndreas Gohr // prepare the attachments 344bb01c27cSAndreas Gohr $attachments = $this->prepareAttachments(); 345bb01c27cSAndreas Gohr 346bb01c27cSAndreas Gohr // do we have alternative text content? 347bb01c27cSAndreas Gohr if($this->text && $this->html){ 348a36fc348SAndreas Gohr $this->headers['Content-Type'] = 'multipart/alternative;'.MAILHEADER_EOL. 349a36fc348SAndreas Gohr ' boundary="'.$this->boundary.'XX"'; 350bb01c27cSAndreas Gohr $body .= '--'.$this->boundary.'XX'.MAILHEADER_EOL; 3511d045709SAndreas Gohr $body .= 'Content-Type: text/plain; charset=UTF-8'.MAILHEADER_EOL; 3521d045709SAndreas Gohr $body .= 'Content-Transfer-Encoding: base64'.MAILHEADER_EOL; 353bb01c27cSAndreas Gohr $body .= MAILHEADER_EOL; 354bb01c27cSAndreas Gohr $body .= chunk_split(base64_encode($this->text),74,MAILHEADER_EOL); 355bb01c27cSAndreas Gohr $body .= '--'.$this->boundary.'XX'.MAILHEADER_EOL; 356a36fc348SAndreas Gohr $body .= 'Content-Type: multipart/related;'.MAILHEADER_EOL. 357a36fc348SAndreas Gohr ' boundary="'.$this->boundary.'"'.MAILHEADER_EOL; 358bb01c27cSAndreas Gohr $body .= MAILHEADER_EOL; 359bb01c27cSAndreas Gohr } 360bb01c27cSAndreas Gohr 3611d045709SAndreas Gohr $body .= '--'.$this->boundary.MAILHEADER_EOL; 3621d045709SAndreas Gohr $body .= 'Content-Type: text/html; charset=UTF-8'.MAILHEADER_EOL; 3631d045709SAndreas Gohr $body .= 'Content-Transfer-Encoding: base64'.MAILHEADER_EOL; 364bb01c27cSAndreas Gohr $body .= MAILHEADER_EOL; 3651d045709SAndreas Gohr $body .= chunk_split(base64_encode($this->html),74,MAILHEADER_EOL); 366bb01c27cSAndreas Gohr $body .= MAILHEADER_EOL; 367bb01c27cSAndreas Gohr $body .= $attachments; 368bb01c27cSAndreas Gohr $body .= '--'.$this->boundary.'--'.MAILHEADER_EOL; 369bb01c27cSAndreas Gohr 370bb01c27cSAndreas Gohr // close open multipart/alternative boundary 371bb01c27cSAndreas Gohr if($this->text && $this->html){ 372bb01c27cSAndreas Gohr $body .= '--'.$this->boundary.'XX--'.MAILHEADER_EOL; 373bb01c27cSAndreas Gohr } 374bb01c27cSAndreas Gohr } 375bb01c27cSAndreas Gohr 376bb01c27cSAndreas Gohr return $body; 377bb01c27cSAndreas Gohr } 378bb01c27cSAndreas Gohr 379bb01c27cSAndreas Gohr /** 380a36fc348SAndreas Gohr * Cleanup and encode the headers array 381a36fc348SAndreas Gohr */ 382a36fc348SAndreas Gohr protected function cleanHeaders(){ 383a36fc348SAndreas Gohr global $conf; 384a36fc348SAndreas Gohr 385a36fc348SAndreas Gohr // clean up addresses 386a36fc348SAndreas Gohr if(empty($this->headers['From'])) $this->from($conf['mailfrom']); 387a36fc348SAndreas Gohr $addrs = array('To','From','Cc','Bcc'); 388a36fc348SAndreas Gohr foreach($addrs as $addr){ 389a36fc348SAndreas Gohr if(isset($this->headers[$addr])){ 390a36fc348SAndreas Gohr $this->headers[$addr] = $this->cleanAddress($this->headers[$addr]); 391a36fc348SAndreas Gohr } 392a36fc348SAndreas Gohr } 393a36fc348SAndreas Gohr 394a36fc348SAndreas Gohr if(isset($subject)){ 395a36fc348SAndreas Gohr // add prefix to subject 396a36fc348SAndreas Gohr if($conf['mailprefix']){ 397a36fc348SAndreas Gohr $prefix = '['.$conf['mailprefix'].']'; 398a36fc348SAndreas Gohr $len = strlen($prefix); 399a36fc348SAndreas Gohr if(substr($this->headers['subject'],0,$len) != $prefix){ 400a36fc348SAndreas Gohr $this->headers['subject'] = $prefix.' '.$this->headers['subject']; 401a36fc348SAndreas Gohr } 402a36fc348SAndreas Gohr } 403a36fc348SAndreas Gohr 404a36fc348SAndreas Gohr // encode subject 405a36fc348SAndreas Gohr if(defined('MAILHEADER_ASCIIONLY')){ 406a36fc348SAndreas Gohr $this->headers['subject'] = utf8_deaccent($this->headers['subject']); 407a36fc348SAndreas Gohr $this->headers['subject'] = utf8_strip($this->headers['subject']); 408a36fc348SAndreas Gohr } 409a36fc348SAndreas Gohr if(!utf8_isASCII($this->headers['Subject'])){ 410a36fc348SAndreas Gohr $subject = '=?UTF-8?B?'.base64_encode($this->headers['Subject']).'?='; 411a36fc348SAndreas Gohr } 412a36fc348SAndreas Gohr } 413a36fc348SAndreas Gohr 414a36fc348SAndreas Gohr // wrap headers 415a36fc348SAndreas Gohr foreach($this->headers as $key => $val){ 416a36fc348SAndreas Gohr $this->headers[$key] = wordwrap($val,78,MAILHEADER_EOL.' '); 417a36fc348SAndreas Gohr } 418a36fc348SAndreas Gohr } 419a36fc348SAndreas Gohr 420a36fc348SAndreas Gohr /** 421bb01c27cSAndreas Gohr * Create a string from the headers array 4221d045709SAndreas Gohr * 4231d045709SAndreas Gohr * @returns string the headers 424bb01c27cSAndreas Gohr */ 425bb01c27cSAndreas Gohr protected function prepareHeaders(){ 426bb01c27cSAndreas Gohr $headers = ''; 427bb01c27cSAndreas Gohr foreach($this->headers as $key => $val){ 428bb01c27cSAndreas Gohr $headers .= "$key: $val".MAILHEADER_EOL; 429bb01c27cSAndreas Gohr } 430bb01c27cSAndreas Gohr return $headers; 431bb01c27cSAndreas Gohr } 432bb01c27cSAndreas Gohr 433bb01c27cSAndreas Gohr /** 434bb01c27cSAndreas Gohr * return a full email with all headers 435bb01c27cSAndreas Gohr * 4361d045709SAndreas Gohr * This is mainly intended for debugging and testing but could also be 4371d045709SAndreas Gohr * used for MHT exports 4381d045709SAndreas Gohr * 4391d045709SAndreas Gohr * @return string the mail, false on errors 440bb01c27cSAndreas Gohr */ 441bb01c27cSAndreas Gohr public function dump(){ 442a36fc348SAndreas Gohr $this->cleanHeaders(); 443bb01c27cSAndreas Gohr $body = $this->prepareBody(); 4441d045709SAndreas Gohr if($body === 'false') return false; 4451d045709SAndreas Gohr $headers = $this->prepareHeaders(); 446bb01c27cSAndreas Gohr 447bb01c27cSAndreas Gohr return $headers.MAILHEADER_EOL.$body; 448bb01c27cSAndreas Gohr } 4491d045709SAndreas Gohr 4501d045709SAndreas Gohr /** 4511d045709SAndreas Gohr * Send the mail 4521d045709SAndreas Gohr * 4531d045709SAndreas Gohr * Call this after all data was set 4541d045709SAndreas Gohr * 455*28d2ad80SAndreas Gohr * @triggers MAIL_MESSAGE_SEND 4561d045709SAndreas Gohr * @return bool true if the mail was successfully passed to the MTA 4571d045709SAndreas Gohr */ 4581d045709SAndreas Gohr public function send(){ 459*28d2ad80SAndreas Gohr $success = false; 460a36fc348SAndreas Gohr 461*28d2ad80SAndreas Gohr // prepare hook data 462*28d2ad80SAndreas Gohr $data = array( 463*28d2ad80SAndreas Gohr // pass the whole mail class to plugin 464*28d2ad80SAndreas Gohr 'mail' => $this, 465*28d2ad80SAndreas Gohr // pass references for backward compatibility 466*28d2ad80SAndreas Gohr 'to' => &$this->headers['To'], 467*28d2ad80SAndreas Gohr 'cc' => &$this->headers['Cc'], 468*28d2ad80SAndreas Gohr 'bcc' => &$this->headers['Bcc'], 469*28d2ad80SAndreas Gohr 'from' => &$this->headers['From'], 470*28d2ad80SAndreas Gohr 'subject' => &$this->headers['Subject'], 471*28d2ad80SAndreas Gohr 'body' => &$this->text, 472*28d2ad80SAndreas Gohr 'params' => &$this->sendparams, 473*28d2ad80SAndreas Gohr 'headers' => '', // plugins shouldn't use this 474*28d2ad80SAndreas Gohr // signal if we mailed successfully to AFTER event 475*28d2ad80SAndreas Gohr 'success' => &$success, 476*28d2ad80SAndreas Gohr ); 477*28d2ad80SAndreas Gohr 478*28d2ad80SAndreas Gohr // do our thing if BEFORE hook approves 479*28d2ad80SAndreas Gohr $evt = new Doku_Event('MAIL_MESSAGE_SEND', $data); 480*28d2ad80SAndreas Gohr if ($evt->advise_before(true)) { 481*28d2ad80SAndreas Gohr // clean up before using the headers 482a36fc348SAndreas Gohr $this->cleanHeaders(); 483a36fc348SAndreas Gohr 4841d045709SAndreas Gohr // any recipients? 4851d045709SAndreas Gohr if(trim($this->headers['To']) === '' && 4861d045709SAndreas Gohr trim($this->headers['Cc']) === '' && 4871d045709SAndreas Gohr trim($this->headers['Bcc']) === '') return false; 4881d045709SAndreas Gohr 4891d045709SAndreas Gohr // The To: header is special 4901d045709SAndreas Gohr if(isset($this->headers['To'])){ 4911d045709SAndreas Gohr $to = $this->headers['To']; 4921d045709SAndreas Gohr unset($this->headers['To']); 4931d045709SAndreas Gohr }else{ 4941d045709SAndreas Gohr $to = ''; 4951d045709SAndreas Gohr } 4961d045709SAndreas Gohr 4971d045709SAndreas Gohr // so is the subject 4981d045709SAndreas Gohr if(isset($this->headers['Subject'])){ 4991d045709SAndreas Gohr $subject = $this->headers['Subject']; 5001d045709SAndreas Gohr unset($this->headers['Subject']); 5011d045709SAndreas Gohr }else{ 5021d045709SAndreas Gohr $subject = ''; 5031d045709SAndreas Gohr } 5041d045709SAndreas Gohr 5051d045709SAndreas Gohr // make the body 5061d045709SAndreas Gohr $body = $this->prepareBody(); 5071d045709SAndreas Gohr if($body === 'false') return false; 5081d045709SAndreas Gohr 5091d045709SAndreas Gohr // cook the headers 5101d045709SAndreas Gohr $headers = $this->prepareHeaders(); 511*28d2ad80SAndreas Gohr // add any headers set by legacy plugins 512*28d2ad80SAndreas Gohr if(trim($data['headers'])){ 513*28d2ad80SAndreas Gohr $headers .= MAILHEADER_EOL.trim($data['headers']); 514*28d2ad80SAndreas Gohr } 5151d045709SAndreas Gohr 5161d045709SAndreas Gohr // send the thing 5171d045709SAndreas Gohr if(is_null($this->sendparam)){ 518*28d2ad80SAndreas Gohr $success = @mail($to,$subject,$body,$headers); 5191d045709SAndreas Gohr }else{ 520*28d2ad80SAndreas Gohr $success = @mail($to,$subject,$body,$headers,$this->sendparam); 5211d045709SAndreas Gohr } 5221d045709SAndreas Gohr } 523*28d2ad80SAndreas Gohr // any AFTER actions? 524*28d2ad80SAndreas Gohr $evt->advise_after(); 525*28d2ad80SAndreas Gohr return $success; 526*28d2ad80SAndreas Gohr } 527bb01c27cSAndreas Gohr} 528