<?php
//
// +--------------------------------------------------------------------------+
// | |
// | XS PHP Library Generic Classes Library |
// | |
// | Copyright (c) 2001-2002 XSPHPLib Group. |
// | |
// +--------------------------------------------------------------------------+
// | |
// | Distributed under the terms of the GNU Lesser General Public License as |
// | published by the Free Software Foundation version 2.1 |
// | See the GNU Lesser General Public License for more details. You should |
// | have received a copy of the GNU Lesser General Public License along with |
// | this package; if not, write to the Free Software Foundation, Inc., |
// | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
// | |
// +--------------------------------------------------------------------------+
// | |
// | Authors: Robert Bala <hide@address.com> |
// | |
// +--------------------------------------------------------------------------+
//
// $Id: mail.inc.php,v 1.1.1.1 2002/11/26 23:00:54 rbala Exp $
/**
* MIME Mail entity class.
*
* MIME Mail class is a wrapper to the Internet mail message. It enables to
* parse received messages from POP3 server, writes new simple or multipart
* messages ready to send throught SMTP connection.
*
* @author Robert Bala <hide@address.com>
* @access public
* @package net
* @version $Id: mail.inc.php,v 1.1.1.1 2002/11/26 23:00:54 rbala Exp $
*/
class Mail extends Mime {
/**
* The Mail message blind carbon copy recipients.
* @access private
* @var string
*/
var $_bcc;
/**
* The Mail message headers.
* @access private
* @var array
*/
var $_headers;
/**
* Mail class constructor.
*
* Creates the new instance of Mail class and sets up basic properties.
*
* @access public
* @param string $mtype the MIME type, defaults to "text".
* @param string $stype the MIME subtype, defaults to "plain".
* @return void
*/
function Mail($mtype='text', $stype='plain') {
Mime::Mime($mtype, $stype);
$this->_bcc = '';
$this->_headers = array();
}
/**
* Gets TO header of the Mail message.
*
* Returns TO header of the Mail message or empty string if it is not set.
*
* @access public
* @return string
*/
function getTo() {
return $this->getHeader('to');
}
/**
* Gets CC header of the Mail message.
*
* Returns CC header of the Mail message or empty string if it is not set.
*
* @access public
* @return string
*/
function getCc() {
return $this->getHeader('cc');
}
/**
* Gets blind carbon copy recipients of the Mail message.
*
* Returns blind carbon copy recipients of the Mail message or empty
* string if it is not set.
*
* @access public
* @return string
*/
function getBcc() {
return $this->_bcc;
}
/**
* Gets FROM header of the Mail message.
*
* Returns FROM header of the Mail message or empty string if it is not set.
*
* @access public
* @return string
*/
function getFrom() {
return $this->getHeader('from');
}
/**
* Gets SUBJECT header of the Mail message.
*
* Returns SUBJECT header of the Mail message or empty string if it is not set.
*
* @access public
* @return string
*/
function getSubject() {
return $this->getHeader('subject');
}
/**
* Gets specified header of the Mail message.
*
* Returns specified header value of the Mail message or empty string if it
* is not set.
*
* @access public
* @return string
*/
function getHeader($header) {
$header = strtolower(trim($header));
if (isset($this->_headers[$header])) {
return $this->_headers[$header];
}
return '';
}
/**
* Sets TO header of the Mail message.
*
* Returns true on success or a error object with an error message on any
* kind of failure. The $addr param can be a list of recipients separated by
* commas. Error object will be returned if specified email address is in
* invalid format.
*
* @access public
* @param string $addr the message recipients.
* @return mixed
*/
function setTo($addr) {
if (is_string($addr) && strlen($addr)) {
$addr = explode(',', trim($addr));
for ($i = 0; $i < count($addr); $i++){
$rcpt = mail_extractAddr($addr[$i]);
if (strlen($rcpt)) {
$addr[$i] = $rcpt;
} else {
return new Error('setto(): Invalid address format.');
}
}
$this->_headers['to'] = implode($addr, ',');
} else {
return new Error('setto(): Invalid address format.');
}
return true;
}
/**
* Sets CC header of the Mail message.
*
* Returns true on success or a error object with an error message on any
* kind of failure. The $addr param can be a list of recipients separated by
* commas. Error object will be returned if specified email address is in
* invalid format.
*
* @access public
* @param string $addr the message recipients.
* @return mixed
*/
function setCc($addr) {
if (is_string($addr) && strlen($addr)) {
$addr = explode(',', trim($addr));
for ($i = 0; $i < count($addr); $i++){
$rcpt = mail_extractAddr($addr[$i]);
if (strlen($rcpt)) {
$addr[$i] = $rcpt;
} else {
return new Error('setcc(): Invalid address format.');
}
}
$this->_headers['cc'] = implode($addr, ',');
} else {
return new Error('setcc(): Invalid address format.');
}
return true;
}
/**
* Sets blind carbon copy recipients of the Mail message.
*
* Returns true on success or a error object with an error message on any
* kind of failure. The $addr param can be a list of recipients separated by
* commas. Error object will be returned if specified email address is in
* invalid format.
*
* @access public
* @param string $addr the message recipients.
* @return mixed
*/
function setBcc($addr) {
if (is_string($addr) && strlen($addr)) {
$addr = explode(',', trim($addr));
for ($i = 0; $i < count($addr); $i++){
$rcpt = mail_extractAddr($addr[$i]);
if (strlen($rcpt)) {
$addr[$i] = $rcpt;
} else {
return new Error('setbcc(): Invalid address format.');
}
}
$this->_bcc = implode($addr, ',');
} else {
return new Error('setbcc(): Invalid address format.');
}
return true;
}
/**
* Sets FROM header of the Mail message.
*
* Returns true on success or a error object with an error message on any
* kind of failure. Error object will be returned if specified email address
* is in invalid format.
*
* @access public
* @param string $addr the message recipients.
* @return mixed
*/
function setFrom($addr) {
if (is_string($addr) && strlen($addr)) {
$addr = mail_extractAddr(trim($addr));
if ($addr == '') {
return new Error('setfrom(): Invalid address format.');
}
$this->_headers['from'] = $addr;
} else {
return new Error('from(): Invalid address format.');
}
return true;
}
/**
* Sets SUBJECT header of the Mail message.
*
* Returns true on success or a error object with an error message on any
* kind of failure.
*
* @access public
* @param string $subject the message subject.
* @return mixed
*/
function setSubject($subject) {
$this->_headers['subject'] = trim($subject);
}
/**
* Sets specified header of the Mail message.
*
* Returns true on success or a error object with an error message on any
* kind of failure.
*
* @access public
* @param string $header the header name.
* @param string $header the header value.
* @return mixed
*/
function setHeader($header, $value) {
$header = strtolower(trim($header));
if (strlen($header)) {
$this->_headers[$header] = $value;
} else {
return new Error('setheader(): Header handle could not be empty.');
}
return true;
}
/**
* Retrieves recipient list of Mail message.
*
* Returns recipient list extracted from TO, CC headers and blind carbon
* copy. Returned email addresses are separated by commas.
*
* @access public
* @return string
*/
function readRcpts() {
$result = $this->getTo();
if (strlen($result) && strlen($this->getCc())) {
$result .= ',' . $this->getCc();
} else {
$result .= $this->getCc();
}
if (strlen($result) && strlen($this->getBcc())) {
$result .= ',' . $this->getBcc();
} else {
$result .= $this->getBcc();
}
return $result;
}
/**
* Retrieves Mail message headers.
*
* Returns Mail message headers.
*
* @access public
* @return string
*/
function mailHeaders() {
global $HTTP_SERVER_VARS;
$result = '';
if (isset($this->_headers['return-path'])) {
$result .= 'Return-Path: ' . $this->_headers['return-path'] . "\r\n";
}
if (isset($this->_headers['message-id'])) {
$result .= 'Message-ID: ' . $this->_headers['message-id'] . "\r\n";
} else {
$maildate = date("r");
$mailhost = $HTTP_SERVER_VARS['SERVER_NAME'];
if (isset($HTTP_SERVER_VARS['REMOTE_HOST'])) {
$result = 'Received: from ' . $HTTP_SERVER_VARS['REMOTE_HOST'] . ' ([' . $HTTP_SERVER_VARS['REMOTE_ADDR'] . "])\r\n";
} else {
$result = 'Received: from ' . $HTTP_SERVER_VARS['REMOTE_ADDR'] . "\r\n";
}
$result .= ' by ' . $mailhost . " (XS PHP Library) with HTTP;\r\n";
$result .= ' ' . $maildate . "\r\n";
$result .= 'Message-ID: <' . uniqid(time()) . '.xsphplib@' . $mailhost .">\r\n";
}
$result .= 'From: ' . $this->getFrom() . "\r\n";
$result .= 'To: ' . $this->getTo() . "\r\n";
if (isset($this->_headers['cc'])) {
$result .= 'Cc: ' . $this->_headers['cc'] . "\r\n";
}
if (isset($this->_headers['reply-to'])) {
$result .= 'Reply-To: ' . $this->_headers['reply-to'] . "\r\n";
}
$result .= 'Subject: ' . $this->getSubject() . "\r\n";
if (isset($this->_headers['date'])) {
$result .= 'Date: ' . $this->_headers['date'] . "\r\n";
} else {
$result .= 'Date: ' . $maildate . "\r\n";
}
if (isset($this->_headers['x-priority'])) {
$result .= 'X-Priority: ' . $this->_headers['x-priority'] . "\r\n";
switch ($this->_headers['x-priority']) {
case 1:
$result .= "Importance: High\r\n";
$result .= "X-MSMail-Priority: High\r\n";
break;
case 3:
$result .= "Importance: Normal\r\n";
$result .= "X-MSMail-Priority: Normal\r\n";
break;
case 5:
$result .= "Importance: Low\r\n";
$result .= "X-MSMail-Priority: Low\r\n";
break;
}
} else {
$result .= "X-Priority: 3\r\n";
$result .= "Importance: Normal\r\n";
$result .= "X-MSMail-Priority: Normal\r\n";
}
if (isset($this->_headers['x-mailer'])) {
$result .= 'X-Mailer: ' . $this->_headers['x-mailer'] . "\r\n";
} else {
$result .= 'X-Mailer: XS PHP Library (PHP' . PHP_VERSION . ")\r\n";
}
if (isset($this->_headers['x-sender'])) {
$result .= 'X-Sender: ' . $this->_headers['x-sender'] . "\r\n";
}
$result .= "MIME-Version: 1.0\r\n";
$result .= $this->mimeHeaders();
return trim($result);
}
/**
* Retrieves Mail message content.
*
* Returns Mail message content. If the Mail message is MIME entity
* multipart type and has subpart objects their headers and message
* bodies are included.
*
* @access public
* @return string
*/
function mailMessage() {
return $this->mimeMessage();
}
/**
* Parses message received from POP3 server.
*
* Parses passed message contents and sets extracted properties.
* Returns true on success, false otherwise.
*
* @access public
* @param string $message the message content to parse.
* @return boolean
*/
function parseMessage($message) {
return mail_parseMime($this, $message);
}
}
/**
* Splits MIME subparts content.
*
* This is helper function used to separat nested mulipart MIME subparts entities.
* Used internally by {@link mail_splitMime()} function.
*
* @author Robert Bala <hide@address.com>
* @access private
* @param object $boundary the MIME subparts boundary.
* @param string $content the message content to split.
* @return string
*/
function mail_fetchMime($boundary, $content) {
if (preg_match_all('/--' . $boundary . '\r\n(.*)\r\n\--' . $boundary . '--/sm', $content, $match)) {
$content = trim($match[1][0]);
}
return $content;
}
/**
* Splits MIME subparts content.
*
* This is helper function used to separat nested mulipart MIME subparts entities.
* Used internally by {@link mail_parseMime()} function.
*
* @author Robert Bala <hide@address.com>
* @access private
* @param object $boundary the MIME subparts boundary.
* @param string $content the message content to split.
* @return array
*/
function mail_splitMime($boundary, $content) {
$block = mail_fetchMime($boundary, $content);
if ($block != $content) {
$parts = explode('--' . $boundary . "\r\n", $block);
for ($i = 0; $i < count($parts); $i++) {
$parts[$i] = trim($parts[$i]);
}
return $parts;
}
return array();
}
/**
* Parses message received from POP3 server.
*
* Parses passed message contents and sets extracted properties to {@link Mail}
* object. Returns true on success, false otherwise.
*
* @author Robert Bala <hide@address.com>
* @access public
* @param object $mpart the reference to {@link Mail} object.
* @param string $content the message content to parse.
* @return boolean
*/
function mail_parseMime(&$mpart, $content) {
if (!is_string($content) && ($content == '')) {
return false;
}
$lines = explode("\r\n", trim($content));
for ($i = 0; $i < count($lines); $i++) {
if ($lines[$i] == '') {
break;
}
if (object_isClass($mpart, 'mail')) {
if (eregi ("^to:(.*)$", $lines[$i], $match)) {
$mpart->_headers['to'] = trim($match[1]);
} elseif (eregi ("^cc:(.*)$", $lines[$i], $match)) {
$mpart->_headers['cc'] = trim($match[1]);
} elseif (eregi ("^from:(.*)$", $lines[$i], $match)) {
$mpart->_headers['from'] = trim($match[1]);
} elseif (eregi ("^subject:(.*)$", $lines[$i], $match)) {
$mpart->_headers['subject'] = trim($match[1]);
} elseif (eregi ("^reply-to:(.*)$", $lines[$i], $match)) {
$mpart->_headers['reply-to'] = trim($match[1]);
} elseif (eregi ("^x-sender:(.*)$", $lines[$i], $match)) {
$mpart->_headers['x-sender'] = trim($match[1]);
} elseif (eregi ("^x-mailer:(.*)$", $lines[$i], $match)) {
$mpart->_headers['x-mailer'] = trim($match[1]);
} elseif (eregi ("^x-priority:(.*)$", $lines[$i], $match)) {
$mpart->_headers['x-priority'] = trim($match[1]);
} elseif (eregi ("^return-path:(.*)$", $lines[$i], $match)) {
$mpart->_headers['return-path'] = trim($match[1]);
} elseif (eregi ("^message-id:(.*)$", $lines[$i], $match)) {
$mpart->_headers['message-id'] = trim($match[1]);
} elseif (eregi ("^date:(.*)$", $lines[$i], $match)) {
$mpart->_headers['date'] = trim($match[1]);
}
}
if (eregi ("^content-type:(.*)$", $lines[$i], $match)) {
$type = strtolower(trim($match[1]));
if ($pos = strpos($type, ";")) {
$type = substr($type, 0, $pos);
}
$type = explode("/", $type);
if (isset($type[0])) {
$mpart->_mtype = trim($type[0]);
}
if (isset($type[1])) {
$mpart->_stype = trim($type[1]);
}
} elseif (eregi("content-transfer-encoding:(.*)$", $lines[$i], $match)) {
$mpart->_encoding = trim($match[1]);
} elseif (eregi("content-disposition:(.*)$", $lines[$i], $match)) {
$disposition = strtolower(trim($match[1]));
if ($pos = strpos($disposition, ";")) {
$disposition = substr($disposition, 0, $pos);
}
$mpart->_disposition = trim($disposition);
} elseif (eregi("content-description:(.*)$", $lines[$i], $match)) {
$mpart->_disposition = trim($match[1]);
}
if (eregi('filename="([^"]+)"', $lines[$i], $match)) {
$mpart->_mimeparams['filename'] = trim($match[1]);
} elseif (eregi('name="([^"]+)"', $lines[$i], $match)) {
$mpart->_mimeparams['name'] = trim($match[1]);
} elseif (eregi('charset="([^"]+)"', $lines[$i], $match)) {
$mpart->_mimeparams['charset'] = trim($match[1]);
} elseif (eregi('boundary="([^"]+)"', $lines[$i], $match)) {
$mpart->_boundary = trim($match[1]);
}
}
if ($mpart->_mtype == 'multipart') {
$lines = mail_splitMime($mpart->_boundary, $content);
for ($i = 0; $i < count($lines); $i++) {
$spart = new Mime;
$sname = 'mp' . count($mpart->_mimeparts);
if (!mail_parseMime($spart, $lines[$i])) {
return false;
}
$mpart->_mimeparts[$sname] = $spart;
}
} elseif (preg_match_all('/\r\n\r\n(.*)/sm', $content, $match)) {
$mpart->decode($match[1][0]);
}
return true;
}
/**
* Extracts email address.
*
* Returns valid email address from the string on success, empty string
* otherwise.
*
* @author Robert Bala <hide@address.com>
* @access public
* @param string $string the string from which email is extracted.
* @return string
*/
function mail_extractAddr($string) {
if (preg_match("/([^<)\s]+@\S+\.[^>(\s]+)/", $string, $match)) {
return '<' . $match[0] . '>';
}
return '';
}
?>