OK then... current (unfinished) code below. I wanted to make something light yes, but even with a small class like this you can acheive pretty much all the standard things you'd get from your mail client in terms of what you can send.
I don't think.... the way it's laid out I'd say it lends itself to extensions pretty well (some keywords need changing to "protected").
Code: Select all
<?php
/*
dMail: A Flexible PHP Mailer Class.
Author: Chris Corbyn
Date: 29th April 2006
Current functionality:
* Send uses one single connection to the SMTP server
* Doesn't rely on mail()
* Custom Headers
* Sends Multipart messages, handles encoding
* Sends Plain-text single-part emails
* Batch emailing
* Support for attachments
Things to add:
* Basic SMTP Authentication
* SSL (Not sure?)
* Sendmail binary support
-----------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc.,
51 Franklin Street,
Fifth Floor,
Boston,
MA 02110-1301 USA
"Chris Corbyn" <chris@w3style.co.uk>
*/
class mailer
{
private
$server,
$socket,
$port,
$connectTimeout = 30,
$headers = "X-Mailer: dMail by Chris Corbyn\r\n",
$domain = 'dMailUser',
$mimeBoundary,
$mimeWarning,
$parts = array(),
$attachments = array();
public
//Just logging stuff
$errors = array(),
$transactions = array(),
$lastResponse;
public function __construct($server, $port=25)
{
$this->server = $server;
$this->port = $port;
$this->mimeWarning = "This part of the E-mail should never be seen. If\r\n".
"you are reading this, consider upgrading your e-mail\r\n".
"client to a MIME-compatible client.";
}
private function getMimeBoundary($string=false)
{
$force = true;
if (!$string)
{
$force = false;
$string = implode('', $this->parts);
$string .= implode('', $this->attachments);
}
if ($this->mimeBoundary && !$force) return $this->mimeBoundary;
else
{ //Make sure we don't (as if it would ever happen!) -
// produce a hash that's actually in the email already
do
{
$this->mimeBoundary = 'dMail-'.strtoupper(md5($string));
} while(strpos($string, $this->mimeBoundary));
}
return $this->mimeBoundary;
}
public function setDomain($domain)
{
$this->domain = $domain;
}
public function addHeaders($string)
{
$this->headers .= $string;
}
public function setMimeBoundary($string)
{
$this->mimeBoundary = $string;
}
public function setMimeWarning($warning)
{
$this->mimeWarning = $warning;
}
public function setConnectTimeout($secs)
{
$this->connectTimeout = $secs;
}
//When a multipart message has been sent, and the user wants to
// send a different message through the same connection
public function flush($clear_headers=false)
{
$this->parts = array();
$this->attachments = array();
$this->mimeBoundary = null;
if ($clear_headers) $this->headers = "X-Mailer: dMail by Chris Corbyn\r\n";
}
public function connect()
{
$this->socket = @fsockopen($this->server, $this->port, $errno, $errstr, $this->connectTimeout);
if (!$this->socket)
{
$this->logError($errstr, $errno);
return false;
}
//What did the server greet us with on connect?
$this->logTransaction();
//Just being polite
$this->command("HELO {$this->domain}\r\n");
return true;
}
//Dish out the appropriate commands to send an email through the server
public function send($to, $from, $subject, $body=false, $type='text/plain')
{
if (!is_array($to)) $to = array($to);
foreach ($to as $address)
{
$data = $this->buildMail($address, $from, $subject, $body, $type);
foreach ($data as $command)
{
if (!$this->command($command)) return false;
}
}
return true;
}
//Connect errors, timeouts, anything that went wrong but
// wasn't brought up by SMTP itself
private function logError($errstr, $errno=0)
{
$this->errors[] = array(
'num' => $errno,
'time' => microtime(),
'message' => $errstr
);
}
//Keep track of everything that gets said
private function logTransaction($command='')
{
$this->transactions[] = array(
'command' => $command,
'time' => microtime(),
'response' => $this->getResponse()
);
}
//Read back the data from our socket
private function getResponse()
{
if (!$this->socket) return false;
$ret = fgets($this->socket);
$this->lastResponse = $ret;
return $ret;
}
public function command($comm)
{
if (!$this->socket) return false;
//SMTP commands must end with a return
if (substr($comm, -2) != "\r\n") $comm .= "\r\n";
if (@fwrite($this->socket, $comm))
{
$this->logTransaction($comm);
return $this->lastResponse;
}
else return false;
}
//Add a part to a multipart message
public function addPart($string, $type='text/plain', $encoding='7bit')
{
$ret = "Content-Type: $type\r\n".
"Content-Transfer-Encoding: $encoding\r\n\r\n".
$this->encode($string, $encoding);
$this->parts[] = $ret;
}
//Attachments are added as base64 encoded data
public function addAttachment($data, $filename, $type)
{
$ret = "Content-Type: $type; ".
"name="$filename";\r\n".
"Content-Transfer-Encoding: base64\r\n".
"Content-Disposition: attachment;\r\n\r\n".
chunk_split($this->encode($data, 'base64'));
$this->attachments[] = $ret;
}
public function disconnect()
{
if ($this->socket) fclose($this->socket);
}
private function encode($string, $type)
{
$type = strtolower($type);
switch ($type)
{
case 'base64':
$string = base64_encode($string);
break;
//
case '7bit':
default:
break;
//
}
return $string;
}
//Builds the list of commands to send the email
private function buildMail($to, $from, $subject, $body, $type='text/plain')
{
$date = date('r'); //RFC 2822
if ($body) //Ignore multipart messaging
{
$data = "Subject: $subject\r\n".
"From: $from\r\n".
"Reply-To: $from\r\n".
"To: $to\r\n".
"Date: $date\r\n".
"{$this->headers}\r\n".
"Content-Type: $type\r\n".
"Content-Transfer-Encoding: 7bit\r\n\r\n".
"$body";
}
else //Build a multipart message
{
$boundary = $this->getMimeBoundary(); //Overall MIME boundary
$message_body = implode("\r\n\r\n--$boundary\r\n", $this->parts);
if (!empty($this->attachments)) //Make a sub-message that contains attachment data
{
$attachment_boundary = $this->getMimeBoundary(implode('', $this->attachments));
$attachments = implode("\r\n\r\n--$attachment_boundary\r\n", $this->attachments);
$attachments = "\r\n\r\n--$boundary\r\n".
"Content-Type: multipart/alternative;\r\n".
" boundary="$attachment_boundary"\r\n".
"Content-Transfer-Encoding: 7bit\r\n\r\n".
"\r\n\r\n--$attachment_boundary\r\n".
$attachments.
"\r\n--$attachment_boundary--\r\n";
}
$data = "From: $from\r\n".
"Reply-To: $from\r\n".
"To: $to\r\n".
"Subject: $subject\r\n".
"Date: $date\r\n".
"{$this->headers}".
"MIME-Version: 1.0\r\n".
"Content-Type: multipart/mixed;\r\n".
" boundary="{$boundary}"\r\n".
"Content-Transfer-Encoding: 7bit\r\n\r\n";
$data .= $this->mimeWarning;
if (isset($attachments)) $message_body .= $attachments;
$data .= "\r\n\r\n--$boundary\r\n".
"$message_body\r\n".
"\r\n--$boundary--";
}
$data .= "\r\n.\r\n";
$ret = array(
//Can be spoofed but that's not my problem
"mail from: $from\r\n",
//Recipient
"rcpt to: $to\r\n",
//Inform that we're about the give our message
"data\r\n",
//Message body
$data
);
return $ret;
}
}
?>