00001 <?php 00018 class axMail { 00019 00024 const HEADER_BCC = "Bcc"; 00025 const HEADER_CC = "Cc"; 00026 const HEADER_CONTENT_DESCRIPTION = "Content-Description"; 00027 const HEADER_CONTENT_TYPE = "Content-Type"; 00028 const HEADER_CONTENT_TRANSFERT_ENCODING = "Content-Transfer-Encoding"; 00029 const HEADER_DATE = "Date"; 00030 const HEADER_FROM = "From"; 00031 const HEADER_MIME_VERSION = "MIME-Version"; 00032 const HEADER_PRIORITY = "Priority"; 00033 const HEADER_REPLY_TO = "Reply-To"; 00034 const HEADER_SENDER = "Sender"; 00035 const HEADER_SUBJECT = "Subject"; 00036 const HEADER_TO = "To"; 00037 const HEADER_X_CONFIRM_READING_TO = "X-Confirm-Reading-To"; 00038 const HEADER_X_MAILER = "X-Mailer"; 00039 const HEADER_X_PRIORITY = "X-Priority"; 00040 const HEADER_X_UNSUBSCRIBE_EMAIL = "X-Unsubscribe-Email"; 00041 const HEADER_X_UNSUBSCRIBE_WEB = "X-Unsubscribe-Web"; 00042 00043 const HEADER_SEPARATOR_CRLF = "\r\n"; 00044 const HEADER_SEPARATOR_LF = "\n"; 00045 00050 public static $allowed_headers = array( 00051 self::HEADER_BCC, self::HEADER_CC, self::HEADER_CONTENT_DESCRIPTION, 00052 self::HEADER_CONTENT_TRANSFERT_ENCODING, self::HEADER_CONTENT_TYPE, self::HEADER_DATE, 00053 self::HEADER_FROM, self::HEADER_MIME_VERSION, self::HEADER_PRIORITY, 00054 self::HEADER_REPLY_TO, self::HEADER_SENDER, self::HEADER_SUBJECT, 00055 self::HEADER_TO, self::HEADER_X_CONFIRM_READING_TO, self::HEADER_X_MAILER, 00056 self::HEADER_X_PRIORITY, self::HEADER_X_UNSUBSCRIBE_WEB, self::HEADER_X_UNSUBSCRIBE_EMAIL 00057 ); 00058 00063 protected $_header_separator = self::HEADER_SEPARATOR_CRLF; 00064 00069 protected $_from; 00070 00075 protected $_to = array(); 00076 00081 protected $_subject; 00082 00087 protected $_message_parts = array(); 00088 00093 protected $_headers = array(); 00094 00114 public function __construct ($from, $to, $subject = "No Subject", $message = null, array $headers = array()) { 00115 if (!self::validateEmail($from)) 00116 throw new InvalidArgumentException('First parameter is expected to be a valid email', 2014); 00117 00118 $this->_from = $from; 00119 00120 $to = (array)$to; 00121 foreach ($to as $destination) 00122 $this->addDestination($destination); 00123 00124 if (!empty($subject)) 00125 $this->setSubject($subject); 00126 00127 if (!empty($message)) 00128 $this->addMessagePart($message); 00129 00130 foreach ($headers as $header => $value) 00131 $this->setHeader($header, $value); 00132 } 00133 00143 public static function validateEmail ($email) { 00144 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { 00145 return false; 00146 } 00147 00148 if (function_exists('checkdnsrr')) { 00149 $host = substr($email, strpos($email, '@') + 1); 00150 return checkdnsrr($host, 'MX'); 00151 } 00152 00153 return true; 00154 } 00155 00163 public function addDestination ($to) { 00164 if (!self::validateEmail($to)) 00165 throw new InvalidArgumentException("Invalid destination", 2015); 00166 00167 if (!array_keys($this->_to, $to)) 00168 $this->_to[] = $to; 00169 } 00170 00176 public function removeDestination ($to) { 00177 foreach (array_keys($this->_to, $to) as $key) 00178 unset($this->_to[$key]); 00179 } 00180 00194 public function setHeader ($header, $value) { 00195 if (!in_array($header, self::$allowed_headers)) 00196 throw new InvalidArgumentException("Invalid Header $header", 2016); 00197 00198 switch ($header) { 00199 case self::HEADER_SENDER: 00200 case self::HEADER_FROM: 00201 case self::HEADER_CC: 00202 case self::HEADER_BCC: 00203 case self::HEADER_REPLY_TO: 00204 case self::HEADER_X_CONFIRM_READING_TO: 00205 case self::HEADER_X_UNSUBSCRIBE_EMAIL: 00206 if (!self::validateEmail($value)) 00207 throw new InvalidArgumentException("Invalid email for $header", 2017); 00208 break; 00209 00210 case self::HEADER_CONTENT_TRANSFERT_ENCODING: 00211 if (!in_array($value, array('7bits', '8bits', 'binary', 'quoted-printable', 'base64', 'ietf-token', 00212 'x-token'))) 00213 throw new InvalidArgumentException("Invalid value for $header", 2018); 00214 break; 00215 00216 case self::HEADER_DATE: 00217 if ($time = strtotime($value)) 00218 $value = date('r', $time); 00219 else 00220 throw new InvalidArgumentException("Invalid date format for $header", 2019); 00221 break; 00222 00223 case self::HEADER_PRIORITY: 00224 if (!in_array($value, array('normal', 'urgent', 'non-urgent'))) 00225 throw new InvalidArgumentException("Invalid priority for $header", 2020); 00226 break; 00227 00228 case self::HEADER_X_UNSUBSCRIBE_WEB: 00229 if (!$value = filter_var($value, FILTER_VALIDATE_URL)) 00230 throw new InvalidArgumentException("Invalid url for $header", 2021); 00231 break; 00232 00233 case self::HEADER_MIME_VERSION: 00234 case self::HEADER_CONTENT_TYPE: 00235 case self::HEADER_CONTENT_DESCRIPTION: 00236 case self::HEADER_REPLY_TO: 00237 case self::HEADER_SENDER: 00238 case self::HEADER_SUBJECT: 00239 case self::HEADER_TO: 00240 case self::HEADER_X_CONFIRM_READING_TO: 00241 case self::HEADER_X_MAILER: 00242 case self::HEADER_X_PRIORITY: 00243 $value = trim($value); 00244 if (empty($value)) 00245 throw new InvalidArgumentException("A non empty value is mandatory for $header", 2022); 00246 break; 00247 } 00248 00249 $this->_headers[$header] = $value; 00250 return $this; 00251 } 00252 00264 public function setHeaderSeparator ($glue) { 00265 if (!in_array($glue, array(self::HEADER_SEPARATOR_CRLF, self::HEADER_SEPARATOR_LF))) 00266 throw new InvalidArgumentException("Unrecognized separator", 2023); 00267 $this->_header_separator = $glue; 00268 return $this; 00269 } 00270 00275 public function getSubject () { 00276 return $this->_subject; 00277 } 00278 00284 public function setSubject ($subject) { 00285 $this->_subject = strip_tags(trim($subject)); 00286 } 00287 00300 public function addMessagePart ($message, $content_type = null, $charset = "utf-8") { 00301 $part = $content_type ? "Content-Type: {$content_type}; charset={$charset}": ""; 00302 $part .= $this->_header_separator; 00303 $part .= $this->_header_separator; 00304 $part .= $message; 00305 $part .= $this->_header_separator; 00306 $this->_message_parts[$key = uniqid("part-")] = $part; 00307 00308 return $key; 00309 } 00310 00324 public function addAttachment ($path, $content_type = null, $filename = null) { 00325 if (!is_file($path)) 00326 throw new axMissingFileException($path, 2024); 00327 00328 if (is_dir($path)) 00329 throw new InvalidArgumentException("First parameter is expected to be regular file, directory given", 2025); 00330 00331 if (!$filename) 00332 $filename = basename($path); 00333 00334 if (function_exists('finfo_open') && $finfo = finfo_open(FILEINFO_MIME_TYPE)) { 00335 $content_type = finfo_file($finfo, $path); 00336 finfo_close($finfo); 00337 } 00338 else if (function_exists('mime_content_type')) 00339 $content_type = mime_content_type($path); 00340 else if (empty($content_type)) 00341 $content_type = "application/octet-stream"; 00342 00343 if (!$content = file_get_contents($path, false)) 00344 throw new RuntimeException("Cannot read $path", 2027); 00345 00346 $part = "Content-Type: $content_type; name=$filename"; 00347 00348 $part .= $this->_header_separator; 00349 $part .= "Content-transfer-encoding: base64"; 00350 $part .= $this->_header_separator; 00351 $part .= $this->_header_separator; 00352 $part .= chunk_split(base64_encode($content)); 00353 $part .= $this->_header_separator; 00354 $this->_message_parts[$key = uniqid("part-")] = $part; 00355 00356 return $key; 00357 } 00358 00364 public function removeMessagePart ($key) { 00365 unset($this->_message_parts[$key]); 00366 } 00367 00374 public function removeAttachement ($key) { 00375 return $this->removeMessagePart($key); 00376 } 00377 00386 public function send () { 00387 if (empty($this->_message_parts)) 00388 throw new RuntimeException("Cannot send mail with empty body"); 00389 00390 $message = (string)$this; 00391 $headers = ""; 00392 $results = array(); 00393 00394 if (empty($message)) 00395 throw new RuntimeException("Cannot send empty mail"); 00396 00397 foreach ($this->_headers as $name => $value) 00398 $headers .= "{$name}: {$value}{$this->_header_separator}"; 00399 $headers .= $this->_header_separator; 00400 00401 foreach ($this->_to as $destination) 00402 $results[$destination] = mail($destination, $this->_subject, $message, $headers); 00403 00404 return $results; 00405 } 00406 00416 public function __toString () { 00417 try { 00418 $c = count($this->_message_parts); 00419 $this->setHeader(self::HEADER_FROM, $this->_from); 00420 00421 if ($c > 1) { 00422 $boundary = md5(uniqid(microtime(), true)); 00423 $this->setHeader(self::HEADER_CONTENT_TYPE, "multipart/mixed;boundary=$boundary"); 00424 $str = "--" . $boundary; 00425 $str .= $this->_header_separator; 00426 $str .= implode("--" . $boundary . $this->_header_separator, $this->_message_parts); 00427 $str .= "--" . $boundary . "--"; 00428 return $str; 00429 } 00430 00431 return $c == 1 ? array_shift($this->_message_parts) : ""; 00432 } 00433 catch (Exception $e) { 00434 return ""; 00435 } 00436 } 00437 } 00438