00001 <?php
00010 class helper_email extends object {
00011
00017 protected $attachment = array();
00018
00019 protected function afterInit() {
00020 $this->parseHtml();
00021 }
00022
00029 public function to($addr, $add=false) {
00030 $this->addr($addr, 'to', $add);
00031 }
00032
00039 public function cc($addr, $add=false) {
00040 $this->addr($addr, 'cc', $add);
00041 }
00042
00049 public function bcc($addr, $add=false) {
00050 $this->addr($addr, 'bcc', $add);
00051 }
00052
00060 public function addr($addr, $type, $add=false) {
00061 $tmp = $this->cfg->get($type);
00062 if ($add) {
00063 if (!is_array($tmp))
00064 $tmp = array($tmp);
00065 } else
00066 $tmp = array();
00067 $tmp[] = $addr;
00068 $this->cfg->set($type, $tmp);
00069 }
00070
00080 public function attach($prm) {
00081 if (!is_array($prm))
00082 $prm = array('file'=>$prm);
00083
00084 if (!array_key_exists('file', $prm))
00085 return false;
00086
00087 $ret = false;
00088 if (file::exists($prm['file'])) {
00089 if (!array_key_exists('name', $prm))
00090 $prm['name'] = file::name($prm['file']);
00091 if (!array_key_exists('type', $prm))
00092 $prm['type'] = file::getType($prm['file']);
00093 $this->attachment[] = $prm;
00094 $ret = true;
00095 }
00096 return $ret;
00097 }
00098
00104 public function send() {
00105 if (!is_array($this->cfg->to))
00106 $this->cfg->to = array($this->cfg->to);
00107 if (!is_array($this->cfg->cc))
00108 $this->cfg->cc = array($this->cfg->cc);
00109 if (!is_array($this->cfg->bcc))
00110 $this->cfg->bcc = array($this->cfg->bcc);
00111
00112 if (empty($this->cfg->serverName))
00113 $this->cfg->serverName = request::get('serverName');
00114
00115 $headers = '';
00116
00117 $headers.= $this->headerLine('Return-Path', $this->cfg->from);
00118 $headers.= $this->headerLine('Date', date("D, d M Y G:i:s O"));
00119 $headers.= $this->headerLine('X-Sender', $this->cfg->from);
00120 $headers.= $this->headerLine('Message-ID', '<'.uniqid().'@'.$this->cfg->serverName.'>');
00121 $headers.= $this->headerLine('X-Priority', $this->cfg->priority);
00122 $headers.= $this->headerLine('X-Mailer', $this->cfg->xMailer);
00123 $headers.= $this->headerLine('From', $this->formatAddr(array($this->cfg->from, $this->cfg->fromName)));
00124
00125 if (empty($this->cfg->replyTo))
00126 $this->cfg->replyTo = $this->cfg->from;
00127 $headers.= $this->headerLine('Reply-To', $this->formatAddr($this->cfg->replyTo));
00128
00129 if(!empty($this->cfg->confirmReading))
00130 $headers.= $this->headerLine('Disposition-Notification-To', $this->formatAddr($this->cfg->confirmReading));
00131
00132 if (!empty($this->cfg->cc))
00133 $headers.= $this->headerLine('Cc', $this->headerAddr($this->cfg->cc));
00134
00135 if (!empty($this->cfg->bcc))
00136 $headers.= $this->headerLine('Bcc', $this->headerAddr($this->cfg->bcc));
00137
00138 if (is_array($this->cfg->customHeader))
00139 foreach($this->cfg->customHeader as $k=>$v)
00140 $headers.= $this->headerLine($k, $this->encodeHeader($v));
00141
00142 $headers.= $this->headerLine('MIME-Version', '1.0');
00143
00144 if(empty($this->attachment) && strlen($this->cfg->html) == 0)
00145 $message_type = 'simpleText';
00146 else {
00147 if(strlen($this->cfg->text) > 0 && strlen($this->cfg->html) > 0
00148 && !empty($this->attachment))
00149 $message_type = 'altAttach';
00150 else if(!empty($this->attachment) > 0)
00151 $message_type = 'attach';
00152 else
00153 $message_type = 'alt';
00154 }
00155
00156 switch($message_type) {
00157 case 'simpleText':
00158 $headers.= $this->headerLine('Content-Type', 'text/plain; charset='.$this->cfg->charset);
00159 $headers.= $this->headerLine('Content-Transfer-Encoding', $this->cfg->encoding);
00160 $body = $this->encode($this->cfg->text);
00161 break;
00162 case 'attach':
00163 case 'altAttach':
00164 $boundaryMix = $this->getBoundary();
00165 $headers.= $this->headerLine('Content-Type', 'multipart/mixed; boundary="'.$boundaryMix.'"');
00166
00167
00168 $body = $this->textLine('--'.$boundaryMix);
00169 $body.= $this->getBody();
00170
00171
00172 foreach($this->attachment as $at) {
00173 $body.= $this->textLine('--'.$boundaryMix);
00174 $body.= $this->headerLine('Content-Type', $at['type'].'; name="'.$at['name'].'"');
00175 $body.= $this->headerLine('Content-Transfer-Encoding', $this->cfg->fileEncoding);
00176 $body.= $this->headerLine('Content-Disposition', 'attachment; name="'.$at['name'].'"');
00177 $body.= $this->textLine(null);
00178 $body.= $this->encode(file::read($at['file']), $this->cfg->fileEncoding);
00179 $body.= $this->textLine(null);
00180 }
00181 $body.= $this->textLine('--'.$boundaryMix.'--');
00182 break;
00183 case 'alt':
00184 $body = $this->getBody($headers);
00185 break;
00186 }
00187
00188 $addr = array();
00189 foreach($this->cfg->to as $v)
00190 $addr[] = $this->formatAddr($v);
00191 $to = implode(', ', $addr);
00192
00193 return mail($to, $this->encodeHeader($this->cfg->subject), $body, $headers, $this->cfg->addParam);
00194 }
00195
00201 protected function getBoundary() {
00202 return '_Part_'.md5(uniqid (rand())).'_';
00203 }
00204
00211 protected function quotePrintable($str) {
00212 $ln=strlen($str);
00213 for($w=$e='', $n=0, $l=0, $i=0; $i<$ln; $i++) {
00214 $c = $str[$i];
00215 $o = ord($c);
00216 $en = 0;
00217 switch($o) {
00218 case 9:
00219 case 32:
00220 $w = $c;
00221 $c = '';
00222 break;
00223 case 10:
00224 case 13:
00225 if(strlen($w)) {
00226 if($l+3>75) {
00227 $e.= '='.$this->cfg->crlf;
00228 $l = 0;
00229 }
00230 $e.= sprintf('=%02X', ord($w));
00231 $l+= 3;
00232 $w = '';
00233 }
00234 $e.= $c;
00235 $l = 0;
00236 continue 2;
00237 case 46:
00238 case 70:
00239 case 102:
00240 $en = ($l==0 || $l+1>75);
00241 break;
00242 default:
00243 if ($o>127 || $o<32 || !strcmp($c,'='))
00244 $en = 1;
00245 break;
00246 }
00247 if(strlen($w)) {
00248 if($l+1>75) {
00249 $e.= '='.$this->cfg->crlf;
00250 $l = 0;
00251 }
00252 $e.= $w;
00253 $l++;
00254 $w = '';
00255 }
00256 if(strlen($c)) {
00257 if($en) {
00258 $c = sprintf('=%02X', $o);
00259 $el = 3;
00260 $n = 1;
00261 $b = 1;
00262 }
00263 else
00264 $el=1;
00265 if($l+$el>75) {
00266 $e.= '='.$this->cfg->crlf;
00267 $l = 0;
00268 }
00269 $e.= $c;
00270 $l+= $el;
00271 }
00272 }
00273 if(strlen($w)) {
00274 if($l+3>75)
00275 $e.= '='.$this->cfg->crlf;
00276 $e.= sprintf('=%02X', ord($w));
00277 }
00278 return $e;
00279
00280
00281
00282 $return = '';
00283 $iL = strlen($str);
00284 for($i=0; $i<$iL; $i++) {
00285 $char = $str[$i];
00286 if(ctype_print($char) && !ctype_punct($char))
00287 $return .= $char;
00288 else
00289 $return .= sprintf('=%02X', ord($char));
00290 }
00291 return $return;
00292 return str_replace('%', '=', rawurlencode($str));
00293 }
00294
00295
00302 protected function getBody(&$headers=null) {
00303 $body = null;
00304
00305
00306 $text = $this->cfg->text;
00307
00308 if ($this->cfg->html) {
00309 if (empty($this->cfg->text))
00310
00311 $text = utils::html2Text($this->cfg->html);
00312
00313 $boundary = $this->getBoundary();
00314
00315 if ($headers) {
00316 $headers.= $this->headerLine('Content-Type', 'multipart/alternative; boundary="'.$boundary.'"');
00317
00318 } else {
00319 $body.= $this->headerLine('Content-Type', 'multipart/alternative; boundary="'.$boundary.'"');
00320
00321 $body.= $this->textLine('');
00322 }
00323
00324
00325 $body.= $this->textLine('--'.$boundary);
00326 }
00327 $body.= $this->headerLine('Content-Type', 'text/plain; charset='.$this->cfg->charset.'');
00328
00329 $body.= $this->headerLine('Content-Transfer-Encoding', $this->cfg->encoding);
00330 $body.= $this->headerLine('Content-Disposition', 'inline');
00331 $body.= $this->textLine(null);
00332 $body.= $this->textLine($this->encode($text));
00333
00334 if ($this->cfg->html) {
00335
00336 $body.= $this->textLine('--'.$boundary);
00337
00338 $html = $this->cfg->html;
00339
00340 $inlineImages = false;
00341 if ($this->cfg->htmlInlineImage) {
00342 $rootUri = request::get('rootUri');
00343 preg_match_all('@src="('.$rootUri.'|/)(.+)"@siU', $html, $matches);
00344 if (!empty($matches)) {
00345 $images = array_unique($matches[2]);
00346 $inlineImages = array();
00347 $i = 1;
00348 foreach($images as $img) {
00349 if (file::webExists($img)) {
00350 $file = WEBROOT.str_replace('/', DS, $img);
00351 $cid = 'part'.$i.'.'.$this->getBoundary();
00352 $inlineImages[] = array(
00353 'cid'=>$cid,
00354 'file'=>$file,
00355 'name'=>file::name($file),
00356 'type'=>file::getType($file)
00357 );
00358 $i++;
00359 $html = preg_replace('@src="('.$rootUri.'|/)('.$img.')"@siU', 'src="cid:'.$cid.'"', $html);
00360 }
00361 }
00362 }
00363 }
00364
00365 if (!empty($inlineImages)) {
00366 $boundaryRel = $this->getBoundary();
00367 $body.= $this->headerLine('Content-Type', 'multipart/related; boundary="'.$boundaryRel.'"');
00368
00369 $body.= $this->textLine(null);
00370 $body.= $this->textLine('--'.$boundaryRel);
00371 }
00372
00373 $body.= $this->headerLine('Content-Type', 'text/html; charset='.$this->cfg->charset.'');
00374
00375 $body.= $this->headerLine('Content-Transfer-Encoding', $this->cfg->encoding);
00376 $body.= $this->headerLine('Content-Disposition', 'inline');
00377 $body.= $this->textLine(null);
00378
00379 $body.= $this->textLine($this->encode($html));
00380
00381 if (!empty($inlineImages)) {
00382 foreach($inlineImages as $img) {
00383 $body.= $this->textLine('--'.$boundaryRel);
00384 $body.= $this->headerLine('Content-Type', $img['type'].'; name="'.$img['name'].'"');
00385 $body.= $this->headerLine('Content-Transfer-Encoding', $this->cfg->fileEncoding);
00386 $body.= $this->headerLine('Content-ID', '<'.$img['cid'].'>');
00387 $body.= $this->headerLine('Content-Disposition', 'inline; filename="'.$img['name'].'"');
00388 $body.= $this->textLine(null);
00389 $body.= $this->textLine($this->encode(file::read($img['file']), $this->cfg->fileEncoding));
00390 }
00391 $body.= $this->textLine('--'.$boundaryRel.'--');
00392 $body.= $this->textLine(null);
00393 }
00394
00395 $body.= $this->textLine('--'.$boundary.'--');
00396 $body.= $this->textLine(null);
00397 }
00398
00399 return $body;
00400 }
00401
00409 protected function encode($val, $encoding=null) {
00410 $encoding = is_null($encoding) ? $this->cfg->encoding : $encoding;
00411 switch ($encoding) {
00412 case 'base64';
00413 return chunk_split(base64_encode($val));
00414 }
00415 return $val;
00416 }
00417
00425 protected function headerLine($name, $val) {
00426 return $this->textLine($name.': '.$val);
00427 }
00428
00435 protected function textLine($val) {
00436 return $val.$this->cfg->crlf;
00437 }
00438
00446 protected function headerAddr(array $val) {
00447 $addr = array();
00448 foreach($val as $v)
00449 $addr[] = $this->formatAddr($v);
00450
00451 return implode(', ', $addr);
00452 }
00453
00460 protected function formatAddr($addr) {
00461 if (is_array($addr)) {
00462 if (array_key_exists(1, $addr))
00463 return $this->encodeHeader($addr[1]).' <'.$addr[0].'>';
00464 else
00465 return $addr[0];
00466 } else
00467 return $addr;
00468 }
00469
00477 protected function encodeHeader($val) {
00478 $nb = 0;
00479
00480 if (!preg_match('/[\200-\377]/', $val)) {
00481 return addcslashes($val, "\0..\37\177\\\"");
00482
00483
00484
00485
00486
00487
00488 }
00489 $nb = preg_match_all('/[^\040\041\043-\133\135-\176]/', $val, $matches);
00490
00491 if ($nb == 0)
00492 return $val;
00493
00494 $maxlen = 75;
00495 $maxlen = 75 - 7 - strlen($this->cfg->charset) - $maxlen % 4;
00496 $encoded = trim(chunk_split(base64_encode($val), $maxlen, "\n"));
00497 $encoded = preg_replace('/^(.*)$/m', ' =?'.$this->cfg->charset.'?B?\\1?=', $encoded);
00498 $encoded = trim(str_replace("\n", $this->cfg->crlf, $encoded));
00499
00500 return $encoded;
00501 }
00502
00506 protected function parseHtml() {
00507 if (is_array($this->cfg->html))
00508 $this->cfg->html = utils::render($this->cfg->html);
00509 }
00510
00511 public function __get($name) {
00512 return $this->cfg->get($name);
00513 }
00514
00515 public function __set($name, $val) {
00516 $this->cfg->set($name, $val);
00517 if ($name == 'html')
00518 $this->parseHtml();
00519 }
00520
00521 }