first commit

This commit is contained in:
2026-04-30 14:38:11 +02:00
commit e22bbde336
1994 changed files with 613950 additions and 0 deletions

2909
system/vendor/Markdown.php vendored Normal file

File diff suppressed because it is too large Load Diff

949
system/vendor/swift/EasySwift.php vendored Normal file
View File

@@ -0,0 +1,949 @@
<?php
/**
* EasySwift: Swift Mailer Facade
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package EasySwift
* @version 1.0.3
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/Swift/ClassLoader.php";
Swift_ClassLoader::load("Swift");
Swift_ClassLoader::load("Swift_Connection_SMTP");
Swift_ClassLoader::load("Swift_Connection_Sendmail");
//Some constants for backwards compatibility with v2 code
if (!defined("SWIFT_TLS")) define("SWIFT_TLS", Swift_Connection_SMTP::ENC_TLS);
if (!defined("SWIFT_SSL")) define("SWIFT_SSL", Swift_Connection_SMTP::ENC_SSL);
if (!defined("SWIFT_OPEN")) define("SWIFT_OPEN", Swift_Connection_SMTP::ENC_OFF);
if (!defined("SWIFT_SECURE_PORT")) define("SWIFT_SECURE_PORT", Swift_Connection_SMTP::PORT_SECURE);
if (!defined("SWIFT_DEFAULT_PORT")) define("SWIFT_DEFAULT_PORT", Swift_Connection_SMTP::PORT_DEFAULT);
/**
* EasySwift: Facade for Swift Mailer Version 3.
* Provides (most of) the API from older versions of Swift, wrapped around the new version 3 API.
* Due to the popularity of the new API, EasySwift will not be around indefinitely.
* @package EasySwift
* @author Chris Corbyn <chris@w3style.co.uk>
* @deprecated
*/
class EasySwift
{
/**
* The instance of Swift this class wrappers
* @var Swift
*/
public $swift = null;
/**
* This value becomes set to true when Swift fails
* @var boolean
*/
public $failed = false;
/**
* The number of loaded plugins
* @var int
*/
protected $pluginCount = 0;
/**
* An instance of Swift_Message
* @var Swift_Message
*/
public $message = null;
/**
* An address list to send to (Cc, Bcc, To..)
* @var Swift_RecipientList
*/
public $recipients = null;
/**
* If all recipients should get the same copy of the message, including headers
* This is already implied if any Cc or Bcc recipients are set
* @var boolean
*/
protected $exactCopy = false;
/**
* If EasySwift should get rid of the message and recipients once it's done sending
* @var boolean
*/
protected $autoFlush = true;
/**
* A list of the IDs of all parts added to the message
* @var array
*/
protected $partIds = array();
/**
* A list of all the IDs of the attachments add to the message
* @var array
*/
protected $attachmentIds = array();
/**
* The last response received from the server
* @var string
*/
public $lastResponse = "";
/**
* The 3 digit code in the last response received from the server
* @var int
*/
public $responseCode = 0;
/**
* The list of errors handled at runtime
* @var array
*/
public $errors = array();
/**
* The last error received
* @var string
*/
public $lastError = null;
/**
* Constructor
* @param Swift_Connection The connection to use
* @param string The domain name of this server (not the SMTP server)
*/
public function __construct(Swift_Connection $connection, $domain=null)
{
try {
$this->swift = new Swift($connection, $domain, Swift::ENABLE_LOGGING);
Swift_ClassLoader::load("Swift_Plugin_EasySwiftResponseTracker");
$this->swift->attachPlugin(new Swift_Plugin_EasySwiftResponseTracker($this), "_ResponseTracker");
} catch (Swift_ConnectionException $e) {
$this->failed = true;
$this->setError("The connection failed to start. An exception was thrown:<br />" . $e->getMessage());
}
$this->newMessage();
$this->newRecipientList();
}
/**
* Set an error message
* @param string Error message
*/
public function setError($msg)
{
$this->errors[] = ($this->lastError = $msg);
}
/**
* Get the full list of errors
* @return array
*/
public function getErrors()
{
return $this->errors;
}
/**
* Get the last error that occured
* @return string
*/
public function getLastError()
{
return $this->lastError;
}
/**
* Clear the current list of errors
*/
public function flushErrors()
{
$this->errors = null;
$this->errors = array();
}
/**
* Turn automatic flsuhing on or off.
* This in ON by deault. It removes the message and all parts after sending.
* @param boolean
*/
public function autoFlush($flush=true)
{
$this->autoFlush = $flush;
}
/**
* Set the maximum size of the log
* @param int
*/
public function setMaxLogSize($size)
{
$log = Swift_LogContainer::getLog();
$log->setMaxSize($size);
}
/**
* Turn logging on or off (saves memory)
* @param boolean
*/
public function useLogging($use=true)
{
$log = Swift_LogContainer::getLog();
if ($use) $log->setLogLevel(Swift_Log::LOG_NETWORK);
else $log->setLogLevel(Swift_Log::LOG_NOTHING);
}
/**
* Enable line resizing (on 1000 by default)
* @param int The number of characters allowed on a line
*/
public function useAutoLineResizing($size=1000)
{
$this->message->setLineWrap($size);
}
/**
* Dump the log contents
* @deprecated
*/
public function getTransactions()
{
return $this->dumpLog();
}
/**
* Dump the contents of the log to the browser
* The log contains some &lt; and &gt; characters so you may need to view source
* Note that this method dumps data to the browser, it does NOT return anything.
*/
public function dumpLog()
{
$log = Swift_LogContainer::getLog();
$log->dump();
}
/**
* This method should be called if you do not wish to send messages in batch mode (i.e. if all recipients should see each others' addresses)
* @param boolean If this mode should be used
*/
public function useExactCopy($bool=true)
{
$this->exactCopy = $bool;
}
/**
* Reset the current message and start a fresh one
*/
public function newMessage($msg=false)
{
if (!$msg) $msg = new Swift_Message();
$this->message = $msg;
$this->partIds = array();
$this->attachmentIds = array();
}
/**
* Clear out all message parts
* @return boolean
*/
public function flushParts()
{
$success = true;
foreach ($this->partIds as $id)
{
try {
$this->message->detach($id);
} catch (Swift_Message_MimeException $e) {
$success = false;
$this->setError("A MIME part failed to detach due to the error:<br />" . $e->getMessage());
}
}
$this->partIds = array();
return $success;
}
/**
* Clear out all attachments
* @return boolean
*/
public function flushAttachments()
{
$success = true;
foreach ($this->attachmentIds as $id)
{
try {
$this->message->detach($id);
} catch (Swift_Message_MimeException $e) {
$success = false;
$this->setError("An attachment failed to detach due to the error:<br />" . $e->getMessage());
}
}
$this->attachmentIds = array();
return $success;
}
/**
* Clear out all message headers
* @deprecated
*/
public function flushHeaders()
{
$this->newMessage();
}
/**
* Reset the current list of recipients and start a new one
*/
public function newRecipientList($list=false)
{
if (!$list) $list = new Swift_RecipientList();
$this->recipients = $list;
}
/**
* Check if Swift has failed or not
* This facade stops processing if so
* @return boolean
*/
public function hasFailed()
{
return $this->failed;
}
/**
* Check if the current connection is open or not
* @return boolean
*/
public function isConnected()
{
return (($this->swift !== null) && $this->swift->connection->isAlive());
}
/**
* Connect to the MTA if not already connected
*/
public function connect()
{
if (!$this->isConnected())
{
try {
$this->swift->connect();
return true;
} catch (Swift_ConnectionException $e) {
$this->failed = true;
$this->setError("Swift failed to run the connection process:<br />" . $e->getMessage());
}
}
return false;
}
/**
* Perform the SMTP greeting process (don't do this unless you understand why you're doing it)
*/
public function handshake()
{
$this->swift->handshake();
}
/**
* Close the connection to the MTA
* @return boolean
*/
public function close()
{
if ($this->isConnected())
{
try {
$this->swift->disconnect();
return true;
} catch (Swift_ConnectionException $e) {
$this->setError("Disconnect failed:<br />" . $e->getMessage());
}
}
return false;
}
/**
* Send a command to Swift and get a response
* @param string The command to send (leave of CRLF)
* @return string
*/
public function command($command)
{
if (substr($command, -2) == "\r\n") $command = substr($command, 0, -2);
try {
$rs = $this->swift->command($command);
return $rs->getString();
} catch (Swift_ConnectionException $e) {
$this->setError("Command failed:<br />" . $e->getMessage());
return false;
}
}
/**
* Add a new plugin to respond to events
* @param Swift_Events_Listener The plugin to load
* @param string The ID to identify the plugin by if needed
* @return string The ID of the plugin
*/
public function loadPlugin(Swift_Events_Listener $plugin, $name=null)
{
$this->pluginCount++;
if (!$name) $name = "p" . $this->pluginCount;
$this->swift->attachPlugin($plugin, $name);
return $name;
}
/**
* Get a reference to the plugin identified by $name
* @param string the ID of the plugin
* @return Swift_Events_Listener
*/
public function getPlugin($name)
{
try {
$plugin = $this->swift->getPlugin($name);
return $plugin;
} catch (Exception $e) {
return null;
}
}
/**
* Remove the plugin identified by $name
* @param string The ID of the plugin
* @return boolean
*/
public function removePlugin($name)
{
try {
$this->swift->removePlugin($name);
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Load in a new authentication mechanism for SMTP
* This needn't be called since Swift will locate any available in Swift/Authenticator/*.php
* @param Swift_Authenticator The authentication mechanism to load
* @throws Exception If the wrong connection is used
*/
public function loadAuthenticator(Swift_Authenticator $auth)
{
if (method_exists($this->swift->connection, "attachAuthenticator"))
{
$this->swift->connection->attachAuthenticator($auth);
}
else throw new Exception("SMTP authentication cannot be used with connection class '" . get_class($this->connection) . "'. Swift_Connection_SMTP is needed");
}
/**
* Authenticate with SMTP authentication
* @param string The SMTP username
* @param string The SMTP password
* @return boolean
* @throws Exception If the wrong connection is used
*/
public function authenticate($username, $password)
{
if (method_exists($this->swift->connection, "runAuthenticators"))
{
try {
$this->swift->connection->runAuthenticators($username, $password, $this->swift);
return true;
} catch (Swift_ConnectionException $e) {
$this->setError("Authentication failed:<br />" . $e->getMessage());
return false;
}
}
else throw new Exception("SMTP authentication cannot be used with connection class '" . get_class($this->connection) . "'. Swift_Connection_SMTP is needed");
}
/**
* Turn a string representation of an email address into a Swift_Address object
* @paramm string The email address
* @return Swift_Address
*/
public function stringToAddress($string)
{
$name = null;
$address = null;
// Foo Bar <foo@bar>
// or: "Foo Bar" <foo@bar>
// or: <foo@bar>
Swift_ClassLoader::load("Swift_Message_Encoder");
if (preg_match("/^\\s*(\"?)(.*?)\\1 *<(" . Swift_Message_Encoder::CHEAP_ADDRESS_RE . ")>\\s*\$/", $string, $matches))
{
if (!empty($matches[2])) $name = $matches[2];
$address = $matches[3];
}
elseif (preg_match("/^\\s*" . Swift_Message_Encoder::CHEAP_ADDRESS_RE . "\\s*\$/", $string))
{
$address = trim($string);
}
else return false;
$swift_address = new Swift_Address($address, $name);
return $swift_address;
}
/**
* Set the encoding used in the message header
* The encoding can be one of Q (quoted-printable) or B (base64)
* @param string The encoding to use
*/
public function setHeaderEncoding($mode="B")
{
switch (strtoupper($mode))
{
case "Q": case "QP": case "QUOTED-PRINTABLE":
$this->message->headers->setEncoding("Q");
break;
default:
$this->message->headers->setEncoding("B");
}
}
/**
* Set the return path address (where bounces go to)
* @param mixed The address as a string or Swift_Address
*/
public function setReturnPath($address)
{
return $this->message->setReturnPath($address);
}
/**
* Request for a read recipient to be sent to the reply-to address
* @param boolean
*/
public function requestReadReceipt($request=true)
{
//$this->message->requestReadReceipt(true);
}
/**
* Set the message priority
* This is an integer between 1 (high) and 5 (low)
* @param int The level of priority to use
*/
public function setPriority($priority)
{
$this->message->setPriority($priority);
}
/**
* Get the return-path address as a string
* @return string
*/
public function getReturnPath()
{
try {
return $this->message->getReturnPath();
} catch (Swift_Message_MimeException $e) {
return false;
}
}
/**
* Set the reply-to header
* @param mixed The address replies come to. String, or Swift_Address, or an array of either.
*/
public function setReplyTo($address)
{
return $this->message->setReplyTo($address);
}
/**
* Get the reply-to address(es) as an array of strings
* @return array
*/
public function getReplyTo()
{
try {
return $this->message->getReplyTo();
} catch (Swift_Message_MimeException $e) {
return false;
}
}
/**
* Add To: recipients to the email
* @param mixed To address(es)
* @return boolean
*/
public function addTo($address)
{
return $this->addRecipients($address, "To");
}
/**
* Get an array of To addresses
* This currently returns an array of Swift_Address objects and may be simplified to an array of strings in later versions
* @return array
*/
public function getToAddresses()
{
return $this->recipients->getTo();
}
/**
* Clear out all To: recipients
*/
public function flushTo()
{
$this->recipients->flushTo();
}
/**
* Add Cc: recipients to the email
* @param mixed Cc address(es)
* @return boolean
*/
public function addCc($address)
{
return $this->addRecipients($address, "Cc");
}
/**
* Get an array of Cc addresses
* This currently returns an array of Swift_Address objects and may be simplified to an array of strings in later versions
* @return array
*/
public function getCcAddresses()
{
return $this->recipients->getCc();
}
/**
* Clear out all Cc: recipients
*/
public function flushCc()
{
$this->recipients->flushCc();
}
/**
* Add Bcc: recipients to the email
* @param mixed Bcc address(es)
* @return boolean
*/
public function addBcc($address)
{
return $this->addRecipients($address, "Bcc");
}
/**
* Get an array of Bcc addresses
* This currently returns an array of Swift_Address objects and may be simplified to an array of strings in later versions
* @return array
*/
public function getBccAddresses()
{
return $this->recipients->getBcc();
}
/**
* Clear out all Bcc: recipients
*/
public function flushBcc()
{
$this->recipients->flushBcc();
}
/**
* Add recipients to the email
* @param mixed Address(es)
* @param string Recipient type (To, Cc, Bcc)
* @return boolean
*/
protected function addRecipients($address, $type)
{
if (!in_array($type, array("To", "Cc", "Bcc"))) return false;
$method = "add" . $type;
if ($address instanceof Swift_Address)
{
$this->recipients->$method($address);
return true;
}
else
{
$added = 0;
foreach ((array)$address as $addr)
{
if (is_array($addr))
{
$addr = array_values($addr);
if (count($addr) >= 2)
{
$this->recipients->$method($addr[0], $addr[1]);
$added++;
continue;
}
elseif (count($addr) == 1) $addr = $addr[0];
else continue;
}
if (is_string($addr))
{
$addr = $this->stringToAddress($addr);
$this->recipients->$method($addr);
$added++;
}
}
return ($added > 0);
}
}
/**
* Flush message, recipients and headers
*/
public function flush()
{
$this->newMessage();
$this->newRecipientList();
}
/**
* Get a list of any addresses which have failed since instantiation
* @return array
*/
public function getFailedRecipients()
{
$log = Swift_LogContainer::getLog();
return $log->getFailedRecipients();
}
/**
* Set the multipart MIME warning message (only seen by old clients)
* @param string The message to show
*/
public function setMimeWarning($text)
{
$this->message->setMimeWarning($text);
}
/**
* Get the currently set MIME warning (seen by old clients)
* @return string
*/
public function getMimeWarning()
{
return $this->message->getMimeWarning();
}
/**
* Set the charset of the charset to use in the message
* @param string The charset (e.g. utf-8, iso-8859-1 etc)
* @return boolean
*/
public function setCharset($charset)
{
try {
$this->message->setCharset($charset);
return true;
} catch (Swift_Message_MimeException $e) {
$this->setError("Unable to set the message charset:<br />" . $e->getMessage());
return false;
}
}
/**
* Get the charset of the charset to use in the message
* @return string
*/
public function getCharset()
{
return $this->message->getCharset();
}
/**
* Add a new MIME part to the message
* @param mixed The part to add. If this is a string it's used as the body. If it's an instance of Swift_Message_Part it's used as the entire part
* @param string Content-type, default text/plain
* @param string The encoding used (default is to let Swift decide)
* @param string The charset to use (default is to let swift decide)
*/
public function addPart($body, $type="text/plain", $encoding=null, $charset=null)
{
if ($body instanceof Swift_Message_Mime)
{
try {
$this->partIds[] = $this->message->attach($body);
} catch (Swift_Message_MimeException $e) {
$this->setError("A MIME part failed to attach:<br />" . $e->getMessage());
return false;
}
}
else
{
try {
$this->partIds[] = $this->message->attach(new Swift_Message_Part($body, $type, $encoding, $charset));
} catch (Swift_Message_MimeException $e) {
$this->setError("A MIME part failed to attach:<br />" . $e->getMessage());
return false;
}
}
}
/**
* Add a new attachment to the message
* @param mixed The attachment to add. If this is a string it's used as the file contents. If it's an instance of Swift_Message_Attachment it's used as the entire part. If it's an instance of Swift_File it's used as the contents.
* @param string Filename, optional
* @param string Content-type. Default application/octet-stream
* @param string The encoding used (default is base64)
* @return boolean
*/
public function addAttachment($data, $filename=null, $type="application/octet-stream", $encoding=null)
{
if ($data instanceof Swift_Message_Mime)
{
try {
$this->attachmentIds[] = $this->message->attach($data);
} catch (Swift_Message_MimeException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
}
}
else
{
try {
$this->attachmentIds[] = $this->message->attach(new Swift_Message_Attachment($data, $filename, $type, $encoding));
} catch (Swift_Message_MimeException $e) {
$this->setError("An attachment failed to attach<br />" . $e->getMessage());
return false;
} catch (Swift_FileException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
}
}
return true;
}
/**
* Embed an image into the message and get the src attribute for HTML
* Returns FALSE on failure
* @param mixed The path to the image, a Swift_Message_Image object or a Swift_File object
* @return string
*/
public function addImage($input)
{
$ret = false;
if ($input instanceof Swift_Message_Image)
{
$ret = $this->message->attach($input);
$this->attachmentIds[] = $ret;
return $ret;
}
elseif ($input instanceof Swift_File)
{
try {
$ret = $this->message->attach(new Swift_Message_Image($input));
$this->attachmentIds[] = $ret;
return $ret;
} catch (Swift_Message_MimeException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
} catch (Swift_FileException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
}
}
else
{
try {
$ret = $this->message->attach(new Swift_Message_Image(new Swift_File($input)));
$this->attachmentIds[] = $ret;
return $ret;
} catch (Swift_Message_MimeException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
} catch (Swift_FileException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
}
}
}
/**
* Embed an inline file into the message, such as a Image or MIDI file
* @param mixed The file contents, Swift_File object or Swift_Message_EmbeddedFile object
* @param string The content-type of the file, optional
* @param string The filename to use, optional
* @param string the Content-ID to use, optional
* @return string
*/
public function embedFile($data, $type="application/octet-stream", $filename=null, $cid=null)
{
$ret = false;
if ($data instanceof Swift_Message_EmbeddedFile)
{
$ret = $this->message->attach($data);
$this->attachmentIds[] = $ret;
return $ret;
}
elseif ($data instanceof Swift_File)
{
try {
$ret = $this->message->attach(new Swift_Message_EmbeddedFile($data, $filename, $type, $cid));
$this->attachmentIds[] = $ret;
return $ret;
} catch (Swift_Message_MimeException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
} catch (Swift_FileException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
}
}
else
{
try {
$ret = $this->message->attach(new Swift_Message_EmbeddedFile($data, $filename, $type, $cid));
$this->attachmentIds[] = $ret;
return $ret;
} catch (Swift_Message_MimeException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
} catch (Swift_FileException $e) {
$this->setError("An attachment failed to attach:<br />" . $e->getMessage());
return false;
}
}
}
/**
* Add headers to the message
* @param string The message headers to append, separated by CRLF
* @deprecated
*/
public function addHeaders($string)
{
//Split at the line ending only if it's not followed by LWSP (as in, a full header)
$headers = preg_split("~\r?\n(?![ \t])~", $string);
foreach ($headers as $header)
{
if (empty($header)) continue;
//Get the bit before the colon
$header_name = substr($header, 0, ($c_pos = strpos($header, ": ")));
// ... and trim it away
$header = substr($header, $c_pos+2);
//Try splitting at "; " for attributes
$attribute_pairs = preg_split("~\\s*;\\s+~", $header);
//The value would always be right after the colon
$header_value = $attribute_pairs[0];
$this->message->headers->set($header_name, $header_value);
unset($attribute_pairs[0]);
foreach ($attribute_pairs as $pair)
{
//Now try finding the attribute name, and it's value (removing quotes)
if (preg_match("~^(.*?)=(\"?)(.*?)\\2\\s*\$~", $pair, $matches))
{
try {
$this->message->headers->setAttribute($header_name, $matches[1], $matches[3]);
} catch (Swift_Message_MimeException $e) {
$this->setError("There was a problem parsing or setting a header attribute:<br />" . $e->getMessage());
//Ignored... it's EasySwift... C'mon ;)
}
}
}
}
}
/**
* Set a header in the message
* @param string The name of the header
* @param string The value of the header (without attributes)
* @see {addHeaderAttribute}
*/
public function setHeader($name, $value)
{
$this->message->headers->set($name, $value);
}
/**
* Set an attribute in the message headers
* For example charset in Content-Type: text/html; charset=utf-8 set by $swift->setHeaderAttribute("Content-Type", "charset", "utf-8")
* @param string The name of the header
* @param string The name of the attribute
* @param string The value of the attribute
*/
public function setHeaderAttribute($name, $attribute, $value)
{
if ($this->message->headers->has($name))
$this->message->headers->setAttribute($name, $attribute, $value);
}
/**
* Send an email to a number of recipients
* Returns the number of successful recipients, or FALSE on failure
* @param mixed The recipients to send to. One of string, array, 2-dimensional array or Swift_Address
* @param mixed The address to send from. string or Swift_Address
* @param string The message subject
* @param string The message body, optional
* @return int
*/
public function send($recipients, $from, $subject, $body=null)
{
$this->addTo($recipients);
$sender = false;
if (is_string($from)) $sender = $this->stringToAddress($from);
elseif ($from instanceof Swift_Address) $sender = $from;
if (!$sender) return false;
$this->message->setSubject($subject);
if ($body) $this->message->setBody($body);
try {
if (!$this->exactCopy && !$this->recipients->getCc() && !$this->recipients->getBcc())
{
$sent = $this->swift->batchSend($this->message, $this->recipients, $sender);
}
else
{
$sent = $this->swift->send($this->message, $this->recipients, $sender);
}
if ($this->autoFlush) $this->flush();
return $sent;
} catch (Swift_ConnectionException $e) {
$this->setError("Sending failed:<br />" . $e->getMessage());
return false;
}
}
}

489
system/vendor/swift/Swift.php vendored Normal file
View File

@@ -0,0 +1,489 @@
<?php
/**
* Swift Mailer Core Component.
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @version 3.3.2
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/Swift/ClassLoader.php";
Swift_ClassLoader::load("Swift_LogContainer");
Swift_ClassLoader::load("Swift_ConnectionBase");
Swift_ClassLoader::load("Swift_BadResponseException");
Swift_ClassLoader::load("Swift_Cache");
Swift_ClassLoader::load("Swift_CacheFactory");
Swift_ClassLoader::load("Swift_Message");
Swift_ClassLoader::load("Swift_RecipientList");
Swift_ClassLoader::load("Swift_BatchMailer");
Swift_ClassLoader::load("Swift_Events");
Swift_ClassLoader::load("Swift_Events_Listener");
/**
* Swift is the central component in the Swift library.
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
* @version 3.3.2
*/
class Swift
{
/**
* The version number.
*/
const VERSION = "3.3.2";
/**
* Constant to flag Swift not to try and connect upon instantiation
*/
const NO_START = 2;
/**
* Constant to tell Swift not to perform the standard SMTP handshake upon connect
*/
const NO_HANDSHAKE = 4;
/**
* Constant to ask Swift to start logging
*/
const ENABLE_LOGGING = 8;
/**
* Constant to prevent postConnect() being run in the connection
*/
const NO_POST_CONNECT = 16;
/**
* The connection object currently active
* @var Swift_Connection
*/
public $connection = null;
/**
* The domain name of this server (should technically be a FQDN)
* @var string
*/
protected $domain = null;
/**
* Flags to change the behaviour of Swift
* @var int
*/
protected $options;
/**
* Loaded plugins, separated into containers according to roles
* @var array
*/
protected $listeners = array();
/**
* Constructor
* @param Swift_Connection The connection object to deal with I/O
* @param string The domain name of this server (the client) as a FQDN
* @param int Optional flags
* @throws Swift_ConnectionException If a connection cannot be established or the connection is behaving incorrectly
*/
public function __construct(Swift_Connection $conn, $domain=false, $options=null)
{
$this->initializeEventListenerContainer();
$this->setOptions($options);
$log = Swift_LogContainer::getLog();
if ($this->hasOption(self::ENABLE_LOGGING) && !$log->isEnabled())
{
$log->setLogLevel(Swift_Log::LOG_NETWORK);
}
if (!$domain) $domain = !empty($_SERVER["SERVER_ADDR"]) ? "[" . $_SERVER["SERVER_ADDR"] . "]" : "localhost.localdomain";
$this->setDomain($domain);
$this->connection = $conn;
if ($conn && !$this->hasOption(self::NO_START))
{
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING)) $log->add("Trying to connect...", Swift_Log::NORMAL);
$this->connect();
}
}
/**
* Populate the listeners array with the defined listeners ready for plugins
*/
protected function initializeEventListenerContainer()
{
Swift_ClassLoader::load("Swift_Events_ListenerMapper");
foreach (Swift_Events_ListenerMapper::getMap() as $interface => $method)
{
if (!isset($this->listeners[$interface]))
$this->listeners[$interface] = array();
}
}
/**
* Add a new plugin to Swift
* Plugins must implement one or more event listeners
* @param Swift_Events_Listener The plugin to load
*/
public function attachPlugin(Swift_Events_Listener $plugin, $id)
{
foreach (array_keys($this->listeners) as $key)
{
$listener = "Swift_Events_" . $key;
Swift_ClassLoader::load($listener);
if ($plugin instanceof $listener) $this->listeners[$key][$id] = $plugin;
}
}
/**
* Get an attached plugin if it exists
* @param string The id of the plugin
* @return Swift_Event_Listener
*/
public function getPlugin($id)
{
foreach ($this->listeners as $type => $arr)
{
if (isset($arr[$id])) return $this->listeners[$type][$id];
}
return null; //If none found
}
/**
* Remove a plugin attached under the ID of $id
* @param string The ID of the plugin
*/
public function removePlugin($id)
{
foreach ($this->listeners as $type => $arr)
{
if (isset($arr[$id]))
{
$this->listeners[$type][$id] = null;
unset($this->listeners[$type][$id]);
}
}
}
/**
* Send a new type of event to all objects which are listening for it
* @param Swift_Events The event to send
* @param string The type of event
*/
public function notifyListeners($e, $type)
{
Swift_ClassLoader::load("Swift_Events_ListenerMapper");
if (!empty($this->listeners[$type]) && $notifyMethod = Swift_Events_ListenerMapper::getNotifyMethod($type))
{
$e->setSwift($this);
foreach ($this->listeners[$type] as $k => $listener)
{
$listener->$notifyMethod($e);
}
}
else $e = null;
}
/**
* Check if an option flag has been set
* @param string Option name
* @return boolean
*/
public function hasOption($option)
{
return ($this->options & $option);
}
/**
* Adjust the options flags
* E.g. $obj->setOptions(Swift::NO_START | Swift::NO_HANDSHAKE)
* @param int The bits to set
*/
public function setOptions($options)
{
$this->options = (int) $options;
}
/**
* Get the current options set (as bits)
* @return int
*/
public function getOptions()
{
return (int) $this->options;
}
/**
* Set the FQDN of this server as it will identify itself
* @param string The FQDN of the server
*/
public function setDomain($name)
{
$this->domain = (string) $name;
}
/**
* Attempt to establish a connection with the service
* @throws Swift_ConnectionException If the connection cannot be established or behaves oddly
*/
public function connect()
{
$this->connection->start();
$greeting = $this->command("", 220);
if (!$this->hasOption(self::NO_HANDSHAKE))
{
$this->handshake($greeting);
}
Swift_ClassLoader::load("Swift_Events_ConnectEvent");
$this->notifyListeners(new Swift_Events_ConnectEvent($this->connection), "ConnectListener");
}
/**
* Disconnect from the MTA
* @throws Swift_ConnectionException If the connection will not stop
*/
public function disconnect()
{
$this->command("QUIT");
$this->connection->stop();
Swift_ClassLoader::load("Swift_Events_DisconnectEvent");
$this->notifyListeners(new Swift_Events_DisconnectEvent($this->connection), "DisconnectListener");
}
/**
* Throws an exception if the response code wanted does not match the one returned
* @param Swift_Event_ResponseEvent The full response from the service
* @param int The 3 digit response code wanted
* @throws Swift_BadResponseException If the code does not match
*/
protected function assertCorrectResponse(Swift_Events_ResponseEvent $response, $codes)
{
$codes = (array)$codes;
if (!in_array($response->getCode(), $codes))
{
$log = Swift_LogContainer::getLog();
$error = "Expected response code(s) [" . implode(", ", $codes) . "] but got response [" . $response->getString() . "]";
if ($log->hasLevel(Swift_Log::LOG_ERRORS)) $log->add($error, Swift_Log::ERROR);
throw new Swift_BadResponseException($error);
}
}
/**
* Have a polite greeting with the server and work out what it's capable of
* @param Swift_Events_ResponseEvent The initial service line respoonse
* @throws Swift_ConnectionException If conversation is not going very well
*/
protected function handshake(Swift_Events_ResponseEvent $greeting)
{
if ($this->connection->getRequiresEHLO() || strpos($greeting->getString(), "ESMTP"))
$this->setConnectionExtensions($this->command("EHLO " . $this->domain, 250));
else $this->command("HELO " . $this->domain, 250);
//Connection might want to do something like authenticate now
if (!$this->hasOption(self::NO_POST_CONNECT)) $this->connection->postConnect($this);
}
/**
* Set the extensions which the service reports in the connection object
* @param Swift_Events_ResponseEvent The list of extensions as reported by the service
*/
protected function setConnectionExtensions(Swift_Events_ResponseEvent $list)
{
$le = (strpos($list->getString(), "\r\n") !== false) ? "\r\n" : "\n";
$list = explode($le, $list->getString());
for ($i = 1, $len = count($list); $i < $len; $i++)
{
$extension = substr($list[$i], 4);
$attributes = split("[ =]", $extension);
$this->connection->setExtension($attributes[0], (isset($attributes[1]) ? array_slice($attributes, 1) : array()));
}
}
/**
* Execute a command against the service and get the response
* @param string The command to execute (leave off any CRLF!!!)
* @param int The code to check for in the response, if any. -1 indicates that no response is wanted.
* @return Swift_Events_ResponseEvent The server's response (could be multiple lines)
* @throws Swift_ConnectionException If a code was expected but does not match the one returned
*/
public function command($command, $code=null)
{
$log = Swift_LogContainer::getLog();
Swift_ClassLoader::load("Swift_Events_CommandEvent");
if ($command !== "")
{
$command_event = new Swift_Events_CommandEvent($command, $code);
$command = null; //For memory reasons
$this->notifyListeners($command_event, "BeforeCommandListener");
if ($log->hasLevel(Swift_Log::LOG_NETWORK) && $code != -1) $log->add($command_event->getString(), Swift_Log::COMMAND);
$end = ($code != -1) ? "\r\n" : null;
$this->connection->write($command_event->getString(), $end);
$this->notifyListeners($command_event, "CommandListener");
}
if ($code == -1) return null;
Swift_ClassLoader::load("Swift_Events_ResponseEvent");
$response_event = new Swift_Events_ResponseEvent($this->connection->read());
$this->notifyListeners($response_event, "ResponseListener");
if ($log->hasLevel(Swift_Log::LOG_NETWORK)) $log->add($response_event->getString(), Swift_Log::RESPONSE);
if ($command !== "" && $command_event->getCode() !== null)
$this->assertCorrectResponse($response_event, $command_event->getCode());
return $response_event;
}
/**
* Reset a conversation which has gone badly
* @throws Swift_ConnectionException If the service refuses to reset
*/
public function reset()
{
$this->command("RSET", 250);
}
/**
* Send a message to any number of recipients
* @param Swift_Message The message to send. This does not need to (and shouldn't really) have any of the recipient headers set.
* @param mixed The recipients to send to. Can be a string, Swift_Address or Swift_RecipientList. Note that all addresses apart from Bcc recipients will appear in the message headers
* @param mixed The address to send the message from. Can either be a string or an instance of Swift_Address.
* @return int The number of successful recipients
* @throws Swift_ConnectionException If sending fails for any reason.
*/
public function send(Swift_Message $message, $recipients, $from)
{
Swift_ClassLoader::load("Swift_Message_Encoder");
if (is_string($recipients) && preg_match("/^" . Swift_Message_Encoder::CHEAP_ADDRESS_RE . "\$/", $recipients))
{
$recipients = new Swift_Address($recipients);
}
elseif (!($recipients instanceof Swift_AddressContainer))
throw new Exception("The recipients parameter must either be a valid string email address, ".
"an instance of Swift_RecipientList or an instance of Swift_Address.");
if (is_string($from) && preg_match("/^" . Swift_Message_Encoder::CHEAP_ADDRESS_RE . "\$/", $from))
{
$from = new Swift_Address($from);
}
elseif (!($from instanceof Swift_Address))
throw new Exception("The sender parameter must either be a valid string email address or ".
"an instance of Swift_Address.");
$log = Swift_LogContainer::getLog();
if (!$message->getEncoding() && !$this->connection->hasExtension("8BITMIME"))
{
$message->setEncoding("QP", true, true);
}
$list = $recipients;
if ($recipients instanceof Swift_Address)
{
$list = new Swift_RecipientList();
$list->addTo($recipients);
}
Swift_ClassLoader::load("Swift_Events_SendEvent");
$send_event = new Swift_Events_SendEvent($message, $list, $from, 0);
$this->notifyListeners($send_event, "BeforeSendListener");
$to = $cc = array();
if (!($has_from = $message->getFrom())) $message->setFrom($from);
if (!($has_return_path = $message->getReturnPath())) $message->setReturnPath($from->build(true));
if (!($has_reply_to = $message->getReplyTo())) $message->setReplyTo($from);
if (!($has_message_id = $message->getId())) $message->generateId();
$this->command("MAIL FROM: " . $message->getReturnPath(true), 250);
$failed = 0;
$sent = 0;
$tmp_sent = 0;
$it = $list->getIterator("to");
while ($it->hasNext())
{
$it->next();
$address = $it->getValue();
$to[] = $address->build();
try {
$this->command("RCPT TO: " . $address->build(true), 250);
$tmp_sent++;
} catch (Swift_BadResponseException $e) {
$failed++;
$send_event->addFailedRecipient($address->getAddress());
if ($log->hasLevel(Swift_Log::LOG_FAILURES)) $log->addfailedRecipient($address->getAddress());
}
}
$it = $list->getIterator("cc");
while ($it->hasNext())
{
$it->next();
$address = $it->getValue();
$cc[] = $address->build();
try {
$this->command("RCPT TO: " . $address->build(true), 250);
$tmp_sent++;
} catch (Swift_BadResponseException $e) {
$failed++;
$send_event->addFailedRecipient($address->getAddress());
if ($log->hasLevel(Swift_Log::LOG_FAILURES)) $log->addfailedRecipient($address->getAddress());
}
}
if ($failed == (count($to) + count($cc)))
{
$this->reset();
$this->notifyListeners($send_event, "SendListener");
return 0;
}
if (!($has_to = $message->getTo()) && !empty($to)) $message->setTo($to);
if (!($has_cc = $message->getCc()) && !empty($cc)) $message->setCc($cc);
$this->command("DATA", 354);
$data = $message->build();
while (false !== $bytes = $data->read())
$this->command($bytes, -1);
if ($log->hasLevel(Swift_Log::LOG_NETWORK)) $log->add("<MESSAGE DATA>", Swift_Log::COMMAND);
try {
$this->command("\r\n.", 250);
$sent += $tmp_sent;
} catch (Swift_BadResponseException $e) {
$failed += $tmp_sent;
}
$tmp_sent = 0;
$has_bcc = $message->getBcc();
$it = $list->getIterator("bcc");
while ($it->hasNext())
{
$it->next();
$address = $it->getValue();
if (!$has_bcc) $message->setBcc($address->build());
try {
$this->command("MAIL FROM: " . $message->getReturnPath(true), 250);
$this->command("RCPT TO: " . $address->build(true), 250);
$this->command("DATA", 354);
$data = $message->build();
while (false !== $bytes = $data->read())
$this->command($bytes, -1);
if ($log->hasLevel(Swift_Log::LOG_NETWORK)) $log->add("<MESSAGE DATA>", Swift_Log::COMMAND);
$this->command("\r\n.", 250);
$sent++;
} catch (Swift_BadResponseException $e) {
$failed++;
$send_event->addFailedRecipient($address->getAddress());
if ($log->hasLevel(Swift_Log::LOG_FAILURES)) $log->addfailedRecipient($address->getAddress());
$this->reset();
}
}
$total = count($to) + count($cc) + count($list->getBcc());
$send_event->setNumSent($sent);
$this->notifyListeners($send_event, "SendListener");
if (!$has_return_path) $message->setReturnPath("");
if (!$has_from) $message->setFrom("");
if (!$has_to) $message->setTo("");
if (!$has_reply_to) $message->setReplyTo(null);
if (!$has_cc) $message->setCc(null);
if (!$has_bcc) $message->setBcc(null);
if (!$has_message_id) $message->setId(null);
if ($log->hasLevel(Swift_Log::LOG_NETWORK)) $log->add("Message sent to " . $sent . "/" . $total . " recipients", Swift_Log::NORMAL);
return $sent;
}
/**
* Send a message to a batch of recipients.
* Unlike send() this method ignores Cc and Bcc recipients and does not reveal every recipients' address in the headers
* @param Swift_Message The message to send (leave out the recipient headers unless you are deliberately overriding them)
* @param Swift_RecipientList The addresses to send to
* @param Swift_Address The address the mail is from (sender)
* @return int The number of successful recipients
*/
public function batchSend(Swift_Message $message, Swift_RecipientList $to, $from)
{
$batch = new Swift_BatchMailer($this);
return $batch->send($message, $to, $from);
}
}

104
system/vendor/swift/Swift/Address.php vendored Normal file
View File

@@ -0,0 +1,104 @@
<?php
/**
* Swift Mailer Address Container (purely for rigid RFC conformance)
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_AddressContainer");
/**
* Swift_Address is just a lone e-mail address reprsented as an object
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Address extends Swift_AddressContainer
{
/**
* The e-mail address portion
* @var string
*/
protected $address = null;
/**
* The personal name part
* @var string
*/
protected $name = null;
/**
* Constructor
* @param string The address portion
* @param string The personal name, optional
*/
public function __construct($address, $name=null)
{
$this->setAddress($address);
if ($name !== null) $this->setName($name);
}
/**
* Set the email address
* @param string
*/
public function setAddress($address)
{
$this->address = trim((string)$address);
}
/**
* Get the address portion
* @return string
*/
public function getAddress()
{
return $this->address;
}
/**
* Set the personal name
* @param string
*/
public function setName($name)
{
if ($name !== null) $this->name = (string) $name;
else $this->name = null;
}
/**
* Get personal name portion
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Build the address the way it should be structured
* @param boolean If the string will be sent to a SMTP server as an envelope
* @return string
*/
public function build($smtp=false)
{
if ($smtp)
{
return "<" . $this->address . ">";
}
else
{
if (($this->name !== null))
{
return $this->name . " <" . $this->address . ">";
}
else return $this->address;
}
}
/**
* PHP's casting conversion
* @return string
*/
public function __toString()
{
return $this->build(true);
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* This is purely here for identify reasons on some subclasses
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_AddressContainer {}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Swift Mailer Authenticator Interface
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Authenticator
* @license GNU Lesser General Public License
*/
/**
* Swift Authenticator Interface
* Lists the methods all authenticators must implement
* @package Swift_Authenticator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Authenticator
{
/**
* Try to authenticate using the username and password
* Returns false on failure
* @param string The username
* @param string The password
* @param Swift The instance of Swift this authenticator is used in
* @return boolean
*/
public function isAuthenticated($username, $password, Swift $instance);
/**
* Return the name of the AUTH extension this is for
* @return string
*/
public function getAuthExtensionName();
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* Swift Mailer PopB4Smtp Authenticator Mechanism
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Authenticator
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Authenticator");
Swift_ClassLoader::load("Swift_LogContainer");
/**
* Swift PopB4Smtp Authenticator
* This form of authentication requires a quick connection to be made with a POP3 server before finally connecting to SMTP
* @package Swift_Authenticator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Authenticator_PopB4Smtp implements Swift_Authenticator
{
protected $connection = null;
/**
* Constructor
* @param mixed Swift_Authenticator_PopB4Smtp_Pop3Connection or string FQDN of POP3 server
* @param int The remote port number
* @param int The level of encryption to use
*/
public function __construct($conn=null, $port=110, $encryption=0)
{
if (is_object($conn)) $this->connection = $conn;
else
{
Swift_ClassLoader::load("Swift_Authenticator_PopB4Smtp_Pop3Connection");
$this->connection = new Swift_Authenticator_PopB4Smtp_Pop3Connection($conn, $port, $encryption);
}
}
/**
* Try to authenticate using the username and password
* Returns false on failure
* @param string The username
* @param string The password
* @param Swift The instance of Swift this authenticator is used in
* @return boolean
*/
public function isAuthenticated($user, $pass, Swift $swift)
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Trying POP3 Before SMTP authentication. Disconnecting from SMTP first.");
}
$swift->disconnect();
try {
$this->connection->start();
$this->connection->assertOk($this->connection->read());
$this->connection->write("USER " . $user);
$this->connection->assertOk($this->connection->read());
$this->connection->write("PASS " . $pass);
$this->connection->assertOk($this->connection->read());
$this->connection->write("QUIT");
$this->connection->assertOk($this->connection->read());
$this->connection->stop();
} catch (Swift_ConnectionException $e) {
if ($log->hasLevel(Swift_Log::LOG_ERRORS))
{
$log->add("POP3 authentication failed.");
}
return false;
}
$options = $swift->getOptions();
$swift->setOptions($options | Swift::NO_POST_CONNECT);
$swift->connect();
$swift->setOptions($options);
return true;
}
/**
* Return the name of the AUTH extension this is for
* @return string
*/
public function getAuthExtensionName()
{
return "*PopB4Smtp";
}
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* Swift Mailer CRAM-MD5 Authenticator Mechanism
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Authenticator
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Authenticator");
/**
* Swift CRAM-MD5 Authenticator
* This form of authentication is a secure challenge-response method
* @package Swift_Authenticator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Authenticator_CRAMMD5 implements Swift_Authenticator
{
/**
* Try to authenticate using the username and password
* Returns false on failure
* @param string The username
* @param string The password
* @param Swift The instance of Swift this authenticator is used in
* @return boolean
*/
public function isAuthenticated($user, $pass, Swift $swift)
{
try {
$encoded_challenge = substr($swift->command("AUTH CRAM-MD5", 334)->getString(), 4);
$challenge = base64_decode($encoded_challenge);
$response = base64_encode($user . " " . self::generateCRAMMD5Hash($pass, $challenge));
$swift->command($response, 235);
} catch (Swift_ConnectionException $e) {
$swift->reset();
return false;
}
return true;
}
/**
* Return the name of the AUTH extension this is for
* @return string
*/
public function getAuthExtensionName()
{
return "CRAM-MD5";
}
/**
* Generate a CRAM-MD5 hash from a challenge
* @param string The string to get a hash from
* @param string The challenge to use to make the hash
* @return string
*/
public static function generateCRAMMD5Hash($password, $challenge)
{
if (strlen($password) > 64)
$password = pack('H32', md5($password));
if (strlen($password) < 64)
$password = str_pad($password, 64, chr(0));
$k_ipad = substr($password, 0, 64) ^ str_repeat(chr(0x36), 64);
$k_opad = substr($password, 0, 64) ^ str_repeat(chr(0x5C), 64);
$inner = pack('H32', md5($k_ipad.$challenge));
$digest = md5($k_opad.$inner);
return $digest;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Swift Mailer LOGIN Authenticator Mechanism
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Authenticator
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Authenticator");
/**
* Swift LOGIN Authenticator
* @package Swift_Authenticator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Authenticator_LOGIN implements Swift_Authenticator
{
/**
* Try to authenticate using the username and password
* Returns false on failure
* @param string The username
* @param string The password
* @param Swift The instance of Swift this authenticator is used in
* @return boolean
*/
public function isAuthenticated($user, $pass, Swift $swift)
{
try {
$swift->command("AUTH LOGIN", 334);
$swift->command(base64_encode($user), 334);
$swift->command(base64_encode($pass), 235);
} catch (Swift_ConnectionException $e) {
$swift->reset();
return false;
}
return true;
}
/**
* Return the name of the AUTH extension this is for
* @return string
*/
public function getAuthExtensionName()
{
return "LOGIN";
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Swift Mailer PLAIN Authenticator Mechanism
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Authenticator
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Authenticator");
/**
* Swift PLAIN Authenticator
* This form of authentication is unbelievably insecure since everything is done plain-text
* @package Swift_Authenticator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Authenticator_PLAIN implements Swift_Authenticator
{
/**
* Try to authenticate using the username and password
* Returns false on failure
* @param string The username
* @param string The password
* @param Swift The instance of Swift this authenticator is used in
* @return boolean
*/
public function isAuthenticated($user, $pass, Swift $swift)
{
try {
//The authorization string uses ascii null as a separator (See RFC 2554)
$credentials = base64_encode($user . chr(0) . $user . chr(0) . $pass);
$swift->command("AUTH PLAIN " . $credentials, 235);
} catch (Swift_ConnectionException $e) {
$swift->reset();
return false;
}
return true;
}
/**
* Return the name of the AUTH extension this is for
* @return string
*/
public function getAuthExtensionName()
{
return "PLAIN";
}
}

View File

@@ -0,0 +1,176 @@
<?php
/**
* Swift Mailer PopB4Smtp Pop3 Connection component
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Authenticator
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../../ClassLoader.php";
/**
* Swift PopB4Smtp Authenticator Connection Component for the POP3 server
* Provides a I/O wrapper for the POP3 connection
* @package Swift_Authenticator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Authenticator_PopB4Smtp_Pop3Connection
{
/**
* Constant for no encyption
*/
const ENC_OFF = 0;
/**
* Constant for SSL encryption
*/
const ENC_SSL = 1;
/**
* The server to connect to (IP or FQDN)
* @var string
*/
protected $server = null;
/**
* The port to connect to
* @var int
*/
protected $port = null;
/**
* The open connection resource from fsockopen()
* @var resource
*/
protected $handle = null;
/**
* Constructor
* @param string The name of the POP3 server
* @param int The port for the POP3 service
* @param int The level of encryption to use
*/
public function __construct($server="localhost", $port=110, $encryption=0)
{
$this->setServer($server);
$this->setPort($port);
$this->setEncryption($encryption);
}
/**
* Set the server name
* @param string The IP or FQDN of the POP3 server
*/
public function setServer($server)
{
$this->server = (string) $server;
}
/**
* Set the port number for the POP3 server
* @param int
*/
public function setPort($port)
{
$this->port = (int) $port;
}
/**
* Get the server name
* @return string
*/
public function getServer()
{
return $this->server;
}
/**
* Get the remote port number
* @return int
*/
public function getPort()
{
return $this->port;
}
/**
* Set the level of enryption to use (see ENC_OFF or ENC_SSL)
* @param int The constant for the encryption level
*/
public function setEncryption($enc)
{
$this->encryption = (int) $enc;
}
/**
* Get the current encryption level set (corresponds to ENC_SSL or ENC_OFF)
* @return int
*/
public function getEncryption()
{
return $this->encryption;
}
/**
* Check if the response is a +OK response
* @throws Swift_ConnectionException Upon bad response
*/
public function assertOk($line)
{
if (substr($line, 0, 3) != "+OK")
{
Swift_ClassLoader::load("Swift_ConnectionException");
throw new Swift_ConnectionException("The POP3 server did not suitably respond with a +OK response. " .
"[" . $line . "]");
}
}
/**
* Try to open the connection
* @throws Swift_ConnectionException If the connection will not start
*/
public function start()
{
$url = $this->getServer();
if ($this->getEncryption() == self::ENC_SSL) $url = "ssl://" . $url;
if ((false === $this->handle = fsockopen($url, $this->getPort(), $errno, $errstr, $timeout)))
{
Swift_ClassLoader::load("Swift_ConnectionException");
throw new Swift_ConnectionException("The POP3 connection failed to start. The error string returned from fsockopen() is [" . $errstr . "] #" . $errno);
}
}
/**
* Try to close the connection
* @throws Swift_ConnectionException If the connection won't close
*/
public function stop()
{
if ($this->handle !== null)
{
if (false === fclose($this->handle))
{
Swift_ClassLoader::load("Swift_ConnectionException");
throw new Swift_ConnectionException("The POP3 connection did not close successfully.");
}
}
$this->handle = null;
}
/**
* Return the unread buffer contents
* @return string
* @throws Swift_ConnectionException If the connection will not allow data to be read
*/
public function read()
{
if (false === $response = fgets($this->handle))
{
Swift_ClassLoader::load("Swift_ConnectionException");
throw new Swift_ConnectionException("Data could not be read from the POP3 connection.");
}
return trim($response);
}
/**
* Write a command to the remote socket
* @param string the command to send (without CRLF)
* @throws Swift_ConnectionException If the command cannot be written
*/
public function write($command)
{
if (false !== fwrite($this->handle, $command . "\r\n"))
{
Swift_ClassLoader::load("Swift_ConnectionException");
throw new Swift_ConnectionException("Data could not be written to the POP3 connection.");
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Swift Bad Response Code Exception
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_ConnectionException");
/**
* Swift Bad Response Exception
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_BadResponseException extends Swift_ConnectionException
{
}

View File

@@ -0,0 +1,229 @@
<?php
/**
* Handles batch mailing with Swift Mailer with fail-safe support.
* Restarts the connection if it dies and then continues where it left off.
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
class Swift_BatchMailer
{
/**
* The current instance of Swift.
* @var Swift
*/
protected $swift;
/**
* The maximum number of times a single recipient can be attempted before giving up.
* @var int
*/
protected $maxTries = 2;
/**
* The number of seconds to sleep for if an error occurs.
* @var int
*/
protected $sleepTime = 0;
/**
* Failed recipients (undeliverable)
* @var array
*/
protected $failed = array();
/**
* The maximum number of successive failures before giving up.
* @var int
*/
protected $maxFails = 0;
/**
* A temporary copy of some message headers.
* @var array
*/
protected $headers = array();
/**
* Constructor.
* @param Swift The current instance of Swift
*/
public function __construct(Swift $swift)
{
$this->setSwift($swift);
}
/**
* Set the current Swift instance.
* @param Swift The instance
*/
public function setSwift(Swift $swift)
{
$this->swift = $swift;
}
/**
* Get the Swift instance which is running.
* @return Swift
*/
public function getSwift()
{
return $this->swift;
}
/**
* Set the maximum number of times a single address is allowed to be retried.
* @param int The maximum number of tries.
*/
public function setMaxTries($max)
{
$this->maxTries = abs($max);
}
/**
* Get the number of times a single address will be attempted in a batch.
* @return int
*/
public function getMaxTries()
{
return $this->maxTries;
}
/**
* Set the amount of time to sleep for if an error occurs.
* @param int Number of seconds
*/
public function setSleepTime($secs)
{
$this->sleepTime = abs($secs);
}
/**
* Get the amount of time to sleep for on errors.
* @return int
*/
public function getSleepTime()
{
return $this->sleepTime;
}
/**
* Log a failed recipient.
* @param string The email address.
*/
public function addFailedRecipient($address)
{
$this->failed[] = $address;
$this->failed = array_unique($this->failed);
}
/**
* Get all recipients which failed in this batch.
* @return array
*/
public function getFailedRecipients()
{
return $this->failed;
}
/**
* Clear out the list of failed recipients.
*/
public function flushFailedRecipients()
{
$this->failed = null;
$this->failed = array();
}
/**
* Set the maximum number of times an error can be thrown in succession and still be hidden.
* @param int
*/
public function setMaxSuccessiveFailures($fails)
{
$this->maxFails = abs($fails);
}
/**
* Get the maximum number of times an error can be thrown and still be hidden.
* @return int
*/
public function getMaxSuccessiveFailures()
{
return $this->maxFails;
}
/**
* Restarts Swift forcibly.
*/
protected function forceRestartSwift()
{
//Pre-empting problems trying to issue "QUIT" to a dead connection
$this->swift->connection->stop();
$this->swift->connection->start();
$this->swift->disconnect();
//Restart swift
$this->swift->connect();
}
/**
* Takes a temporary copy of original message headers in case an error occurs and they need restoring.
* @param Swift_Message The message object
*/
protected function copyMessageHeaders(&$message)
{
$this->headers["To"] = $message->headers->has("To") ?
$message->headers->get("To") : null;
$this->headers["Reply-To"] = $message->headers->has("Reply-To") ?
$message->headers->get("Reply-To") : null;
$this->headers["Return-Path"] = $message->headers->has("Return-Path") ?
$message->headers->get("Return-Path") : null;
$this->headers["From"] = $message->headers->has("From") ?
$message->headers->get("From") : null;
}
/**
* Restore message headers to original values in the event of a failure.
* @param Swift_Message The message
*/
protected function restoreMessageHeaders(&$message)
{
foreach ($this->headers as $name => $value)
{
$message->headers->set($name, $value);
}
}
/**
* Run a batch send in a fail-safe manner.
* This operates as Swift::batchSend() except it deals with errors itself.
* @param Swift_Message To send
* @param Swift_RecipientList Recipients (To: only)
* @param Swift_Address The sender's address
* @return int The number sent to
*/
public function send(Swift_Message $message, Swift_RecipientList $recipients, $sender)
{
$sent = 0;
$successive_fails = 0;
$it = $recipients->getIterator("to");
while ($it->hasNext())
{
$it->next();
$recipient = $it->getValue();
$tried = 0;
$loop = true;
while ($loop && $tried < $this->getMaxTries())
{
try {
$tried++;
$loop = false;
$this->copyMessageHeaders($message);
$sent += ($n = $this->swift->send($message, $recipient, $sender));
if (!$n) $this->addFailedRecipient($recipient->getAddress());
$successive_fails = 0;
} catch (Exception $e) {
$successive_fails++;
$this->restoreMessageHeaders($message);
if (($max = $this->getMaxSuccessiveFailures())
&& $successive_fails > $max)
{
throw new Exception(
"Too many successive failures. BatchMailer is configured to allow no more than " . $max .
" successive failures.");
}
//If an exception was thrown, give it one more go
if ($t = $this->getSleepTime()) sleep($t);
$this->forceRestartSwift();
$loop = true;
}
}
}
return $sent;
}
}

55
system/vendor/swift/Swift/Cache.php vendored Normal file
View File

@@ -0,0 +1,55 @@
<?php
/**
* Swift Mailer Runtime caching base component.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Cache
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
/**
* The interface for any cache mechanisms to follow
* @package Swift_Cache
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_Cache
{
/**
* Append bytes to the cache buffer identified by $key
* @param string The Cache key
* @param string The bytes to append
*/
abstract public function write($key, $data);
/**
* Clear out the buffer for $key
* @param string The cache key
*/
abstract public function clear($key);
/**
* Check if there is something in the cache for $key
* @param string The cache key
* @return boolean
*/
abstract public function has($key);
/**
* Read bytes from the cached buffer and seek forward in the buffer
* Returns false once no more bytes are left to read
* @param int The number of bytes to read (may be ignored)
* @return string
*/
abstract public function read($key, $size=null);
/**
* A factory method to return an output stream object for the relevant location in the cache
* @param string The cache key to fetch the stream for
* @return Swift_Cache_OutputStream
*/
public function getOutputStream($key)
{
Swift_ClassLoader::load("Swift_Cache_OutputStream");
$os = new Swift_Cache_OutputStream($this, $key);
return $os;
}
}

130
system/vendor/swift/Swift/Cache/Disk.php vendored Normal file
View File

@@ -0,0 +1,130 @@
<?php
/**
* Swift Mailer disk runtime cache
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Cache
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Cache");
/**
* Caches data in files on disk - this is the best approach if possible
* @package Swift_Cache
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Cache_Disk extends Swift_Cache
{
/**
* Open file handles
* @var array
*/
protected $open = array();
/**
* The prefix to prepend to files
* @var string
*/
protected $prefix;
/**
* The save path on disk
* @var string
*/
protected static $save_path = "/tmp";
/**
* Ctor
*/
public function __construct()
{
$this->prefix = md5(uniqid(microtime(), true));
}
/**
* Set the save path of the disk - this is a global setting and called statically!
* @param string The path to a writable directory
*/
public static function setSavePath($path="/tmp")
{
self::$save_path = realpath($path);
}
/**
* Write data to the cache
* @param string The cache key
* @param string The data to write
*/
public function write($key, $data)
{
$handle = fopen(self::$save_path . "/" . $this->prefix . $key, "ab");
if (false === fwrite($handle, $data))
{
Swift_ClassLoader::load("Swift_FileException");
throw new Swift_FileException("Disk Caching failed. Tried to write to file at [" .
self::$save_path . "/" . $this->prefix . $key . "] but failed. Check the permissions, or don't use disk caching.");
}
fclose($handle);
}
/**
* Clear the cached data (unlink)
* @param string The cache key
*/
public function clear($key)
{
@unlink(self::$save_path . "/" . $this->prefix . $key);
}
/**
* Check if data is cached for $key
* @param string The cache key
* @return boolean
*/
public function has($key)
{
return file_exists(self::$save_path . "/" . $this->prefix . $key);
}
/**
* Read data from the cache for $key
* @param string The cache key
* @param int The number of bytes to read
* @return string
*/
public function read($key, $size=null)
{
if ($size === null) $size = 8190;
if (!$this->has($key)) return false;
if (!isset($this->open[$key]))
{
$this->open[$key] = fopen(self::$save_path . "/" . $this->prefix . $key, "rb");
}
if (feof($this->open[$key]))
{
fclose($this->open[$key]);
unset($this->open[$key]);
return false;
}
$ret = fread($this->open[$key], $size);
if ($ret !== false)
{
return $ret;
}
else
{
fclose($this->open[$key]);
unset($this->open[$key]);
return false;
}
}
/**
* Dtor.
* Clear out cached data at end of script execution or cache destruction
*/
public function __destruct()
{
$list = glob(self::$save_path . "/" . $this->prefix . "*");
foreach ((array)$list as $f)
{
@unlink($f);
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Swift Mailer Joint Output stream to chain multiple output streams together
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Cache
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Cache_OutputStream");
/**
* Makes multiple output streams act as one super sream
* @package Swift_Cache
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Cache_JointOutputStream extends Swift_Cache_OutputStream
{
/**
* The streams to join
* @var array
*/
protected $streams = array();
/**
* The current stream in use
* @var int
*/
protected $pointer = 0;
/**
* Ctor
* @param array An array of Swift_Cache_OutputStream instances
*/
public function __construct($streams=array())
{
$this->streams = $streams;
}
/**
* Add a new output stream
* @param Swift_Cache_OutputStream
*/
public function addStream(Swift_Cache_OutputStream $stream)
{
$this->streams[] = $stream;
}
/**
* Read data from all streams as if they are one stream
* @param int The number of bytes to read from each stream
* @return string
*/
public function read($size=null)
{
$ret = $this->streams[$this->pointer]->read($size);
if ($ret !== false)
{
return $ret;
}
else
{
if (isset($this->streams[($this->pointer+1)]))
{
$this->pointer++;
return $this->read($size);
}
else
{
$this->pointer = 0;
return false;
}
}
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Swift Mailer Native memory runtime cache
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Cache
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Cache");
/**
* Caches data in variables - uses memory!
* @package Swift_Cache
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Cache_Memory extends Swift_Cache
{
/**
* The storage container for this cache
* @var array
*/
protected $store = array();
/**
* The key which was last requested
* @var string
*/
protected $requested;
/**
* Write data to the cache
* @param string The cache key
* @param string The data to write
*/
public function write($key, $data)
{
if (!isset($this->store[$key])) $this->store[$key] = $data;
else $this->store[$key] .= $data;
}
/**
* Clear the cached data (unset)
* @param string The cache key
*/
public function clear($key)
{
$this->store[$key] = null;
unset($this->store[$key]);
}
/**
* Check if data is cached for $key
* @param string The cache key
* @return boolean
*/
public function has($key)
{
return array_key_exists($key, $this->store);
}
/**
* Read data from the cache for $key
* It makes no difference to memory/speed if data is read in chunks so arguments are ignored
* @param string The cache key
* @return string
*/
public function read($key, $size=null)
{
if (!$this->has($key)) return false;
if ($this->requested == $key)
{
$this->requested = null;
return false;
}
$this->requested = $key;
return $this->store[$key];
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Swift Mailer Output stream to read bytes from cached data
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Cache
* @license GNU Lesser General Public License
*/
/**
* The wraps the streaming functionality of the cache
* @package Swift_Cache
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Cache_OutputStream
{
/**
* The key to read in the actual cache
* @var string
*/
protected $key;
/**
* The cache object to read
* @var Swift_Cache
*/
protected $cache;
/**
* Ctor.
* @param Swift_Cache The cache to read from
* @param string The key for the cached data
*/
public function __construct(Swift_Cache $cache, $key)
{
$this->cache = $cache;
$this->key = $key;
}
/**
* Read bytes from the cache and seek through the buffer
* Returns false if EOF is reached
* @param int The number of bytes to read (could be ignored)
* @return string The read bytes
*/
public function read($size=null)
{
return $this->cache->read($this->key, $size);
}
/**
* Read the entire cached data as one string
* @return string
*/
public function readFull()
{
$ret = "";
while (false !== $bytes = $this->read())
$ret .= $bytes;
return $ret;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Swift Mailer Cache Factory class
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Cache
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
/**
* Makes instances of the cache the user has defined
* @package Swift_Cache
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_CacheFactory
{
/**
* The name of the class which defines the cache
* @var string Case SenSITivE
*/
protected static $className = "Swift_Cache_Memory";
/**
* Set the name of the class which is supposed to be used
* This also includes the file
* @param string The class name
*/
public static function setClassName($name)
{
Swift_ClassLoader::load($name);
self::$className = $name;
}
/**
* Return a new instance of the cache object
* @return Swift_Cache
*/
public static function getCache()
{
$className = self::$className;
Swift_ClassLoader::load($className);
$instance = new $className();
return $instance;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Swift Mailer Class Loader for includes
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
if (!defined("SWIFT_ABS_PATH")) define("SWIFT_ABS_PATH", dirname(__FILE__) . "/..");
/**
* Locates and includes class files
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_ClassLoader
{
/**
* A list of files already located
* @var array
*/
protected static $located = array();
/**
* Load a new class into memory
* @param string The name of the class, case SenSItivE
*/
public static function load($name)
{
if (in_array($name, self::$located) || class_exists($name, false) || interface_exists($name, false))
return;
require_once SWIFT_ABS_PATH . "/" . str_replace("_", "/", $name) . ".php";
self::$located[] = $name;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Swift Mailer Connection Interface
* All connection handlers extend this abstract class
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
/**
* Swift Connection Interface
* Lists methods which are required by any connections
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Connection
{
/**
* Try to start the connection
* @throws Swift_ConnectionException If the connection cannot be started
*/
public function start();
/**
* Return the contents of the buffer
* @return string
* @throws Swift_ConnectionException If the buffer cannot be read
*/
public function read();
/**
* Write a command to the buffer
* @param string The command to send
* @throws Swift_ConnectionException If the write fails
*/
public function write($command, $end="\r\n");
/**
* Try to stop the connection
* @throws Swift_ConnectionException If the connection cannot be closed/stopped
*/
public function stop();
/**
* Check if the connection is up or not
* @return boolean
*/
public function isAlive();
/**
* Add an extension which is available on this connection
* @param string The name of the extension
* @param array The list of attributes for the extension
*/
public function setExtension($name, $list=array());
/**
* Check if an extension exists by the name $name
* @param string The name of the extension
* @return boolean
*/
public function hasExtension($name);
/**
* Get the list of attributes for the extension $name
* @param string The name of the extension
* @return array
* @throws Swift_ConnectionException If no such extension can be found
*/
public function getAttributes($name);
/**
* Execute logic needed after SMTP greetings
* @param Swift An instance of Swift
*/
public function postConnect(Swift $instance);
/**
* Returns TRUE if the connection needs a EHLO greeting.
* @return boolean
*/
public function getRequiresEHLO();
/**
* Set if the connection needs a EHLO greeting.
* @param boolean
*/
public function setRequiresEHLO($set);
}

View File

@@ -0,0 +1,161 @@
<?php
/**
* Swift Mailer Multiple Redundant Connection component.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_ConnectionBase");
/**
* Swift Multi Connection
* Tries to connect to a number of connections until one works successfully
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Connection_Multi extends Swift_ConnectionBase
{
/**
* The list of available connections
* @var array
*/
protected $connections = array();
/**
* The id of the active connection
* @var string
*/
protected $active = null;
/**
* Constructor
*/
public function __construct($connections=array())
{
foreach ($connections as $id => $conn)
{
$this->addConnection($connections[$id], $id);
}
}
/**
* Add a connection to the list of options
* @param Swift_Connection An instance of the connection
* @param string An ID to assign to the connection
*/
public function addConnection(Swift_Connection $connection, $id=null)
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Adding new connection of type '" . get_class($connection) . "' to the multi-redundant connection.");
}
if ($id !== null) $this->connections[$id] = $connection;
else $this->connections[] = $connection;
}
/**
* Read a full response from the buffer
* @return string
* @throws Swift_ConnectionException Upon failure to read
*/
public function read()
{
if ($this->active === null)
{
throw new Swift_ConnectionException("None of the connections set have been started");
}
return $this->connections[$this->active]->read();
}
/**
* Write a command to the server (leave off trailing CRLF)
* @param string The command to send
* @throws Swift_ConnectionException Upon failure to write
*/
public function write($command, $end="\r\n")
{
if ($this->active === null)
{
throw new Swift_ConnectionException("None of the connections set have been started");
}
return $this->connections[$this->active]->write($command, $end);
}
/**
* Try to start the connection
* @throws Swift_ConnectionException Upon failure to start
*/
public function start()
{
$log = Swift_LogContainer::getLog();
$fail_messages = array();
foreach ($this->connections as $id => $conn)
{
try {
$this->connections[$id]->start();
if ($this->connections[$id]->isAlive())
{
$this->active = $id;
return true;
}
else
{
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Connection (" . $id . ") failed. Will try next connection if available.");
}
throw new Swift_ConnectionException("The connection started but reported that it was not active");
}
} catch (Swift_ConnectionException $e) {
$fail_messages[] = $id . ": " . $e->getMessage();
}
}
$failure = implode("<br />", $fail_messages);
throw new Swift_ConnectionException($failure);
}
/**
* Try to close the connection
* @throws Swift_ConnectionException Upon failure to close
*/
public function stop()
{
if ($this->active !== null) $this->connections[$this->active]->stop();
$this->active = null;
}
/**
* Check if the current connection is alive
* @return boolean
*/
public function isAlive()
{
return (($this->active !== null) && $this->connections[$this->active]->isAlive());
}
/**
* Call the current connection's postConnect() method
*/
public function postConnect(Swift $instance)
{
$this->connections[$this->active]->postConnect($instance);
}
/**
* Call the current connection's setExtension() method
*/
public function setExtension($extension, $attributes=array())
{
$this->connections[$this->active]->setExtension($extension, $attributes);
}
/**
* Call the current connection's hasExtension() method
*/
public function hasExtension($name)
{
return $this->connections[$this->active]->hasExtension($name);
}
/**
* Call the current connection's getAttributes() method
*/
public function getAttributes($name)
{
return $this->connections[$this->active]->getAttributes($name);
}
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* Swift Mailer mail() connection component
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_ConnectionBase");
Swift_ClassLoader::load("Swift_Plugin_MailSend");
/**
* Swift mail() Connection
* NOTE: This class is nothing more than a stub. The MailSend plugin does the actual sending.
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Connection_NativeMail extends Swift_ConnectionBase
{
/**
* The response the stub will be giving next
* @var string Response
*/
protected $response = "220 Stubbed";
/**
* The 5th parameter in mail() is a sprintf() formatted string.
* @var string
*/
protected $pluginParams;
/**
* An instance of the MailSend plugin.
* @var Swift_Plugin_MailSend
*/
protected $plugin = null;
/**
* Ctor.
* @param string The 5th parameter in mail() as a sprintf() formatted string where %s is the sender address. This only comes into effect if safe_mode is OFF.
*/
public function __construct($additional_params="-oi -f %s")
{
$this->setAdditionalMailParams($additional_params);
}
/**
* Sets the MailSend plugin in Swift once Swift has connected
* @param Swift The current instance of Swift
*/
public function postConnect(Swift $instance)
{
$this->plugin = new Swift_Plugin_MailSend($this->getAdditionalMailParams());
$instance->attachPlugin($this->plugin, "_MAIL_SEND");
}
/**
* Set the 5th parameter in mail() as a sprintf() formatted string. Only used if safe_mode is off.
* @param string
*/
public function setAdditionalMailParams($params)
{
$this->pluginParams = $params;
if ($this->plugin !== null)
{
$this->plugin->setAdditionalParams($params);
}
}
/**
* Get the 5th parameter in mail() as a sprintf() formatted string.
* @return string
*/
public function getAdditionalMailParams()
{
return $this->pluginParams;
}
/**
* Read a full response from the buffer (this is spoofed if running in -t mode)
* @return string
* @throws Swift_ConnectionException Upon failure to read
*/
public function read()
{
return $this->response;
}
/**
* Set the response this stub will return
* @param string The response to send
*/
public function setResponse($int)
{
$this->response = $int . " Stubbed";
}
/**
* Write a command to the process (leave off trailing CRLF)
* @param string The command to send
* @throws Swift_ConnectionException Upon failure to write
*/
public function write($command, $end="\r\n")
{
$command = strtoupper($command);
if (strpos($command, " ")) $command = substr($command, 0, strpos($command, " "));
switch ($command)
{
case "DATA":
$this->setResponse(354);
break;
case "EHLO": case "MAIL": case "RCPT": case "QUIT": case "RSET": default:
$this->setResponse(250);
break;
}
}
/**
* Try to start the connection
* @throws Swift_ConnectionException Upon failure to start
*/
public function start()
{
$this->response = "220 Stubbed";
}
/**
* Try to close the connection
* @throws Swift_ConnectionException Upon failure to close
*/
public function stop()
{
$this->response = "220 Stubbed";
}
/**
* Check if the process is still alive
* @return boolean
*/
public function isAlive()
{
return function_exists("mail");
}
}

View File

@@ -0,0 +1,194 @@
<?php
/**
* Swift Mailer Multiple Redundant Cycling Connection component.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_ConnectionBase");
/**
* Swift Rotator Connection
* Switches through each connection in turn after sending each message
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Connection_Rotator extends Swift_ConnectionBase
{
/**
* The list of available connections
* @var array
*/
protected $connections = array();
/**
* The id of the active connection
* @var int
*/
protected $active = null;
/**
* Contains a list of any connections which were tried but found to be dead
* @var array
*/
protected $dead = array();
/**
* Constructor
*/
public function __construct($connections=array())
{
foreach ($connections as $id => $conn)
{
$this->addConnection($connections[$id], $id);
}
}
/**
* Add a connection to the list of options
* @param Swift_Connection An instance of the connection
*/
public function addConnection(Swift_Connection $connection)
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Adding new connection of type '" . get_class($connection) . "' to rotator.");
}
$this->connections[] = $connection;
}
/**
* Rotate to the next working connection
* @throws Swift_ConnectionException If no connections are available
*/
public function nextConnection()
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add(" <==> Rotating connection.");
}
$total = count($this->connections);
$start = $this->active === null ? 0 : ($this->active + 1);
if ($start >= $total) $start = 0;
$fail_messages = array();
for ($id = $start; $id < $total; $id++)
{
if (in_array($id, $this->dead)) continue; //The connection was previously found to be useless
try {
if (!$this->connections[$id]->isAlive()) $this->connections[$id]->start();
if ($this->connections[$id]->isAlive())
{
$this->active = $id;
return true;
}
else
{
$this->dead[] = $id;
$this->connections[$id]->stop();
throw new Swift_ConnectionException("The connection started but reported that it was not active");
}
} catch (Swift_ConnectionException $e) {
$fail_messages[] = $id . ": " . $e->getMessage();
}
}
$failure = implode("<br />", $fail_messages);
throw new Swift_ConnectionException("No connections were started.<br />" . $failure);
}
/**
* Read a full response from the buffer
* @return string
* @throws Swift_ConnectionException Upon failure to read
*/
public function read()
{
if ($this->active === null)
{
throw new Swift_ConnectionException("None of the connections set have been started");
}
return $this->connections[$this->active]->read();
}
/**
* Write a command to the server (leave off trailing CRLF)
* @param string The command to send
* @throws Swift_ConnectionException Upon failure to write
*/
public function write($command, $end="\r\n")
{
if ($this->active === null)
{
throw new Swift_ConnectionException("None of the connections set have been started");
}
return $this->connections[$this->active]->write($command, $end);
}
/**
* Try to start the connection
* @throws Swift_ConnectionException Upon failure to start
*/
public function start()
{
if ($this->active === null) $this->nextConnection();
}
/**
* Try to close the connection
* @throws Swift_ConnectionException Upon failure to close
*/
public function stop()
{
foreach ($this->connections as $id => $conn)
{
if ($this->connections[$id]->isAlive()) $this->connections[$id]->stop();
}
$this->active = null;
}
/**
* Check if the current connection is alive
* @return boolean
*/
public function isAlive()
{
return (($this->active !== null) && $this->connections[$this->active]->isAlive());
}
/**
* Get the ID of the active connection
* @return int
*/
public function getActive()
{
return $this->active;
}
/**
* Call the current connection's postConnect() method
*/
public function postConnect(Swift $instance)
{
Swift_ClassLoader::load("Swift_Plugin_ConnectionRotator");
if (!$instance->getPlugin("_ROTATOR")) $instance->attachPlugin(new Swift_Plugin_ConnectionRotator(), "_ROTATOR");
$this->connections[$this->active]->postConnect($instance);
}
/**
* Call the current connection's setExtension() method
*/
public function setExtension($extension, $attributes=array())
{
$this->connections[$this->active]->setExtension($extension, $attributes);
}
/**
* Call the current connection's hasExtension() method
*/
public function hasExtension($name)
{
return $this->connections[$this->active]->hasExtension($name);
}
/**
* Call the current connection's getAttributes() method
*/
public function getAttributes($name)
{
return $this->connections[$this->active]->getAttributes($name);
}
}

View File

@@ -0,0 +1,452 @@
<?php
/**
* Swift Mailer SMTP Connection component.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_ConnectionBase");
Swift_ClassLoader::load("Swift_Authenticator");
/**
* Swift SMTP Connection
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Connection_SMTP extends Swift_ConnectionBase
{
/**
* Constant for TLS connections
*/
const ENC_TLS = 2;
/**
* Constant for SSL connections
*/
const ENC_SSL = 4;
/**
* Constant for unencrypted connections
*/
const ENC_OFF = 8;
/**
* Constant for the default SMTP port
*/
const PORT_DEFAULT = 25;
/**
* Constant for the default secure SMTP port
*/
const PORT_SECURE = 465;
/**
* Constant for auto-detection of paramters
*/
const AUTO_DETECT = -2;
/**
* A connection handle
* @var resource
*/
protected $handle = null;
/**
* The remote port number
* @var int
*/
protected $port = null;
/**
* Encryption type to use
* @var int
*/
protected $encryption = null;
/**
* A connection timeout
* @var int
*/
protected $timeout = 15;
/**
* A username to authenticate with
* @var string
*/
protected $username = false;
/**
* A password to authenticate with
* @var string
*/
protected $password = false;
/**
* Loaded authentication mechanisms
* @var array
*/
protected $authenticators = array();
/**
* Fsockopen() error codes.
* @var int
*/
protected $errno;
/**
* Fsockopen() error codes.
* @var string
*/
protected $errstr;
/**
* Constructor
* @param string The remote server to connect to
* @param int The remote port to connect to
* @param int The encryption level to use
*/
public function __construct($server="localhost", $port=null, $encryption=null)
{
$this->setServer($server);
$this->setEncryption($encryption);
$this->setPort($port);
}
/**
* Set the timeout to connect in seconds
* @param int Timeout to use
*/
public function setTimeout($time)
{
$this->timeout = (int) $time;
}
/**
* Get the timeout currently set for connecting
* @return int
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* Set the remote server to connect to as a FQDN
* @param string Server name
*/
public function setServer($server)
{
if ($server == self::AUTO_DETECT)
{
$server = @ini_get("SMTP");
if (!$server) $server = "localhost";
}
$this->server = (string) $server;
}
/**
* Get the remote server name
* @return string
*/
public function getServer()
{
return $this->server;
}
/**
* Set the remote port number to connect to
* @param int Port number
*/
public function setPort($port)
{
if ($port == self::AUTO_DETECT)
{
$port = @ini_get("SMTP_PORT");
}
if (!$port) $port = ($this->getEncryption() == self::ENC_OFF) ? self::PORT_DEFAULT : self::PORT_SECURE;
$this->port = (int) $port;
}
/**
* Get the remote port number currently used to connect
* @return int
*/
public function getPort()
{
return $this->port;
}
/**
* Provide a username for authentication
* @param string The username
*/
public function setUsername($user)
{
$this->setRequiresEHLO(true);
$this->username = $user;
}
/**
* Get the username for authentication
* @return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set the password for SMTP authentication
* @param string Password to use
*/
public function setPassword($pass)
{
$this->setRequiresEHLO(true);
$this->password = $pass;
}
/**
* Get the password for authentication
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Add an authentication mechanism to authenticate with
* @param Swift_Authenticator
*/
public function attachAuthenticator(Swift_Authenticator $auth)
{
$this->authenticators[$auth->getAuthExtensionName()] = $auth;
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Authentication mechanism '" . $auth->getAuthExtensionName() . "' attached.");
}
}
/**
* Set the encryption level to use on the connection
* See the constants ENC_TLS, ENC_SSL and ENC_OFF
* NOTE: PHP needs to have been compiled with OpenSSL for SSL and TLS to work
* NOTE: Some PHP installations will not have the TLS stream wrapper
* @param int Level of encryption
*/
public function setEncryption($enc)
{
if (!$enc) $enc = self::ENC_OFF;
$this->encryption = (int) $enc;
}
/**
* Get the current encryption level used
* This method returns an integer corresponding to one of the constants ENC_TLS, ENC_SSL or ENC_OFF
* @return int
*/
public function getEncryption()
{
return $this->encryption;
}
/**
* Read a full response from the buffer
* inner !feof() patch provided by Christian Rodriguez:
* <a href="http://www.flyspray.org/">www.flyspray.org</a>
* @return string
* @throws Swift_ConnectionException Upon failure to read
*/
public function read()
{
if (!$this->handle) throw new Swift_ConnectionException(
"The SMTP connection is not alive and cannot be read from." . $this->smtpErrors());
$ret = "";
$line = 0;
while (!feof($this->handle))
{
$line++;
stream_set_timeout($this->handle, $this->timeout);
$tmp = @fgets($this->handle);
if ($tmp === false && !feof($this->handle))
{
throw new Swift_ConnectionException(
"There was a problem reading line " . $line . " of an SMTP response. The response so far was:<br />[" . $ret .
"]. It appears the connection has died without saying goodbye to us! Too many emails in one go perhaps?" .
$this->smtpErrors());
}
$ret .= trim($tmp) . "\r\n";
if ($tmp{3} == " ") break;
}
return $ret = substr($ret, 0, -2);
}
/**
* Write a command to the server (leave off trailing CRLF)
* @param string The command to send
* @throws Swift_ConnectionException Upon failure to write
*/
public function write($command, $end="\r\n")
{
if (!$this->handle) throw new Swift_ConnectionException(
"The SMTP connection is not alive and cannot be written to." .
$this->smtpErrors());
if (!@fwrite($this->handle, $command . $end) && !empty($command)) throw new Swift_ConnectionException("The SMTP connection did not allow the command '" . $command . "' to be sent." . $this->smtpErrors());
}
/**
* Try to start the connection
* @throws Swift_ConnectionException Upon failure to start
*/
public function start()
{
if ($this->port === null)
{
switch ($this->encryption)
{
case self::ENC_TLS: case self::ENC_SSL:
$this->port = 465;
break;
case null: default:
$this->port = 25;
break;
}
}
$server = $this->server;
if ($this->encryption == self::ENC_TLS) $server = "tls://" . $server;
elseif ($this->encryption == self::ENC_SSL) $server = "ssl://" . $server;
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_log::LOG_EVERYTHING))
{
$log->add("Trying to connect to SMTP server at '" . $server . ":" . $this->port);
}
if (!$this->handle = @fsockopen($server, $this->port, $errno, $errstr, $this->timeout))
{
$error_msg = "The SMTP connection failed to start [" . $server . ":" . $this->port . "]: fsockopen returned Error Number " . $errno . " and Error String '" . $errstr . "'";
if ($log->isEnabled())
{
$log->add($error_msg, Swift_Log::ERROR);
}
$this->handle = null;
throw new Swift_ConnectionException($error_msg);
}
$this->errno =& $errno;
$this->errstr =& $errstr;
}
/**
* Get the smtp error string as recorded by fsockopen()
* @return string
*/
public function smtpErrors()
{
return " (fsockopen: " . $this->errstr . "#" . $this->errno . ") ";
}
/**
* Authenticate if required to do so
* @param Swift An instance of Swift
* @throws Swift_ConnectionException If authentication fails
*/
public function postConnect(Swift $instance)
{
if ($this->getUsername() && $this->getPassword())
{
$this->runAuthenticators($this->getUsername(), $this->getPassword(), $instance);
}
}
/**
* Run each authenticator in turn an try for a successful login
* If none works, throw an exception
* @param string Username
* @param string Password
* @param Swift An instance of swift
* @throws Swift_ConnectionException Upon failure to authenticate
*/
public function runAuthenticators($user, $pass, Swift $swift)
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Trying to authenticate with username '" . $user . "'.");
}
//Load in defaults
if (empty($this->authenticators))
{
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("No authenticators loaded; looking for defaults.");
}
$dir = dirname(__FILE__) . "/../Authenticator";
$handle = opendir($dir);
while (false !== $file = readdir($handle))
{
if (preg_match("/^[A-Za-z0-9-]+\\.php\$/", $file))
{
$name = preg_replace('/[^a-zA-Z0-9]+/', '', substr($file, 0, -4));
require_once $dir . "/" . $file;
$class = "Swift_Authenticator_" . $name;
$this->attachAuthenticator(new $class());
}
}
closedir($handle);
}
$tried = 0;
$looks_supported = true;
//Allow everything we have if the server has the audacity not to help us out.
if (!$this->hasExtension("AUTH"))
{
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Server (perhaps wrongly) is not advertising AUTH... manually overriding.");
}
$looks_supported = false;
$this->setExtension("AUTH", array_keys($this->authenticators));
}
foreach ($this->authenticators as $name => $obj)
{
//Server supports this authentication mechanism
if (in_array($name, $this->getAttributes("AUTH")) || $name{0} == "*")
{
$tried++;
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Trying '" . $name . "' authentication...");
}
if ($this->authenticators[$name]->isAuthenticated($user, $pass, $swift))
{
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Success! Authentication accepted.");
}
return true;
}
}
}
//Server doesn't support authentication
if (!$looks_supported && $tried == 0)
throw new Swift_ConnectionException("Authentication is not supported by the server but a username and password was given.");
if ($tried == 0)
throw new Swift_ConnectionException("No authentication mechanisms were tried since the server did not support any of the ones loaded. " .
"Loaded authenticators: [" . implode(", ", array_keys($this->authenticators)) . "]");
else
throw new Swift_ConnectionException("Authentication failed using username '" . $user . "' and password '". str_repeat("*", strlen($pass)) . "'");
}
/**
* Try to close the connection
* @throws Swift_ConnectionException Upon failure to close
*/
public function stop()
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Closing down SMTP connection.");
}
if ($this->handle)
{
if (!fclose($this->handle))
{
throw new Swift_ConnectionException("The SMTP connection could not be closed for an unknown reason." . $this->smtpErrors());
}
$this->handle = null;
}
}
/**
* Check if the SMTP connection is alive
* @return boolean
*/
public function isAlive()
{
return ($this->handle !== null);
}
/**
* Destructor.
* Cleans up any open connections.
*/
public function __destruct()
{
$this->stop();
}
}

View File

@@ -0,0 +1,352 @@
<?php
/**
* Swift Mailer Sendmail Connection component.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_ConnectionBase");
/**
* Swift Sendmail Connection
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Connection_Sendmail extends Swift_ConnectionBase
{
/**
* Constant for auto-detection of paths
*/
const AUTO_DETECT = -2;
/**
* Flags for the MTA (options such as bs or t)
* @var string
*/
protected $flags = null;
/**
* The full path to the MTA
* @var string
*/
protected $path = null;
/**
* The type of last request sent
* For example MAIL, RCPT, DATA
* @var string
*/
protected $request = null;
/**
* The process handle
* @var resource
*/
protected $proc;
/**
* I/O pipes for the process
* @var array
*/
protected $pipes;
/**
* Switches to true for just one command when DATA has been issued
* @var boolean
*/
protected $send = false;
/**
* The timeout in seconds before giving up
* @var int Seconds
*/
protected $timeout = 10;
/**
* Constructor
* @param string The command to execute
* @param int The timeout in seconds before giving up
*/
public function __construct($command="/usr/sbin/sendmail -bs", $timeout=10)
{
$this->setCommand($command);
$this->setTimeout($timeout);
}
/**
* Set the timeout on the process
* @param int The number of seconds
*/
public function setTimeout($secs)
{
$this->timeout = (int)$secs;
}
/**
* Get the timeout on the process
* @return int
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* Set the operating flags for the MTA
* @param string
*/
public function setFlags($flags)
{
$this->flags = $flags;
}
/**
* Get the operating flags for the MTA
* @return string
*/
public function getFlags()
{
return $this->flags;
}
/**
* Set the path to the binary
* @param string The path (must be absolute!)
*/
public function setPath($path)
{
if ($path == self::AUTO_DETECT) $path = $this->findSendmail();
$this->path = $path;
}
/**
* Get the path to the binary
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* For auto-detection of sendmail path
* Thanks to "Joe Cotroneo" for providing the enhancement
* @return string
*/
public function findSendmail()
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Sendmail path auto-detection in progress. Trying `which sendmail`");
}
$path = @trim(shell_exec('which sendmail'));
if (!is_executable($path))
{
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("No luck so far, trying some common paths...");
}
$common_locations = array(
'/usr/bin/sendmail',
'/usr/lib/sendmail',
'/var/qmail/bin/sendmail',
'/bin/sendmail',
'/usr/sbin/sendmail',
'/sbin/sendmail'
);
foreach ($common_locations as $path)
{
if (is_executable($path)) return $path;
}
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Falling back to /usr/sbin/sendmail (but it doesn't look good)!");
}
//Fallback (swift will still throw an error)
return "/usr/sbin/sendmail";
}
else return $path;
}
/**
* Set the sendmail command (path + flags)
* @param string Command
* @throws Swift_ConnectionException If the command is not correctly structured
*/
public function setCommand($command)
{
if ($command == self::AUTO_DETECT) $command = $this->findSendmail() . " -bs";
if (!strrpos($command, " -"))
{
throw new Swift_ConnectionException("Cannot set sendmail command with no command line flags. e.g. /usr/sbin/sendmail -t");
}
$path = substr($command, 0, strrpos($command, " -"));
$flags = substr($command, strrpos($command, " -")+2);
$this->setPath($path);
$this->setFlags($flags);
}
/**
* Get the sendmail command (path + flags)
* @return string
*/
public function getCommand()
{
return $this->getPath() . " -" . $this->getFlags();
}
/**
* Write a command to the open pipe
* @param string The command to write
* @throws Swift_ConnectionException If the pipe cannot be written to
*/
protected function pipeIn($command, $end="\r\n")
{
if (!$this->isAlive()) throw new Swift_ConnectionException("The sendmail process is not alive and cannot be written to.");
if (!@fwrite($this->pipes[0], $command . $end) && !empty($command)) throw new Swift_ConnectionException("The sendmail process did not allow the command '" . $command . "' to be sent.");
fflush($this->pipes[0]);
}
/**
* Read data from the open pipe
* @return string
* @throws Swift_ConnectionException If the pipe is not operating as expected
*/
protected function pipeOut()
{
if (strpos($this->getFlags(), "t") !== false) return;
if (!$this->isAlive()) throw new Swift_ConnectionException("The sendmail process is not alive and cannot be read from.");
$ret = "";
$line = 0;
while (true)
{
$line++;
stream_set_timeout($this->pipes[1], $this->timeout);
$tmp = @fgets($this->pipes[1]);
if ($tmp === false)
{
throw new Swift_ConnectionException("There was a problem reading line " . $line . " of a sendmail SMTP response. The response so far was:<br />[" . $ret . "]. It appears the process has died.");
}
$ret .= trim($tmp) . "\r\n";
if ($tmp{3} == " ") break;
}
fflush($this->pipes[1]);
return $ret = substr($ret, 0, -2);
}
/**
* Read a full response from the buffer (this is spoofed if running in -t mode)
* @return string
* @throws Swift_ConnectionException Upon failure to read
*/
public function read()
{
if (strpos($this->getFlags(), "t") !== false)
{
switch (strtolower($this->request))
{
case null:
return "220 Greetings";
case "helo": case "ehlo":
return "250 hello";
case "mail": case "rcpt": case "rset":
return "250 ok";
case "quit":
return "221 bye";
case "data":
$this->send = true;
return "354 go ahead";
default:
return "250 ok";
}
}
else return $this->pipeOut();
}
/**
* Write a command to the process (leave off trailing CRLF)
* @param string The command to send
* @throws Swift_ConnectionException Upon failure to write
*/
public function write($command, $end="\r\n")
{
if (strpos($this->getFlags(), "t") !== false)
{
if (!$this->send && strpos($command, " ")) $command = substr($command, strpos($command, " ")+1);
elseif ($this->send)
{
$this->pipeIn($command);
}
$this->request = $command;
$this->send = (strtolower($command) == "data");
}
else $this->pipeIn($command, $end);
}
/**
* Try to start the connection
* @throws Swift_ConnectionException Upon failure to start
*/
public function start()
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Trying to start a sendmail process.");
}
if (!$this->getPath() || !$this->getFlags())
{
throw new Swift_ConnectionException("Sendmail cannot be started without a path to the binary including flags.");
}
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Trying to stat the executable '" . $this->getPath() . "'.");
}
if (!@lstat($this->getPath()))
{
throw new Swift_ConnectionException(
"Sendmail cannot be seen with lstat(). The command given [" . $this->getCommand() . "] does not appear to be valid.");
}
$pipes_spec = array(
array("pipe", "r"),
array("pipe", "w"),
array("pipe", "w")
);
$this->proc = proc_open($this->getCommand(), $pipes_spec, $this->pipes);
if (!$this->isAlive())
{
throw new Swift_ConnectionException(
"The sendmail process failed to start. Please verify that the path exists and PHP has permission to execute it.");
}
}
/**
* Try to close the connection
*/
public function stop()
{
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Terminating sendmail process.");
}
foreach ((array)$this->pipes as $pipe)
{
@fclose($pipe);
}
if ($this->proc)
{
proc_close($this->proc);
$this->pipes = null;
$this->proc = null;
}
}
/**
* Check if the process is still alive
* @return boolean
*/
public function isAlive()
{
return ($this->proc !== false
&& is_resource($this->proc)
&& is_resource($this->pipes[0])
&& is_resource($this->pipes[1])
&& $this->proc !== null);
}
/**
* Destructor.
* Cleans up by stopping any running processes.
*/
public function __destruct()
{
$this->stop();
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* Swift Mailer Connection Base Class
* All connection handlers extend this abstract class
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_LogContainer");
Swift_ClassLoader::load("Swift_Connection");
Swift_ClassLoader::load("Swift_ConnectionException");
/**
* Swift Connection Base Class
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_ConnectionBase implements Swift_Connection
{
/**
* Any extensions the server might support
* @var array
*/
protected $extensions = array();
/**
* True if the connection is ESMTP.
* @var boolean
*/
protected $isESMTP = false;
/**
* Set an extension which the connection reports to support
* @param string Extension name
* @param array Attributes of the extension
*/
public function setExtension($name, $options=array())
{
$this->extensions[$name] = $options;
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("SMTP extension '" . $name . "' reported with attributes [" . implode(", ", $options) . "].");
}
}
/**
* Check if a given extension has been set as available
* @param string The name of the extension
* @return boolean
*/
public function hasExtension($name)
{
return array_key_exists($name, $this->extensions);
}
/**
* Execute any needed logic after connecting and handshaking
*/
public function postConnect(Swift $instance) {}
/**
* Get the list of attributes supported by the given extension
* @param string The name of the connection
* @return array The list of attributes
* @throws Swift_ConnectionException If the extension cannot be found
*/
public function getAttributes($extension)
{
if ($this->hasExtension($extension))
{
return $this->extensions[$extension];
}
else
{
throw new Swift_ConnectionException(
"Unable to locate any attributes for the extension '" . $extension . "' since the extension cannot be found. " .
"Consider using hasExtension() to check.");
}
}
/**
* Returns TRUE if the connection needs a EHLO greeting.
* @return boolean
*/
public function getRequiresEHLO()
{
return $this->isESMTP;
}
/**
* Set TRUE if the connection needs a EHLO greeting.
* @param boolean
*/
public function setRequiresEHLO($set)
{
$this->isESMTP = (bool) $set;
$log = Swift_LogContainer::getLog();
if ($log->hasLevel(Swift_Log::LOG_EVERYTHING))
{
$log->add("Forcing ESMTP mode. HELO is EHLO.");
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Swift Connection Exception
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_Exception");
/**
* Swift Connection Exception
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_ConnectionException extends Swift_Exception
{
}

41
system/vendor/swift/Swift/Events.php vendored Normal file
View File

@@ -0,0 +1,41 @@
<?php
/**
* Swift Mailer Events Layer
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Provides core functionality for Swift generated events for plugins
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_Events
{
/**
* An instance of Swift
* @var Swift
*/
protected $swift = null;
/**
* Provide a reference to te currently running Swift this event was generated from
* @param Swift
*/
public function setSwift(Swift $swift)
{
$this->swift = $swift;
}
/**
* Get the current instance of swift
* @return Swift
*/
public function getSwift()
{
return $this->swift;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Swift Mailer Before Command Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a CommandEvent, before it is sent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_BeforeCommandListener extends Swift_Events_Listener
{
/**
* Executes just before Swift sends a command
* @param Swift_Events_CommandEvent Information about the being command sent
*/
public function beforeCommandSent(Swift_Events_CommandEvent $e);
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Swift Mailer Before Send Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a SendEvent before the message is sent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_BeforeSendListener extends Swift_Events_Listener
{
/**
* Executes just before Swift sends a message
* @param Swift_Events_SendEvent Information about the message being sent
*/
public function beforeSendPerformed(Swift_Events_SendEvent $e);
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Swift Mailer Command Event
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Generated when Swift is sending a command
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_CommandEvent extends Swift_Events
{
/**
* Contains the command being sent
* @var string
*/
protected $string = null;
/**
* Contains the expected response code (or null)
* @var int
*/
protected $code = null;
/**
* Constructor
* @param string The command being sent
* @param int The expected code
*/
public function __construct($string, $code=null)
{
$this->setString($string);
$this->setCode($code);
}
/**
* Set the command being sent (without CRLF)
* @param string The command being sent
*/
public function setString($string)
{
$this->string = (string) $string;
}
/**
* Get the command being sent
* @return string
*/
public function getString()
{
return $this->string;
}
/**
* Set response code which is expected
* @param int The response code
*/
public function setCode($code)
{
if ($code === null) $this->code = null;
else $this->code = (int) $code;
}
/**
* Get the expected response code
* @return int
*/
public function getCode()
{
return $this->code;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Swift Mailer Command Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a CommandEvent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_CommandListener extends Swift_Events_Listener
{
/**
* Executes when Swift sends a command
* @param Swift_Events_CommandEvent Information about the command sent
*/
public function commandSent(Swift_Events_CommandEvent $e);
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Swift Mailer Connect Event
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Generated every time Swift connects with a MTA
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_ConnectEvent extends Swift_Events
{
/**
* A reference to the connection object
* @var Swift_Connection
*/
protected $connection = null;
/**
* Constructor
* @param Swift_Connection The new connection
*/
public function __construct(Swift_Connection $connection)
{
$this->connection = $connection;
}
/**
* Get the connection object
* @return Swift_Connection
*/
public function getConnection()
{
return $this->connection;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Swift Mailer Connect Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a ConnectEvent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_ConnectListener extends Swift_Events_Listener
{
/**
* Executes when Swift initiates a connection
* @param Swift_Events_ConnectEvent Information about the connection
*/
public function connectPerformed(Swift_Events_ConnectEvent $e);
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Swift Mailer Disconnect Event
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Generated every time Swift disconnects from a MTA
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_DisconnectEvent extends Swift_Events
{
/**
* A reference to the connection object
* @var Swift_Connection
*/
protected $connection = null;
/**
* Constructor
* @param Swift_Connection The dead connection
*/
public function __construct(Swift_Connection $connection)
{
$this->connection = $connection;
}
/**
* Get the connection object
* @return Swift_Connection
*/
public function getConnection()
{
return $this->connection;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Swift Mailer Disconnect Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a DisconnectEvent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_DisconnectListener extends Swift_Events_Listener
{
/**
* Executes when Swift closes a connection
* @param Swift_Events_DisconnectEvent Information about the connection
*/
public function disconnectPerformed(Swift_Events_DisconnectEvent $e);
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Swift Mailer Event Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Used for identity only
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_Listener {}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Swift Mailer Mapper for Event Listeners
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Maps event listener names to the methods they implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_ListenerMapper
{
/**
* Get the mapped names (Class => Method(s))
* @return array
*/
public static function getMap()
{
$map = array(
"SendListener" => "sendPerformed",
"BeforeSendListener" => "beforeSendPerformed",
"CommandListener" => "commandSent",
"BeforeCommandListener" => "beforeCommandSent",
"ResponseListener" => "responseReceived",
"ConnectListener" => "connectPerformed",
"DisconnectListener" => "disconnectPerformed"
);
return $map;
}
/**
* Get the name of the method which needs running based upon the listener name
* @return string
*/
public static function getNotifyMethod($listener)
{
$map = self::getMap();
if (isset($map[$listener])) return $map[$listener];
else return false;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* Swift Mailer Response Event
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Generated when Swift receives a server response
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_ResponseEvent extends Swift_Events
{
/**
* Contains the response received
* @var string
*/
protected $string = null;
/**
* Contains the response code
* @var int
*/
protected $code = null;
/**
* Constructor
* @param string The received response
*/
public function __construct($string)
{
$this->setString($string);
$this->setCode(substr($string, 0, 3));
}
/**
* Set the response received
* @param string The response
*/
public function setString($string)
{
$this->string = (string) $string;
}
/**
* Get the received response
* @return string
*/
public function getString()
{
return $this->string;
}
/**
* Set response code
* @param int The response code
*/
public function setCode($code)
{
$this->code = (int) $code;
}
/**
* Get the response code
* @return int
*/
public function getCode()
{
return $this->code;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Swift Mailer Response Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a ResponseEvent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_ResponseListener extends Swift_Events_Listener
{
/**
* Executes when Swift receives a response
* @param Swift_Events_ResponseEvent Information about the response
*/
public function responseReceived(Swift_Events_ResponseEvent $e);
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* Swift Mailer Send Event
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Generated every time a message is sent with Swift
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_SendEvent extends Swift_Events
{
/**
* A reference to the message being sent
* @var Swift_Message
*/
protected $message = null;
/**
* A reference to the sender address object
* @var Swift_Address
*/
protected $sender = null;
/**
* A reference to the recipients being sent to
* @var Swift_RecipientList
*/
protected $recipients = null;
/**
* The number of recipients sent to so
* @var int
*/
protected $sent = null;
/**
* Recipients we couldn't send to
* @var array
*/
protected $failed = array();
/**
* Constructor
* @param Swift_Message The message being sent
* @param Swift_RecipientList The recipients
* @param Swift_Address The sender address
* @param int The number of addresses sent to
*/
public function __construct(Swift_Message $message, Swift_RecipientList $list, Swift_Address $from, $sent=0)
{
$this->message = $message;
$this->recipients = $list;
$this->sender = $from;
$this->sent = $sent;
}
/**
* Get the message being sent
* @return Swift_Message
*/
public function getMessage()
{
return $this->message;
}
/**
* Get the list of recipients
* @return Swift_RecipientList
*/
public function getRecipients()
{
return $this->recipients;
}
/**
* Get the sender's address
* @return Swift_Address
*/
public function getSender()
{
return $this->sender;
}
/**
* Set the number of recipients to how many were sent
* @param int
*/
public function setNumSent($sent)
{
$this->sent = (int) $sent;
}
/**
* Get the total number of addresses to which the email sent successfully
* @return int
*/
public function getNumSent()
{
return $this->sent;
}
/**
* Add an email address to the failed recipient list for this send
* @var string The email address
*/
public function addFailedRecipient($address)
{
$this->failed[] = $address;
}
/**
* Get an array of failed recipients for this send
* @return array
*/
public function getFailedRecipients()
{
return $this->failed;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Swift Mailer Send Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Events
* @license GNU Lesser General Public License
*/
/**
* Contains the list of methods a plugin requiring the use of a SendEvent must implement
* @package Swift_Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_SendListener extends Swift_Events_Listener
{
/**
* Executes when Swift sends a message
* @param Swift_Events_SendEvent Information about the message being sent
*/
public function sendPerformed(Swift_Events_SendEvent $e);
}

36
system/vendor/swift/Swift/Exception.php vendored Normal file
View File

@@ -0,0 +1,36 @@
<?php
/**
* Swift Mailer Logging Layer Interface
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Log
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_LogContainer");
/**
* The Logger Interface
* @package Swift_Log
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Exception extends Exception
{
/**
* Constructor.
* Creates the exception and appends log information if available.
* @param string Message
* @param int Code
*/
public function __construct($message, $code = 0)
{
if (($log = Swift_LogContainer::getLog()) && $log->isEnabled())
{
$message .= "<h3>Log Information</h3>";
$message .= "<pre>" . htmlentities($log->dump(true)) . "</pre>";
}
parent::__construct($message, $code);
}
}

215
system/vendor/swift/Swift/File.php vendored Normal file
View File

@@ -0,0 +1,215 @@
<?php
/**
* Swift Mailer File Stream Wrapper
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_FileException");
/**
* Swift File stream abstraction layer
* Reads bytes from a file
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_File
{
/**
* The accessible path to the file
* @var string
*/
protected $path = null;
/**
* The name of the file
* @var string
*/
protected $name = null;
/**
* The resource returned by fopen() against the path
* @var resource
*/
protected $handle = null;
/**
* The status of magic_quotes in php.ini
* @var boolean
*/
protected $magic_quotes = false;
/**
* Constructor
* @param string The path the the file
* @throws Swift_FileException If the file cannot be found
*/
public function __construct($path)
{
$this->setPath($path);
$this->magic_quotes = get_magic_quotes_runtime();
}
/**
* Set the path to the file
* @param string The path to the file
* @throws Swift_FileException If the file cannot be found
*/
public function setPath($path)
{
if (!file_exists($path))
{
throw new Swift_FileException("No such file '" . $path ."'");
}
$this->handle = null;
$this->path = $path;
$this->name = null;
$this->name = $this->getFileName();
}
/**
* Get the path to the file
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Get the name of the file without it's full path
* @return string
*/
public function getFileName()
{
if ($this->name !== null)
{
return $this->name;
}
else
{
return basename($this->getPath());
}
}
/**
* Establish an open file handle on the file if the file is not yet opened
* @throws Swift_FileException If the file cannot be opened for reading
*/
protected function createHandle()
{
if ($this->handle === null)
{
if (!$this->handle = fopen($this->path, "rb"))
{
throw new Swift_FileException("Unable to open file '" . $this->path . " for reading. Check the file permissions.");
}
}
}
/**
* Check if the pointer as at the end of the file
* @return boolean
* @throws Swift_FileException If the file cannot be read
*/
public function EOF()
{
$this->createHandle();
return feof($this->handle);
}
/**
* Get a single byte from the file
* Returns false past EOF
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function getByte()
{
$this->createHandle();
return $this->read(1);
}
/**
* Read one full line from the file including the line ending
* Returns false past EOF
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function readln()
{
set_magic_quotes_runtime(0);
$this->createHandle();
if (!$this->EOF())
{
$ret = fgets($this->handle);
}
else $ret = false;
set_magic_quotes_runtime($this->magic_quotes);
return $ret;
}
/**
* Get the entire file contents as a string
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function readFull()
{
$ret = "";
set_magic_quotes_runtime(0);
while (false !== $chunk = $this->read(8192, false)) $ret .= $chunk;
set_magic_quotes_runtime($this->magic_quotes);
return $ret;
}
/**
* Read a given number of bytes from the file
* Returns false past EOF
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function read($bytes, $unquote=true)
{
if ($unquote) set_magic_quotes_runtime(0);
$this->createHandle();
if (!$this->EOF())
{
$ret = fread($this->handle, $bytes);
}
else $ret = false;
if ($unquote) set_magic_quotes_runtime($this->magic_quotes);
return $ret;
}
/**
* Get the size of the file in bytes
* @return int
*/
public function length()
{
return filesize($this->path);
}
/**
* Close the open handle on the file
* @throws Swift_FileException If the file cannot be read
*/
public function close()
{
$this->createHandle();
fclose($this->handle);
$this->handle = null;
}
/**
* Reset the file pointer back to zero
*/
public function reset()
{
$this->createHandle();
fseek($this->handle, 0);
}
/**
* Destructor
* Closes the file
*/
public function __destruct()
{
if ($this->handle !== null) $this->close();
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Swift File Exception
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_Exception");
/**
* Swift File Exception
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_FileException extends Swift_Exception
{
}

44
system/vendor/swift/Swift/Iterator.php vendored Normal file
View File

@@ -0,0 +1,44 @@
<?php
/**
* Swift Mailer Iterator Interface
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
/**
* Swift Iterator Interface
* Provides the interface for iterators used for retrieving addresses in batch sends.
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Iterator
{
/**
* Check if there is a value in the list after the current one.
* @return boolean
*/
public function hasNext();
/**
* Move to the next position in the list if possible.
* @return boolean
*/
public function next();
/**
* Seek to the given numeric index in the list of possible.
* @return boolean
*/
public function seekTo($pos);
/**
* Get the value of the list at the current position.
* @return mixed
*/
public function getValue();
/**
* Get the current list position.
* @return int
*/
public function getPosition();
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* Swift Mailer Array Iterator Interface
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Iterator");
/**
* Swift Array Iterator Interface
* Iterates over a standard PHP array.
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Iterator_Array implements Swift_Iterator
{
/**
* All keys in this array.
* @var array
*/
protected $keys;
/**
* All values in this array.
* @var array
*/
protected $values;
/**
* The current array position.
* @var int
*/
protected $pos = -1;
/**
* Ctor.
* @param array The array to iterate over.
*/
public function __construct($input)
{
$input = (array) $input;
$this->keys = array_keys($input);
$this->values = array_values($input);
}
/**
* Returns the original array.
* @return array
*/
public function getArray()
{
return array_combine($this->keys, $this->values);
}
/**
* Returns true if there is a value after the current one.
* @return boolean
*/
public function hasNext()
{
return array_key_exists($this->pos + 1, $this->keys);
}
/**
* Moves to the next array element if possible.
* @return boolean
*/
public function next()
{
if ($this->hasNext())
{
++$this->pos;
return true;
}
return false;
}
/**
* Goes directly to the given element in the array if possible.
* @param int Numeric position
* @return boolean
*/
public function seekTo($pos)
{
if (array_key_exists($pos, $this->keys))
{
$this->pos = $pos;
return true;
}
return false;
}
/**
* Returns the value at the current position, or NULL otherwise.
* @return mixed.
*/
public function getValue()
{
if (array_key_exists($this->pos, $this->values))
return $this->values[$this->pos];
else return null;
}
/**
* Gets the current numeric position within the array.
* @return int
*/
public function getPosition()
{
return $this->pos;
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* Swift Mailer MySQL Resultset Iterator
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Iterator");
/**
* Swift Mailer MySQL Resultset Iterator.
* Iterates over MySQL Resultset from mysql_query().
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Iterator_MySQLResult implements Swift_Iterator
{
/**
* The MySQL resource.
* @var resource
*/
protected $resultSet;
/**
* The current row (array) in the resultset.
* @var array
*/
protected $currentRow = array(null, null);
/**
* The current array position.
* @var int
*/
protected $pos = -1;
/**
* The total number of rows in the resultset.
* @var int
*/
protected $numRows = 0;
/**
* Ctor.
* @param resource The resultset iterate over.
*/
public function __construct($rs)
{
$this->resultSet = $rs;
$this->numRows = mysql_num_rows($rs);
}
/**
* Get the resultset.
* @return resource
*/
public function getResultSet()
{
return $this->resultSet;
}
/**
* Returns true if there is a value after the current one.
* @return boolean
*/
public function hasNext()
{
return (($this->pos + 1) < $this->numRows);
}
/**
* Moves to the next array element if possible.
* @return boolean
*/
public function next()
{
if ($this->hasNext())
{
$this->currentRow = mysql_fetch_array($this->resultSet);
$this->pos++;
return true;
}
return false;
}
/**
* Goes directly to the given element in the array if possible.
* @param int Numeric position
* @return boolean
*/
public function seekTo($pos)
{
if ($pos >= 0 && $pos < $this->numRows)
{
mysql_data_seek($this->resultSet, $pos);
$this->currentRow = mysql_fetch_array($this->resultSet);
mysql_data_seek($this->resultSet, $pos);
$this->pos = $pos;
return true;
}
return false;
}
/**
* Returns the value at the current position, or NULL otherwise.
* @return mixed.
*/
public function getValue()
{
$row = $this->currentRow;
if ($row[0] !== null)
return new Swift_Address($row[0], isset($row[1]) ? $row[1] : null);
else
return null;
}
/**
* Gets the current numeric position within the array.
* @return int
*/
public function getPosition()
{
return $this->pos;
}
}

152
system/vendor/swift/Swift/Log.php vendored Normal file
View File

@@ -0,0 +1,152 @@
<?php
/**
* Swift Mailer Logging Layer base class.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Log
* @license GNU Lesser General Public License
*/
/**
* The Logger class/interface.
* @package Swift_Log
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_Log
{
/**
* A command type entry
*/
const COMMAND = ">>";
/**
* A response type entry
*/
const RESPONSE = "<<";
/**
* An error type entry
*/
const ERROR = "!!";
/**
* A standard entry
*/
const NORMAL = "++";
/**
* Logging is off.
*/
const LOG_NOTHING = 0;
/**
* Only errors are logged.
*/
const LOG_ERRORS = 1;
/**
* Errors + sending failures.
*/
const LOG_FAILURES = 2;
/**
* All SMTP instructions + failures + errors.
*/
const LOG_NETWORK = 3;
/**
* Runtime info + SMTP instructions + failures + errors.
*/
const LOG_EVERYTHING = 4;
/**
* Failed recipients
* @var array
*/
protected $failedRecipients = array();
/**
* The maximum number of log entries
* @var int
*/
protected $maxSize = 50;
/**
* The level of logging currently set.
* @var int
*/
protected $logLevel = self::LOG_NOTHING;
/**
* Add a new entry to the log
* @param string The information to log
* @param string The type of entry (see the constants: COMMAND, RESPONSE, ERROR, NORMAL)
*/
abstract public function add($text, $type = self::NORMAL);
/**
* Dump the contents of the log to the browser.
* @param boolean True if the string should be returned rather than output.
*/
abstract public function dump($return_only=false);
/**
* Empty the log contents
*/
abstract public function clear();
/**
* Check if logging is enabled.
*/
public function isEnabled()
{
return ($this->logLevel > self::LOG_NOTHING);
}
/**
* Add a failed recipient to the list
* @param string The address of the recipient
*/
public function addFailedRecipient($address)
{
$this->failedRecipients[$address] = null;
$this->add("Recipient '" . $address . "' rejected by connection.", self::ERROR);
}
/**
* Get the list of failed recipients
* @return array
*/
public function getFailedRecipients()
{
return array_keys($this->failedRecipients);
}
/**
* Set the maximum size of this log (zero is no limit)
* @param int The maximum entries
*/
public function setMaxSize($size)
{
$this->maxSize = (int) $size;
}
/**
* Get the current maximum allowed log size
* @return int
*/
public function getMaxSize()
{
return $this->maxSize;
}
/**
* Set the log level to one of the constants provided.
* @param int Level
*/
public function setLogLevel($level)
{
$level = (int)$level;
$this->add("Log level changed to " . $level, self::NORMAL);
$this->logLevel = $level;
}
/**
* Get the current log level.
* @return int
*/
public function getLogLevel()
{
return $this->logLevel;
}
/**
* Check if the log level includes the one given.
* @param int Level
* @return boolean
*/
public function hasLevel($level)
{
return ($this->logLevel >= ((int)$level));
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Swift Mailer Default Logger
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Log
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Log");
/**
* The Default Logger class
* @package Swift_Log
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Log_DefaultLog extends Swift_Log
{
/**
* Lines in the log
* @var array
*/
protected $entries = array();
/**
* Add a log entry
* @param string The text for this entry
* @param string The label for the type of entry
*/
public function add($text, $type = self::NORMAL)
{
$this->entries[] = $type . " " . $text;
if ($this->getMaxSize() > 0) $this->entries = array_slice($this->entries, (-1 * $this->getMaxSize()));
}
/**
* Dump the contents of the log to the browser.
* @param boolean True if the string should be returned rather than output.
*/
public function dump($return_only=false)
{
$ret = implode("\n", $this->entries);
if (!$return_only) echo $ret;
else return $ret;
}
/**
* Empty the log
*/
public function clear()
{
$this->failedRecipients = null;
$this->failedRecipients = array();
$this->entries = null;
$this->entries = array();
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* A registry for the logger object.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Log
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_Log_DefaultLog");
/**
* A registry holding the current instance of the log.
* @package Swift_Log
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_LogContainer
{
/**
* The log instance.
* @var Swift_Log
*/
protected static $log = null;
/**
* Registers the logger.
* @param Swift_Log The log
*/
public static function setLog(Swift_Log $log)
{
self::$log = $log;
}
/**
* Returns the current instance of the log, or lazy-loads the default one.
* @return Swift_Log
*/
public static function getLog()
{
if (self::$log === null)
{
self::setLog(new Swift_Log_DefaultLog());
}
return self::$log;
}
}

797
system/vendor/swift/Swift/Message.php vendored Normal file
View File

@@ -0,0 +1,797 @@
<?php
/**
* Swift Mailer Message Component
* Composes MIME 1.0 messages meeting various RFC standards
* Deals with attachments, embedded images, multipart bodies, forwarded messages...
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_Address");
Swift_ClassLoader::load("Swift_Message_Mime");
Swift_ClassLoader::load("Swift_Message_Image");
Swift_ClassLoader::load("Swift_Message_Part");
/**
* Swift Message class
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message extends Swift_Message_Mime
{
/**
* Constant from a high priority message (pretty meaningless)
*/
const PRIORITY_HIGH = 1;
/**
* Constant for a low priority message
*/
const PRIORITY_LOW = 5;
/**
* Constant for a normal priority message
*/
const PRIORITY_NORMAL = 3;
/**
* The MIME warning for client not supporting multipart content
* @var string
*/
protected $mimeWarning = null;
/**
* The version of the library (Swift) if known.
* @var string
*/
protected $libVersion = "";
/**
* A container for references to other objects.
* This is used in some very complex logic when sub-parts get shifted around.
* @var array
*/
protected $references = array(
"parent" => array("alternative" => null, "mixed" => null, "related" => null),
"alternative" => array(),
"mixed" => array(),
"related" => array()
);
/**
* Ctor.
* @param string Message subject
* @param string Body
* @param string Content-type
* @param string Encoding
* @param string Charset
*/
public function __construct($subject="", $body=null, $type="text/plain", $encoding=null, $charset=null)
{
parent::__construct();
if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get"))
{
date_default_timezone_set(@date_default_timezone_get());
}
$this->setReturnPath(null);
$this->setTo("");
$this->setFrom("");
$this->setCc(null);
$this->setBcc(null);
$this->setReplyTo(null);
$this->setSubject($subject);
$this->setDate(time());
if (defined("Swift::VERSION"))
{
$this->libVersion = Swift::VERSION;
$this->headers->set("X-LibVersion", $this->libVersion);
}
$this->headers->set("MIME-Version", "1.0");
$this->setContentType($type);
$this->setCharset($charset);
$this->setFlowed(true);
$this->setEncoding($encoding);
foreach (array_keys($this->references["parent"]) as $key)
{
$this->setReference("parent", $key, $this);
}
$this->setMimeWarning(
"This is a message in multipart MIME format. Your mail client should not be displaying this. " .
"Consider upgrading your mail client to view this message correctly."
);
if ($body !== null)
{
$this->setData($body);
if ($charset === null)
{
Swift_ClassLoader::load("Swift_Message_Encoder");
if (Swift_Message_Encoder::instance()->isUTF8($body)) $this->setCharset("utf-8");
else $this->setCharset("iso-8859-1");
}
}
}
/**
* Sets a reference so when nodes are nested, operations can be redirected.
* This really should be refactored to use just one array rather than dynamic variables.
* @param string Key 1
* @param string Key 2
* @param Object Reference
*/
protected function setReference($where, $key, $ref)
{
if ($ref === $this) $this->references[$where][$key] = false;
else $this->references[$where][$key] = $ref;
}
/**
* Get a reference to an object (for complex reasons).
* @param string Key 1
* @param string Key 2
* @return Object
*/
protected function getReference($where, $key)
{
if (!$this->references[$where][$key]) return $this;
else return $this->references[$where][$key];
}
/**
* Get the level in the MIME hierarchy at which this section should appear.
* @return string
*/
public function getLevel()
{
return Swift_Message_Mime::LEVEL_TOP;
}
/**
* Set the message id literally.
* Unless you know what you are doing you should be using generateId() rather than this method,
* otherwise you may break compliancy with RFC 2822.
* @param string The message ID string.
*/
public function setId($id)
{
$this->headers->set("Message-ID", $id);
}
/**
* Create a RFC 2822 compliant message id, optionally based upon $idstring.
* The message ID includes information about the current time, the server and some random characters.
* @param string An optional string the base the ID on
* @return string The generated message ID, including the <> quotes.
* @author Cristian Rodriguez <judas.iscariote@flyspray.org>
*/
public function generateId($idstring=null)
{
$midparams = array(
"utctime" => gmstrftime("%Y%m%d%H%M%S"),
"pid" => getmypid(),
"randint" => mt_rand(),
"customstr" => (preg_match("/^(?<!\\.)[a-z0-9\\.]+(?!\\.)\$/iD", $idstring) ? $idstring : "swift") ,
"hostname" => (isset($_SERVER["SERVER_NAME"]) ? $_SERVER["SERVER_NAME"] : php_uname("n")),
);
$this->setId(vsprintf("<%s.%d.%d.%s@%s>", $midparams));
return $this->getId();
}
/**
* Get the generated message ID for this message, including the <> quotes.
* If generated automatically, or using generateId() this method returns a RFC2822 compliant Message-ID.
* @return string
* @author Cristian Rodriguez <judas.iscariote@flyspray.org>
*/
public function getId()
{
return $this->headers->has("Message-ID") ? $this->headers->get("Message-ID") : null;
}
/**
* Set the address in the Return-Path: header
* @param string The bounce-detect address
*/
public function setReturnPath($address)
{
if ($address instanceof Swift_Address) $address = $address->build(true);
$this->headers->set("Return-Path", $address);
}
/**
* Return the address used in the Return-Path: header
* @return string
* @param boolean Return the address for SMTP command
*/
public function getReturnPath($smtp=false)
{
if ($this->headers->has("Return-Path"))
{
if (!$smtp) return $this->headers->get("Return-Path");
else
{
$path = $this->headers->get("Return-Path");
if (strpos($path, ">") > strpos($path, "<")) return substr($path, ($start = strpos($path, "<")), ($start + strrpos($path, ">") + 1));
else return "<" . $path . ">";
}
}
}
/**
* Set the address in the From: header
* @param string The address to set as From
*/
public function setFrom($from)
{
if ($from instanceof Swift_Address) $from = $from->build();
$this->headers->set("From", $from);
}
/**
* Get the address used in the From: header
* @return string
*/
public function getFrom()
{
if ($this->headers->has("From")) return $this->headers->get("From");
}
/**
* Set the list of recipients in the To: header
* @param mixed An array or a string
*/
public function setTo($to)
{
if ($to)
{
if (!is_array($to)) $to = array($to);
foreach ($to as $key => $value)
{
if ($value instanceof Swift_Address) $to[$key] = $value->build();
}
}
$this->headers->set("To", $to);
}
/**
* Return the list of recipients in the To: header
* @return array
*/
public function getTo()
{
if ($this->headers->has("To"))
{
$to = $this->headers->get("To");
if ($to == "") return array();
else return (array) $to;
}
}
/**
* Set the list of recipients in the Reply-To: header
* @param mixed An array or a string
*/
public function setReplyTo($replyto)
{
if ($replyto)
{
if (!is_array($replyto)) $replyto = array($replyto);
foreach ($replyto as $key => $value)
{
if ($value instanceof Swift_Address) $replyto[$key] = $value->build();
}
}
$this->headers->set("Reply-To", $replyto);
}
/**
* Return the list of recipients in the Reply-To: header
* @return array
*/
public function getReplyTo()
{
if ($this->headers->has("Reply-To"))
{
$reply_to = $this->headers->get("Reply-To");
if ($reply_to == "") return array();
else return (array) $reply_to;
}
}
/**
* Set the list of recipients in the Cc: header
* @param mixed An array or a string
*/
public function setCc($cc)
{
if ($cc)
{
if (!is_array($cc)) $cc = array($cc);
foreach ($cc as $key => $value)
{
if ($value instanceof Swift_Address) $cc[$key] = $value->build();
}
}
$this->headers->set("Cc", $cc);
}
/**
* Return the list of recipients in the Cc: header
* @return array
*/
public function getCc()
{
if ($this->headers->has("Cc"))
{
$cc = $this->headers->get("Cc");
if ($cc == "") return array();
else return (array) $cc;
}
}
/**
* Set the list of recipients in the Bcc: header
* @param mixed An array or a string
*/
public function setBcc($bcc)
{
if ($bcc)
{
if (!is_array($bcc)) $bcc = array($bcc);
foreach ($bcc as $key => $value)
{
if ($value instanceof Swift_Address) $bcc[$key] = $value->build();
}
}
$this->headers->set("Bcc", $bcc);
}
/**
* Return the list of recipients in the Bcc: header
* @return array
*/
public function getBcc()
{
if ($this->headers->has("Bcc"))
{
$bcc = $this->headers->get("Bcc");
if ($bcc == "") return array();
else return (array) $bcc;
}
}
/**
* Set the subject in the headers
* @param string The subject of the email
*/
public function setSubject($subject)
{
$this->headers->set("Subject", $subject);
}
/**
* Get the current subject used in the headers
* @return string
*/
public function getSubject()
{
return $this->headers->get("Subject");
}
/**
* Set the date in the headers in RFC 2822 format
* @param int The time as a UNIX timestamp
*/
public function setDate($date)
{
$this->headers->set("Date", date("r", $date));
}
/**
* Get the date as it looks in the headers
* @return string
*/
public function getDate()
{
return strtotime($this->headers->get("Date"));
}
/**
* Set the charset of the document
* @param string The charset used
*/
public function setCharset($charset)
{
$this->headers->setAttribute("Content-Type", "charset", $charset);
if (($this->getEncoding() == "7bit") && (strtolower($charset) == "utf-8" || strtolower($charset) == "utf8")) $this->setEncoding("8bit");
}
/**
* Get the charset used in the document
* Returns null if none is set
* @return string
*/
public function getCharset()
{
if ($this->headers->hasAttribute("Content-Type", "charset"))
{
return $this->headers->getAttribute("Content-Type", "charset");
}
else
{
return null;
}
}
/**
* Set the "format" attribute to flowed
* @param boolean On or Off
*/
public function setFlowed($flowed=true)
{
$value = null;
if ($flowed) $value = "flowed";
$this->headers->setAttribute("Content-Type", "format", $value);
}
/**
* Check if the message format is set as flowed
* @return boolean
*/
public function isFlowed()
{
if ($this->headers->hasAttribute("Content-Type", "format")
&& $this->headers->getAttribute("Content-Type", "format") == "flowed")
{
return true;
}
else return false;
}
/**
* Set the message prioirty in the mail client (don't rely on this)
* @param int The priority as a value between 1 (high) and 5 (low)
*/
public function setPriority($priority)
{
$priority = (int) $priority;
if ($priority > self::PRIORITY_LOW) $priority = self::PRIORITY_LOW;
if ($priority < self::PRIORITY_HIGH) $priority = self::PRIORITY_HIGH;
$label = array(1 => "High", 2 => "High", 3 => "Normal", 4 => "Low", 5 => "Low");
$this->headers->set("X-Priority", $priority);
$this->headers->set("X-MSMail-Priority", $label[$priority]);
$this->headers->set("X-MimeOLE", "Produced by SwiftMailer " . $this->libVersion);
}
/**
* Request that the client send back a read-receipt (don't rely on this!)
* @param string Request address
*/
public function requestReadReceipt($request)
{
if ($request instanceof Swift_Address) $request = $request->build();
if (!$request)
{
$this->headers->set("Disposition-Notification-To", null);
$this->headers->set("X-Confirm-Reading-To", null);
$this->headers->set("Return-Receipt-To", null);
}
else
{
$this->headers->set("Disposition-Notification-To", $request);
$this->headers->set("X-Confirm-Reading-To", $request);
$this->headers->set("Return-Receipt-To", $request);
}
}
/**
* Check if a read receipt has been requested for this message
* @return boolean
*/
public function wantsReadReceipt()
{
return $this->headers->has("Disposition-Notification-To");
}
/**
* Get the current message priority
* Returns NULL if none set
* @return int
*/
public function getPriority()
{
if ($this->headers->has("X-Priority")) return $this->headers->get("X-Priority");
else return null;
}
/**
* Alias for setData()
* @param mixed Body
*/
public function setBody($body)
{
$this->setData($body);
}
/**
* Alias for getData()
* @return mixed The document body
*/
public function getBody()
{
return $this->getData();
}
/**
* Set the MIME warning message which is displayed to old clients
* @var string The full warning message (in 7bit ascii)
*/
public function setMimeWarning($text)
{
$this->mimeWarning = (string) $text;
}
/**
* Get the MIME warning which is displayed to old clients
* @return string
*/
public function getMimeWarning()
{
return $this->mimeWarning;
}
/**
* Attach a mime part or an attachment of some sort
* Any descendant of Swift_Message_Mime can be added safely (including other Swift_Message objects for mail forwarding!!)
* @param Swift_Message_Mime The document to attach
* @param string An identifier to use (one is returned otherwise)
* @return string The identifier for the part
*/
public function attach(Swift_Message_Mime $child, $id=null)
{
try {
switch ($child->getLevel())
{
case Swift_Message_Mime::LEVEL_ALTERNATIVE:
$sign = (strtolower($child->getContentType()) == "text/plain") ? -1 : 1;
$id = $this->getReference("parent", "alternative")->addChild($child, $id, $sign);
$this->setReference("alternative", $id, $child);
break;
case Swift_Message_Mime::LEVEL_RELATED:
$id = "cid:" . $child->getContentId();
$id = $this->getReference("parent", "related")->addChild($child, $id, 1);
$this->setReference("related", $id, $child);
break;
case Swift_Message_Mime::LEVEL_MIXED: default:
$id = $this->getReference("parent", "mixed")->addChild($child, $id, 1);
$this->setReference("mixed", $id, $child);
break;
}
$this->postAttachFixStructure();
$this->fixContentType();
return $id;
} catch (Swift_Message_MimeException $e) {
throw new Swift_Message_MimeException("Something went wrong whilst trying to move some MIME parts during an attach(). " .
"The MIME component threw an exception:<br />" . $e->getMessage());
}
}
/**
* Remove a nested MIME part
* @param string The ID of the attached part
* @throws Swift_Message_MimeException If no such part exists
*/
public function detach($id)
{
try {
switch (true)
{
case array_key_exists($id, $this->references["alternative"]):
$this->getReference("parent", "alternative")->removeChild($id);
unset($this->references["alternative"][$id]);
break;
case array_key_exists($id, $this->references["related"]):
$this->getReference("parent", "related")->removeChild($id);
unset($this->references["related"][$id]);
break;
case array_key_exists($id, $this->references["mixed"]):
$this->getReference("parent", "mixed")->removeChild($id);
unset($this->references["mixed"][$id]);
break;
default:
throw new Swift_Message_MimeException("Unable to detach part identified by ID '" . $id . "' since it's not registered.");
break;
}
$this->postDetachFixStructure();
$this->fixContentType();
} catch (Swift_Message_MimeException $e) {
throw new Swift_Message_MimeException("Something went wrong whilst trying to move some MIME parts during a detach(). " .
"The MIME component threw an exception:<br />" . $e->getMessage());
}
}
/**
* Sets the correct content type header by looking at what types of data we have set
*/
protected function fixContentType()
{
if (!empty($this->references["mixed"])) $this->setContentType("multipart/mixed");
elseif (!empty($this->references["related"])) $this->setContentType("multipart/related");
elseif (!empty($this->references["alternative"])) $this->setContentType("multipart/alternative");
}
/**
* Move a branch of the tree, containing all it's MIME parts onto another branch
* @param string The content type on the branch itself
* @param string The content type which may exist in the branch's parent
* @param array The array containing all the nodes presently
* @param string The location of the branch now
* @param string The location of the branch after moving
* @param string The key to identify the branch by in it's new location
*/
protected function moveBranchIn($type, $nested_type, $from, $old_branch, $new_branch, $tag)
{
$new = new Swift_Message_Part();
$new->setContentType($type);
$this->getReference("parent", $new_branch)->addChild($new, $tag, -1);
switch ($new_branch)
{
case "related": $this->setReference("related", $tag, $new);//relatedRefs[$tag] = $new;
break;
case "mixed": $this->setReference("mixed", $tag, $new);//mixedRefs[$tag] = $new;
break;
}
foreach ($from as $id => $ref)
{
if (!$ref) $ref = $this;
$sign = (strtolower($ref->getContentType()) == "text/plain"
|| strtolower($ref->getContentType()) == $nested_type) ? -1 : 1;
switch ($new_branch)
{
case "related": $this->getReference("related", $tag)->addChild($ref, $id, $sign);
break;
case "mixed": $this->getReference("mixed", $tag)->addChild($ref, $id, $sign);
break;
}
$this->getReference("parent", $old_branch)->removeChild($id);
}
$this->setReference("parent", $old_branch, $new); //parentRefs[$old_branch] = $new;
}
/**
* Analyzes the mixing of MIME types in a mulitpart message an re-arranges if needed
* It looks complicated and long winded but the concept is pretty simple, even if putting it
* in code does me make want to cry!
*/
protected function postAttachFixStructure()
{
switch (true)
{
case (!empty($this->references["mixed"]) && !empty($this->references["related"]) && !empty($this->references["alternative"])):
if (!isset($this->references["related"]["_alternative"]))
{
$this->moveBranchIn(
"multipart/alternative", "multipart/alternative", $this->references["alternative"], "alternative", "related", "_alternative");
}
if (!isset($this->references["mixed"]["_related"]))
{
$this->moveBranchIn(
"multipart/related", "multipart/alternative", $this->references["related"], "related", "mixed", "_related");
}
break;
case (!empty($this->references["mixed"]) && !empty($this->references["related"])):
if (!isset($this->references["mixed"]["_related"]))
{
$this->moveBranchIn(
"multipart/related", "multipart/related", $this->references["related"], "related", "mixed", "_related");
}
break;
case (!empty($this->references["mixed"]) && !empty($this->references["alternative"])):
if (!isset($this->references["mixed"]["_alternative"]))
{
$this->moveBranchIn(
"multipart/alternative", null, $this->references["alternative"], "alternative", "mixed", "_alternative");
}
break;
case (!empty($this->references["related"]) && !empty($this->references["alternative"])):
if (!isset($this->references["related"]["_alternative"]))
{
$this->moveBranchIn(
"multipart/alternative", "multipart/alternative", $this->references["alternative"], "alternative", "related", "_alternative");
}
break;
}
}
/**
* Move a branch further toward the top of the tree
* @param array The array containing MIME parts from the old branch
* @param string The name of the old branch
* @param string The name of the new branch
* @param string The key of the branch being moved
*/
protected function moveBranchOut($from, $old_branch, $new_branch, $tag)
{
foreach ($from as $id => $ref)
{
if (!$ref) $ref = $this;
$sign = (strtolower($ref->getContentType()) == "text/html"
|| strtolower($ref->getContentType()) == "multipart/alternative") ? -1 : 1;
$this->getReference("parent", $new_branch)->addChild($ref, $id, $sign);
switch ($new_branch)
{
case "related": $this->getReference("related", $tag)->removeChild($id);
break;
case "mixed": $this->getReference("parent", $old_branch)->removeChild($id);
break;
}
}
$this->getReference("parent", $new_branch)->removeChild($tag);
$mixed = $this->getReference("parent", $new_branch);//parentRefs[$new_branch];
$this->setReference("parent", $old_branch, $mixed);//parentRefs[$old_branch] = $mixed;
switch ($new_branch)
{
case "related": unset($this->references["related"][$tag]);
break;
case "mixed": unset($this->references["mixed"][$tag]);
break;
}
}
/**
* Analyzes the mixing of MIME types in a mulitpart message an re-arranges if needed
* It looks complicated and long winded but the concept is pretty simple, even if putting it
* in code does me make want to cry!
*/
protected function postDetachFixStructure()
{
switch (true)
{
case (!empty($this->references["mixed"]) && !empty($this->references["related"]) && !empty($this->references["alternative"])):
if (array_keys($this->references["related"]) == array("_alternative"))
{
$alt = $this->getReference("parent", "related")->getChild("_alternative");
$this->getReference("parent", "mixed")->addChild($alt, "_alternative", -1);
$this->setReference("mixed", "_alternative", $alt);//mixedRefs["_alternative"] = $alt;
$this->getReference("parent", "related")->removeChild("_alternative");
unset($this->references["related"]["_alternative"]);
$this->getReference("parent", "mixed")->removeChild("_related");
unset($this->references["mixed"]["_related"]);
}
if (array_keys($this->references["mixed"]) == array("_related"))
{
$this->moveBranchOut($this->references["related"], "related", "mixed", "_related");
}
break;
case (!empty($this->references["mixed"]) && !empty($this->references["related"])):
if (array_keys($this->references["mixed"]) == array("_related"))
{
$this->moveBranchOut($this->references["related"], "related", "mixed", "_related");
}
if (isset($this->references["related"]["_alternative"]))
{
$this->detach("_alternative");
}
break;
case (!empty($this->references["mixed"]) && !empty($this->references["alternative"])):
if (array_keys($this->references["mixed"]) == array("_alternative"))
{
$this->moveBranchOut($this->references["alternative"], "alternative", "mixed", "_alternative");
}
break;
case (!empty($this->references["related"]) && !empty($this->references["alternative"])):
if (array_keys($this->references["related"]) == array("_alternative"))
{
$this->moveBranchOut($this->references["alternative"], "alternative", "related", "_alternative");
}
break;
case (!empty($this->references["mixed"])):
if (isset($this->references["mixed"]["_related"])) $this->detach("_related");
case (!empty($this->references["related"])):
if (isset($this->references["related"]["_alternative"]) || isset($this->references["mixed"]["_alternative"]))
$this->detach("_alternative");
break;
}
}
/**
* Execute needed logic prior to compilation
*/
public function preBuild()
{
$data = $this->getData();
if (!($enc = $this->getEncoding()))
{
$this->setEncoding("8bit");
}
if ($this->getCharset() === null && !$this->numChildren())
{
Swift_ClassLoader::load("Swift_Message_Encoder");
if (is_string($data) && Swift_Message_Encoder::instance()->isUTF8($data))
{
$this->setCharset("utf-8");
}
elseif(is_string($data) && Swift_Message_Encoder::instance()->is7BitAscii($data))
{
$this->setCharset("us-ascii");
if (!$enc) $this->setEncoding("7bit");
}
else $this->setCharset("iso-8859-1");
}
elseif ($this->numChildren())
{
if (!$this->getData())
{
$this->setData($this->getMimeWarning());
$this->setLineWrap(76);
}
if ($this->getCharset() !== null) $this->setCharset(null);
if ($this->isFlowed()) $this->setFlowed(false);
$this->setEncoding("7bit");
}
}
}

View File

@@ -0,0 +1,161 @@
<?php
/**
* Swift Mailer Message Attachment
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
/**
* Attachment component for Swift Mailer
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_Attachment extends Swift_Message_Mime
{
/**
* A numeric counter, incremented by 1 when a filename is made.
* @var int
*/
protected static $fileId = 0;
/**
* Constructor
* @param mixed The data to use in the body
* @param string Mime type
* @param string The encoding format used
* @param string The charset used
*/
public function __construct($data=null, $name=null, $type="application/octet-stream", $encoding="base64", $disposition="attachment")
{
parent::__construct();
$this->setContentType($type);
$this->setEncoding($encoding);
$this->setDescription($name);
$this->setDisposition($disposition);
$this->setFileName($name);
if ($data !== null) $this->setData($data, ($name === null));
}
/**
* Get a unique filename (just a sequence)
* @param string the prefix for the filename
* @return string
*/
public static function generateFileName($prefix="file")
{
return $prefix . (self::$fileId++);
}
/**
* Get the level in the MIME hierarchy at which this section should appear.
* @return string
*/
public function getLevel()
{
return Swift_Message_Mime::LEVEL_MIXED;
}
/**
* Overrides setData() in MIME so that a filename can be set
* @param mixed The data to set for the body
* @param boolean If the stream is a file, should it's filename be used?
* @throws Swift_FileException If the stream cannot be read
*/
public function setData($data, $read_filename=true)
{
parent::setData($data);
if ($read_filename && ($data instanceof Swift_file))
{
$this->setFileName($data->getFileName());
}
}
/**
* Set the name (and description) used to identify the file
* This method overrides any value previously set with setDescription()
* @param string The filename including it's extension if any
* @throws Swift_Message_MimeException If some required headers have been deliberately removed
*/
public function setFileName($name)
{
$this->headers->setAttribute("Content-Type", "name", $name);
$this->setDescription($name);
if ($this->headers->has("Content-Disposition"))
{
$this->headers->setAttribute("Content-Disposition", "filename", $name);
}
}
/**
* Get the filename of this attachment
* @return string
* @throws Swift_Message_MimeException If some vital headers have been removed
*/
public function getFileName()
{
if ($this->headers->hasAttribute("Content-Type", "name"))
{
return $this->headers->getAttribute("Content-Type", "name");
}
else return null;
}
/**
* Set the Content-Description header
* @param string The description in the header (filename usually!)
*/
public function setDescription($desc)
{
$this->headers->set("Content-Description", $desc);
}
/**
* Return the description in the headers
* @return string
*/
public function getDescription()
{
if ($this->headers->has("Content-Description"))
{
return $this->headers->get("Content-Description");
}
else return null;
}
/**
* Set the disposition of the attachment (usually inline or attachment)
* @param string The value to use in the Content-Disposition field
*/
public function setDisposition($disposition)
{
$this->headers->set("Content-Disposition", $disposition);
}
/**
* Get the disposition used in the attachment (usually inline or attachment)
* @return string
*/
public function getDisposition()
{
if ($this->headers->has("Content-Disposition"))
{
return $this->headers->get("Content-Disposition");
}
else return null;
}
/**
* Execute needed logic prior to building
*/
public function preBuild()
{
if ($this->getFileName() === null)
{
if ($this->getData() instanceof Swift_File)
{
$this->setFileName($this->getData()->getFileName());
}
else
{
$this->setFileName(self::generateFileName("file.att."));
}
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* Swift Mailer Embedded File (like an image or a midi file)
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Message_Attachment");
/**
* Embedded File component for Swift Mailer
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_EmbeddedFile extends Swift_Message_Attachment
{
/**
* The content-id in the headers (used in <img src=...> values)
* @var string
*/
protected $cid = null;
/**
* Constructor
* @param mixed The input source. Can be a file or a string
* @param string The filename to use, optional
* @param string The MIME type to use, optional
* @param string The Content-ID to use, optional
* @param string The encoding format to use, optional
*/
public function __construct($data=null, $name=null, $type="application/octet-stream", $cid=null, $encoding="base64")
{
parent::__construct($data, $name, $type, $encoding, "inline");
if ($cid === null)
{
$cid = self::generateFileName("swift-" . uniqid(time()) . ".");
$cid = urlencode($cid) . "@" . (!empty($_SERVER["SERVER_NAME"]) ? $_SERVER["SERVER_NAME"] : "swift");
}
$this->setContentId($cid);
if ($name === null && !($data instanceof Swift_File)) $this->setFileName($cid);
$this->headers->set("Content-Description", null);
}
/**
* Get the level in the MIME hierarchy at which this section should appear.
* @return string
*/
public function getLevel()
{
return Swift_Message_Mime::LEVEL_RELATED;
}
/**
* Set the Content-Id to use
* @param string The content-id
*/
public function setContentId($id)
{
$id = (string) $id;
$this->cid = $id;
$this->headers->set("Content-ID", "<" . $id . ">");
}
/**
* Get the content-id of this file
* @return string
*/
public function getContentId()
{
return $this->cid;
}
}

View File

@@ -0,0 +1,455 @@
<?php
/**
* Swift Mailer Message Encoder
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_File");
/**
* Encodes strings in a variety of formats and detects some encoding formats
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_Encoder
{
/**
* A regular expression which matches valid e-mail addresses (including some unlikely ones)
*/
const CHEAP_ADDRESS_RE = '(?#Start of dot-atom
)[-!#\$%&\'\*\+\/=\?\^_`{}\|~0-9A-Za-z]+(?:\.[-!#\$%&\'\*\+\/=\?\^_`{}\|~0-9A-Za-z]+)*(?#
End of dot-atom)(?:@(?#Start of domain)[-0-9A-Za-z]+(?:\.[-0-9A-Za-z]+)*(?#End of domain))?';
/**
* A singleton of this class
* @var Swift_Message_Encoder
*/
protected static $instance = null;
/**
* Retreive an instance of the encoder as a singleton.
* New instances are never ever needed since it's monostatic.
* @return Message_Encoder
*/
public static function instance()
{
if (self::$instance === null)
{
self::$instance = new Swift_Message_Encoder();
}
return self::$instance;
}
/**
* Break a string apart at every occurence of <add@ress> and return an array
* This method does NOT remove any characters like a preg_split() would do.
* Elements matching an address start with "a" followed by the numeric index
* @param string The input string to separate
* @return array
*/
public function addressChunk($input)
{
$elements = 0;
while (preg_match('/^(.*?)(<' . self::CHEAP_ADDRESS_RE . '>)/s', $input, $matches))
{
if (!empty($matches[1])) $ret[($elements++)] = $matches[1];
$ret[('a' . ($elements++))] = $matches[2];
$input = substr($input, strlen($matches[0]));
}
if ($input != "") $ret[($elements++)] = $input; //Whatever is left over
return $ret;
}
/**
* Break a string apart at every occurence of <xxxyyy> and return an array
* This method does NOT remove any characters like a preg_split() would do.
* Elements matching a quoted string start with "a" followed by the numeric index
* @param string The input string to separate
* @return array
*/
public function quoteChunk($input)
{
$elements = 0;
while (preg_match('/^(.*?)(<[\x20-\x3A\x3C-\x7E]*>)/s', $input, $matches))
{
if (!empty($matches[1])) $ret[($elements++)] = $matches[1];
$ret[('a' . ($elements++))] = $matches[2];
$input = substr($input, strlen($matches[0]));
}
if ($input != "") $ret[($elements++)] = $input; //Whatever is left over
return $ret;
}
/**
* Return the base64 encoded version of the string
* @param string The input string to encode
* @param int The maximum length of each line of output (inc CRLF)
* @param int The maximum length of the first line in the output (for headers)
* @param boolean Whether email addresses between < and > chars should be preserved or not
* @param string The line ending
* @return string
*/
public function base64Encode($data, $chunk=76, $init_chunk=0, $headers=false, $le="\r\n")
{
$ret = "";
$chunk -= 2;
$chunk = $this->getHcf($chunk, 4);
if ($init_chunk >= 2)
{
$init_chunk -= 2;
$init_chunk = $this->getHcf($init_chunk, 4);
}
if ($headers) $data = $this->quoteChunk($data);
else $data = array($data);
foreach ($data as $key => $string)
{
$key = (string) $key;
if ($key{0} == 'a') //This is an address
{
if ($init_chunk && $init_chunk < (strlen($string)+2)) $ret .= $le;
$ret .= $le . $string;
}
else
{
$string = $this->rawBase64Encode($string);
if ($init_chunk > 2)
{
$ret .= substr($string, 0, $init_chunk) . $le;
$string = substr($string, $init_chunk);
}
elseif ($init_chunk) $ret .= $le;
$ret .= trim(chunk_split($string, $chunk, $le)) . $le;
}
$init_chunk = 0;
}
return trim($ret);
}
/**
* Return the base64 encoded version of a string with no breaks
* @param The input string to encode
* @return string
*/
public function rawBase64Encode($string)
{
return $string = base64_encode($string);
}
/**
* Return the base64 encoded version of a file
* @param Swift_File The file input stream
* @param int Max line length
* @param string The line ending
* @return Swift_Cache_OutputStream
* @throws Swift_FileException If the file cannot be read
*/
public function base64EncodeFile(Swift_File $file, $chunk=76, $le="\r\n")
{
Swift_ClassLoader::load("Swift_CacheFactory");
$cache = Swift_CacheFactory::getCache();
$chunk -= 2;
$chunk = $this->getHcf($chunk, 4);
$loop = false;
//We have to read in multiples of 3 bytes but avoid doing such small chunks that it takes too long
while (false !== $bytes = $file->read(8190))
{
if ($loop) $cache->write("b64", $le);
$loop = true;
$next = chunk_split($this->rawBase64Encode($bytes), $chunk, $le);
$next = trim($next);
$cache->write("b64", $next);
}
$file->reset();
return $cache->getOutputStream("b64");
}
/**
* Return the quoted printable version of the input string
* @param string The input string to encode
* @param int The maximum length of each line of output (inc CRLF)
* @param int The maximum length of the first line in the output (for headers)
* @param boolean Whether email addresses between < and > chars should be preserved or not
* @param string The line ending
* @return string
*/
public function QPEncode($data, $chunk=76, $init_chunk=0, $headers=false, $le="\r\n")
{
$ret = "";
if ($headers) $data = $this->quoteChunk($data);
else $data = array($data);
$trailing_spaces = chr(9) . chr(32);
foreach ($data as $key => $string)
{
$key = (string) $key;
if ($key{0} == 'a') //An address
{
if ($init_chunk && $init_chunk < (strlen($string)+3)) $ret .= "=";
$ret .= $le . $string;
}
else
{
$lines = explode($le, $string);
foreach ($lines as $n => $line)
$lines[$n] = $this->rawQPEncode(rtrim($line, $trailing_spaces));
$string = implode($le, $lines);
if ($init_chunk > 3)
{
if (preg_match('/^.{1,'.($init_chunk-5).'}[^=]{2}(?!=[A-F0-9]{2})/', $string, $matches)
|| preg_match('/^.{1,'.($init_chunk-6).'}([^=]{0,3})?/', $string, $matches))
{
$ret .= $this->fixLE($matches[0] . "=", $le); //fixLE added 24/08/07
$string = substr($string, strlen($matches[0]));
}
}
elseif ($init_chunk) $ret .= "=";
while (preg_match('/^.{1,'.($init_chunk-5).'}[^=]{2}(?!=[A-F0-9]{2})/', $string, $matches)
|| preg_match('/^.{1,'.($chunk-6).'}([^=]{0,3})?/', $string, $matches)
|| (strlen($string) > 0 && $matches = array($string)))
{
$ret .= $this->fixLE($le . $matches[0] . "=", $le); //fixLE added 24/08/07
$string = substr($string, strlen($matches[0]));
}
}
$init_chunk = 0;
}
if (substr($ret, -1) == "=") return trim(substr($ret, 0, -1));
else return trim($ret);
}
/**
* Return the QP encoded version of a string with no breaks
* @param string The input to encode
* @param boolean True if the data we're encoding is binary
* @return string
*/
public function rawQPEncode($string, $bin=false)
{
$ret = "";
if (!$bin)
{
$string = str_replace(array("\r\n", "\r"), "\n", $string);
$string = str_replace("\n", "\r\n", $string);
}
$len = strlen($string);
for ($i = 0; $i < $len; $i++)
{
$val = ord($string{$i});
//9, 32 = HT, SP; 10, 13 = CR, LF; 33-60 & 62-126 are ok
// 63 = '?'; 95 = '_' and need encoding to go in the headers
if ((!$bin && ($val == 32 || $val == 9 || $val == 10 || $val == 13))
|| ($val >= 33 && $val <= 60) || ($val >= 62 && $val <= 126)
&& $val != 63)
{
$ret .= $string{$i};
}
else
{
$ret .= sprintf("=%02X", $val);
}
}
return $ret;
}
/**
* Return a file as a quoted printable encoded string
* @param Swift_File The file to encode
* @param int Max line length
* @param string The line ending
* @return Swift_Cache_OutputStream
* @throws Swift_FileException If the file cannot be read
*/
public function QPEncodeFile(Swift_File $file, $chunk=76, $le="\r\n")
{
Swift_ClassLoader::load("Swift_CacheFactory");
$cache = Swift_CacheFactory::getCache();
while (false !== $bytes = $file->readln())
{
$next = $this->rawQPEncode($bytes, true);
preg_match_all('/.{1,'.($chunk-6).'}([^=]{0,3})?/', $next, $next);
if (count($next[0])) $cache->write("qp", $this->fixLE(implode("=" . $le, $next[0]), $le));
}
return $cache->getOutputStream("qp");
}
/**
* Encode a string as 7bit ascii
* @param string Input data to encode
* @param int Max line length
* @param string The line ending
* @return string
*/
public function encode7Bit($data, $chunk=76, $le="\r\n")
{
return $this->fixLE(wordwrap($data, $chunk-2, $le, 1), $le);
}
/**
* Return a 7bit string from a file
* @param Swift_File The file stream to read from
* @param int The max line length
* @param string The line ending
* @return Swift_Cache_OutputStream
* @throws Swift_FileException If the file cannot be read
*/
public function encode7BitFile(Swift_File $file, $chunk=76, $le="\r\n")
{
Swift_ClassLoader::load("Swift_CacheFactory");
$cache = Swift_CacheFactory::getCache();
$ret = "";
while (false !== $bytes = $file->read(8192)) $ret .= $bytes;
$cache->write("7b", $this->fixLE(wordwrap($ret, $chunk-2, $le, 1), $le));
return $cache->getOutputStream("7b");
}
/**
* Return the 8bit encoded form of a string (unchanged there-abouts)
* @param string Input data to encode
* @param int Maximum line length
* @param string The line ending
* @return string
*/
public function encode8Bit($data, $chunk=76, $le="\r\n")
{
return $this->fixLE(wordwrap($data, $chunk-2, $le, 1), $le);
}
/**
* Return a 8bit string from a file
* @param Swift_File The file stream to read from
* @param int Max line length (including CRLF)
* @param string The line ending
* @return Swift_Cache_OutputStream
* @throws Swift_FileException If the file cannot be read
*/
public function encode8BitFile(Swift_File $file, $chunk=76, $le="\r\n")
{
Swift_ClassLoader::load("Swift_CacheFactory");
$cache = Swift_CacheFactory::getCache();
$ret = "";
while (false !== $bytes = $file->read(8192)) $ret .= $bytes;
$cache->write("8b", $this->fixLE(wordwrap($ret, $chunk-2, $le, 1), $le));
return $cache->getOutputStream("8b");
}
/**
* Keeps lines longer than 76 characters trimmed down to size
* This currently does not convert other string encodings into 7bit
* @param string The data to make safe for headers (defaults to RFC 2822 standards)
* @param int maximum length of lines returned
* @param int The maximum length of the first line
* @param string The Line ending
* @return string
*/
public function header7BitEncode($data, $chunk=76, $init_chunk=0, $le="\r\n")
{
$data = $this->encode7BitPrintable($data);
$ret = "";
if ($init_chunk > 2)
{
$data_wrapped = wordwrap($data, $init_chunk, $le);
$lines = explode($le, $data_wrapped);
$first_line = array_shift($lines);
$ret .= $first_line . $le;
$data = preg_replace("~^[ \t]~D", "", substr($data, strlen($first_line)));
}
elseif ($init_chunk) $ret .= $le;
$ret .= wordwrap($data, $chunk-2, $le);
return trim($ret);
}
/**
* Strip out any characters which are not in the ASCII 7bit printable range
* @param string The string to clean
* @return string
*/
public function encode7BitPrintable($data)
{
return preg_replace('/[^\x20-\x7E]/', '', $data);
}
/**
* Detect if a string contains multi-byte non-ascii chars that fall in the UTF-8 ranges
* @param string Data to detect UTF-8 sequences in
* @return boolean
*/
public function isUTF8($data)
{
return preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)+%xs', $data);
}
/**
* This function checks for 7bit *printable* characters
* which excludes \r \n \t etc and so, is safe for use in mail headers
* Actual permitted chars [\ !"#\$%&'\(\)\*\+,-\.\/0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz{\|}~]
* Ranges \x00-\x1F are printer control sequences
* \x7F is the ascii delete character
* @param string Data to check against
* @return boolean
*/
public function is7BitPrintable($data)
{
return (!preg_match('/[^\x20-\x7E]/', $data));
}
/**
* Check that a string does not contain any evil characters for headers.
* @param string The string to check
* @return boolean
*/
public function isHeaderSafe($data)
{
return ($this->is7BitPrintable($data) && strpos($data, ";") === false);
}
/**
* If the characters fall exclusively in the 7bit ascii range, return true
* @param string Input to check
* @return boolean
*/
public function is7BitAscii($data)
{
return (!preg_match('/[^\x01-\x7F]/', $data));
}
/**
* Encode a string for RFC 2047 compatability (url-encode)
* @param string The input for encoding
* @param string The charset used
* @param string The language used
* @param int The maximum line length
* @param int The maximum length of the first line
* @param string The line ending
* @return string
*/
public function rfc2047Encode($str, $charset="iso-8859-1", $language="en-us", $chunk=76, $le="\r\n")
{
$lang_spec = "";
if (!$this->is7BitPrintable($str))
{
$lang_spec = $charset . "'" . $language . "'";
$str = $lang_spec . str_replace("+", "%20", urlencode($str));
}
preg_match_all('~.{1,'.($chunk-6).'}([^%]{0,3})~', $str, $matches);
if (count($matches[0])) return implode($le, $matches[0]);
}
/**
* Fixes line endings to be whatever is specified by the user
* SMTP requires the CRLF be used, but using sendmail in -t mode uses LF
* This method also escapes dots on a start of line to avoid injection
* @param string The data to fix
* @return string
*/
protected function fixLE($data, $le)
{
$data = str_replace(array("\r\n", "\r"), "\n", $data);
if ($le != "\n") $data = str_replace("\n", $le, $data);
return $data = str_replace($le . ".", $le . "..", $data);
}
protected function getHcf($value, $factor)
{
return ($value - ($value % $factor));
}
}

View File

@@ -0,0 +1,573 @@
<?php
/**
* Swift Mailer MIME Library Headers component
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
/**
* Contains and constructs the headers for a MIME document
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_Headers
{
/**
* Headers which may contain email addresses, and therefore should take notice when encoding
* @var array headers
*/
protected $emailContainingHeaders = array(
"To", "From", "Reply-To", "Cc", "Bcc", "Return-Path", "Sender");
/**
* The encoding format used for the body of the document
* @var string format
*/
protected $encoding = "B";
/**
* The charset used in the headers
* @var string
*/
protected $charset = false;
/**
* A collection of headers
* @var array headers
*/
protected $headers = array();
/**
* A container of references to the headers
* @var array
*/
protected $lowerHeaders = array();
/**
* Attributes appended to headers
* @var array
*/
protected $attributes = array();
/**
* If QP or Base64 encoding should be forced
* @var boolean
*/
protected $forceEncoding = false;
/**
* The language used in the headers (doesn't really matter much)
* @var string
*/
protected $language = "en-us";
/**
* Cached, pre-built headers
* @var string
*/
protected $cached = array();
/**
* The line ending used in the headers
* @var string
*/
protected $LE = "\r\n";
/**
* Set the line ending character to use
* @param string The line ending sequence
* @return boolean
*/
public function setLE($le)
{
if (in_array($le, array("\r", "\n", "\r\n")))
{
foreach (array_keys($this->cached) as $k) $this->cached[$k] = null;
$this->LE = $le;
return true;
}
else return false;
}
/**
* Get the line ending sequence
* @return string
*/
public function getLE()
{
return $this->LE;
}
/**
* Reset the cache state in these headers
*/
public function uncacheAll()
{
foreach (array_keys($this->cached) as $k)
{
$this->cached[$k] = null;
}
}
/**
* Add a header or change an existing header value
* @param string The header name, for example "From" or "Subject"
* @param string The value to be inserted into the header. This is safe from header injection.
*/
public function set($name, $value)
{
$lname = strtolower($name);
if (!isset($this->lowerHeaders[$lname]))
{
$this->headers[$name] = null;
$this->lowerHeaders[$lname] =& $this->headers[$name];
}
$this->cached[$lname] = null;
Swift_ClassLoader::load("Swift_Message_Encoder");
if (is_array($value))
{
foreach ($value as $v)
{
if (!$this->getCharset() && Swift_Message_Encoder::instance()->isUTF8($v))
{
$this->setCharset("utf-8");
break;
}
}
}
elseif ($value !== null)
{
if (!$this->getCharset() && Swift_Message_Encoder::instance()->isUTF8($value))
{
$this->setCharset("utf-8");
}
}
if (!is_array($value) && $value !== null) $this->lowerHeaders[$lname] = (string) $value;
else $this->lowerHeaders[$lname] = $value;
}
/**
* Get the value at a given header
* @param string The name of the header, for example "From" or "Subject"
* @return string
* @throws Swift_Message_MimeException If no such header exists
* @see hasHeader
*/
public function get($name)
{
$lname = strtolower($name);
if ($this->has($name))
{
return $this->lowerHeaders[$lname];
}
}
/**
* Remove a header from the list
* @param string The name of the header
*/
public function remove($name)
{
$lname = strtolower($name);
if ($this->has($name))
{
unset($this->headers[$name]);
unset($this->lowerHeaders[$lname]);
unset($this->cached[$lname]);
if (isset($this->attributes[$lname])) unset($this->attributes[$lname]);
}
}
/**
* Just fetch the array containing the headers
* @return array
*/
public function getList()
{
return $this->headers;
}
/**
* Check if a header has been set or not
* @param string The name of the header, for example "From" or "Subject"
* @return boolean
*/
public function has($name)
{
$lname = strtolower($name);
return (array_key_exists($lname, $this->lowerHeaders) && $this->lowerHeaders[$lname] !== null);
}
/**
* Set the language used in the headers to $lang (e.g. en-us, en-gb, sv etc)
* @param string The language to use
*/
public function setLanguage($lang)
{
$this->language = (string) $lang;
}
/**
* Get the language used in the headers to $lang (e.g. en-us, en-gb, sv etc)
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set the charset used in the headers
* @param string The charset name
*/
public function setCharset($charset)
{
$this->charset = (string) $charset;
}
/**
* Get the current charset used
* @return string
*/
public function getCharset()
{
return $this->charset;
}
/**
* Specify the encoding to use for the headers if characters outside the 7-bit-printable ascii range are found
* This encoding will never be used if only 7-bit-printable characters are found in the headers.
* Possible values are:
* - QP
* - Q
* - Quoted-Printable
* - B
* - Base64
* NOTE: Q, QP, Quoted-Printable are all the same; as are B and Base64
* @param string The encoding format to use
* @return boolean
*/
public function setEncoding($encoding)
{
switch (strtolower($encoding))
{
case "qp": case "q": case "quoted-printable":
$this->encoding = "Q";
return true;
case "base64": case "b":
$this->encoding = "B";
return true;
default: return false;
}
}
/**
* Get the encoding format used in this document
* @return string
*/
public function getEncoding()
{
return $this->encoding;
}
/**
* Turn on or off forced header encoding
* @param boolean On/Off
*/
public function forceEncoding($force=true)
{
$this->forceEncoding = (boolean) $force;
}
/**
* Set an attribute in a major header
* For example $headers->setAttribute("Content-Type", "format", "flowed")
* @param string The main header these values exist in
* @param string The name for this value
* @param string The value to set
* @throws Swift_Message_MimeException If no such header exists
*/
public function setAttribute($header, $name, $value)
{
$name = strtolower($name);
$lheader = strtolower($header);
$this->cached[$lheader] = null;
if (!$this->has($header))
{
throw new Swift_Message_MimeException(
"Cannot set attribute '" . $name . "' for header '" . $header . "' as the header does not exist. " .
"Consider using Swift_Message_Headers-&gt;has() to check.");
}
else
{
Swift_ClassLoader::load("Swift_Message_Encoder");
if (!$this->getCharset() && Swift_Message_Encoder::instance()->isUTF8($value)) $this->setCharset("utf-8");
if (!isset($this->attributes[$lheader])) $this->attributes[$lheader] = array();
if ($value !== null) $this->attributes[$lheader][$name] = (string) $value;
else $this->attributes[$lheader][$name] = $value;
}
}
/**
* Check if a header has a given attribute applied to it
* @param string The name of the main header
* @param string The name of the attribute
* @return boolean
*/
public function hasAttribute($header, $name)
{
$name = strtolower($name);
$lheader = strtolower($header);
if (!$this->has($header))
{
return false;
}
else
{
return (isset($this->attributes[$lheader]) && isset($this->attributes[$lheader][$name]) && ($this->attributes[$lheader][$name] !== null));
}
}
/**
* Get the value for a given attribute on a given header
* @param string The name of the main header
* @param string The name of the attribute
* @return string
* @throws Swift_Message_MimeException If no header is set
*/
public function getAttribute($header, $name)
{
if (!$this->has($header))
{
throw new Swift_Message_MimeException(
"Cannot locate attribute '" . $name . "' for header '" . $header . "' as the header does not exist. " .
"Consider using Swift_Message_Headers-&gt;has() to check.");
}
$name = strtolower($name);
$lheader = strtolower($header);
if ($this->hasAttribute($header, $name))
{
return $this->attributes[$lheader][$name];
}
}
/**
* Remove an attribute from a header
* @param string The name of the header to remove the attribute from
* @param string The name of the attribute to remove
*/
public function removeAttribute($header, $name)
{
$name = strtolower($name);
$lheader = strtolower($header);
if ($this->has($header))
{
unset($this->attributes[$lheader][$name]);
}
}
/**
* Get a list of all the attributes in the given header.
* @param string The name of the header
* @return array
*/
public function listAttributes($header)
{
$header = strtolower($header);
if (array_key_exists($header, $this->attributes))
{
return $this->attributes[$header];
}
else return array();
}
/**
* Get the header in it's compliant, encoded form
* @param string The name of the header
* @return string
* @throws Swift_Message_MimeException If the header doesn't exist
*/
public function getEncoded($name)
{
if (!$this->getCharset()) $this->setCharset("iso-8859-1");
Swift_ClassLoader::load("Swift_Message_Encoder");
//I'll try as best I can to walk through this...
$lname = strtolower($name);
if ($this->cached[$lname] !== null) return $this->cached[$lname];
$value = $this->get($name);
$is_email = in_array($name, $this->emailContainingHeaders);
$encoded_value = (array) $value; //Turn strings into arrays (just to make the following logic simpler)
//Look at each value in this header
// There will only be 1 value if it was a string to begin with, and usually only address lists will be multiple
foreach ($encoded_value as $key => $row)
{
$spec = ""; //The bit which specifies the encoding of the header (if any)
$end = ""; //The end delimiter for an encoded header
//If the header is 7-bit printable it's at no risk of injection
if (Swift_Message_Encoder::instance()->isHeaderSafe($row) && !$this->forceEncoding)
{
//Keeps the total line length at less than 76 chars, taking into account the Header name length
$encoded_value[$key] = Swift_Message_Encoder::instance()->header7BitEncode(
$row, 72, ($key > 0 ? 0 : (75-(strlen($name)+5))), $this->LE);
}
elseif ($this->encoding == "Q") //QP encode required
{
$spec = "=?" . $this->getCharset() . "?Q?"; //e.g. =?iso-8859-1?Q?
$end = "?=";
//Calculate the length of, for example: "From: =?iso-8859-1?Q??="
$used_length = strlen($name) + 2 + strlen($spec) + 2;
//Encode to QP, excluding the specification for now but keeping the lines short enough to be compliant
$encoded_value[$key] = str_replace(" ", "_", Swift_Message_Encoder::instance()->QPEncode(
$row, (75-(strlen($spec)+6)), ($key > 0 ? 0 : (75-$used_length)), true, $this->LE));
}
elseif ($this->encoding == "B") //Need to Base64 encode
{
//See the comments in the elseif() above since the logic is the same (refactor?)
$spec = "=?" . $this->getCharset() . "?B?";
$end = "?=";
$used_length = strlen($name) + 2 + strlen($spec) + 2;
$encoded_value[$key] = Swift_Message_Encoder::instance()->base64Encode(
$row, (75-(strlen($spec)+5)), ($key > 0 ? 0 : (76-($used_length+3))), true, $this->LE);
}
if (false !== $p = strpos($encoded_value[$key], $this->LE))
{
$cb = 'str_replace("' . $this->LE . '", "", "<$1>");';
$encoded_value[$key] = preg_replace("/<([^>]+)>/e", $cb, $encoded_value[$key]);
}
//Turn our header into an array of lines ready for wrapping around the encoding specification
$lines = explode($this->LE, $encoded_value[$key]);
for ($i = 0, $len = count($lines); $i < $len; $i++)
{
//Don't allow commas in address fields without quotes unless they're encoded
if (empty($spec) && $is_email && (false !== $p = strpos($lines[$i], ",")))
{
$s = strpos($lines[$i], " <");
$e = strpos($lines[$i], ">");
if ($s < $e)
{
$addr = substr($lines[$i], $s);
$lines[$i] = "\"" . substr($lines[$i], 0, $s) . "\"" . $addr;
}
else
{
$lines[$i] = "\"" . $lines[$i] . "\"";
}
}
if ($this->encoding == "Q") $lines[$i] = rtrim($lines[$i], "=");
if ($lines[$i] == "" && $i > 0)
{
unset($lines[$i]); //Empty line, we'd rather not have these in the headers thank you!
continue;
}
if ($i > 0)
{
//Don't stick the specification part around the line if it's an address
if (substr($lines[$i], 0, 1) == '<' && substr($lines[$i], -1) == '>') $lines[$i] = " " . $lines[$i];
else $lines[$i] = " " . $spec . $lines[$i] . $end;
}
else
{
if (substr($lines[$i], 0, 1) != '<' || substr($lines[$i], -1) != '>') $lines[$i] = $spec . $lines[$i] . $end;
}
}
//Build back into a string, now includes the specification
$encoded_value[$key] = implode($this->LE, $lines);
$lines = null;
}
//If there are multiple values in this header, put them on separate lines, cleared by commas
$this->cached[$lname] = implode("," . $this->LE . " ", $encoded_value);
//Append attributes if there are any
if (!empty($this->attributes[$lname])) $this->cached[$lname] .= $this->buildAttributes($this->cached[$lname], $lname);
return $this->cached[$lname];
}
/**
* Build the list of attributes for appending to the given header
* This is RFC 2231 & 2047 compliant.
* A HUGE thanks to Joaquim Homrighausen for heaps of help, advice
* and testing to get this working rock solid.
* @param string The header built without attributes
* @param string The lowercase name of the header
* @return string
* @throws Swift_Message_MimeException If no such header exists or there are no attributes
*/
protected function buildAttributes($header_line, $header_name)
{
Swift_ClassLoader::load("Swift_Message_Encoder");
$lines = explode($this->LE, $header_line);
$used_len = strlen($lines[count($lines)-1]);
$lines= null;
$ret = "";
foreach ($this->attributes[$header_name] as $attribute => $att_value)
{
if ($att_value === null) continue;
// 70 to account for LWSP, CRLF, quotes and a semi-colon
// + length of attribute
// + 4 for a 2 digit number and 2 asterisks
$avail_len = 70 - (strlen($attribute) + 4);
$encoded = Swift_Message_Encoder::instance()->rfc2047Encode($att_value, $this->charset, $this->language, $avail_len, $this->LE);
$lines = explode($this->LE, $encoded);
foreach ($lines as $i => $line)
{
//Add quotes if needed (RFC 2045)
if (preg_match("~[\\s\";,<>\\(\\)@:\\\\/\\[\\]\\?=]~", $line)) $lines[$i] = '"' . $line . '"';
}
$encoded = implode($this->LE, $lines);
//If we can fit this entire attribute onto the same line as the header then do it!
if ((strlen($encoded) + $used_len + strlen($attribute) + 4) < 74)
{
if (strpos($encoded, "'") !== false) $attribute .= "*";
$append = "; " . $attribute . "=" . $encoded;
$ret .= $append;
$used_len += strlen($append);
}
else //... otherwise list of underneath
{
$ret .= ";";
if (count($lines) > 1)
{
$loop = false;
$add_asterisk = false;
foreach ($lines as $i => $line)
{
$att_copy = $attribute; //Because it's multi-line it needs asterisks with decimal indices
$att_copy .= "*" . $i;
if ($add_asterisk || strpos($encoded, "'") !== false)
{
$att_copy .= "*"; //And if it's got a ' then it needs another asterisk
$add_asterisk = true;
}
$append = "";
if ($loop) $append .= ";";
$append .= $this->LE . " " . $att_copy . "=" . $line;
$ret .= $append;
$used_len = strlen($append)+1;
$loop = true;
}
}
else
{
if (strpos($encoded, "'") !== false) $attribute .= "*";
$append = $this->LE . " " . $attribute . "=" . $encoded;
$used_len = strlen($append)+1;
$ret .= $append;
}
}
$lines= null;
}
return $ret;
}
/**
* Compile the list of headers which have been set and return an ascii string
* The return value should always be 7-bit ascii and will have been cleaned for header injection
* If this looks complicated it's probably because it is!! Keeping everything compliant is not easy.
* This is RFC 2822 compliant
* @return string
*/
public function build()
{
$ret = "";
foreach ($this->headers as $name => $value) //Look at each header
{
if ($value === null) continue;
$ret .= ltrim($name, ".") . ": " . $this->getEncoded($name) . $this->LE;
}
return trim($ret);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Swift Mailer Image Component
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Message_EmbeddedFile");
/**
* Embedded Image component for Swift Mailer
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_Image extends Swift_Message_EmbeddedFile
{
/**
* Constructor
* @param Swift_File The input source file
* @param string The filename to use, optional
* @param string The MIME type to use, optional
* @param string The Content-ID to use, optional
* @param string The encoding format to use, optional
*/
public function __construct(Swift_File $data=null, $name=null, $type="application/octet-stream", $cid=null, $encoding="base64")
{
parent::__construct($data, $name, $type, $cid, $encoding);
}
/**
* Set data for the image
* This overrides setData() in Swift_Message_Attachment
* @param Swift_File The data to set, as a file
* @throws Swift_Message_MimeException If the image cannot be used, or the file is not
*/
public function setData($data, $read_filename=true)
{
if (!($data instanceof Swift_File)) throw new Exception("Parameter 1 of " . __METHOD__ . " must be instance of Swift_File");
parent::setData($data, $read_filename);
$img_data = @getimagesize($data->getPath());
if (!$img_data)
{
throw new Swift_Message_MimeException(
"Cannot use file '" . $data->getPath() . "' as image since getimagesize() was unable to detect a file format. " .
"Try using Swift_Message_EmbeddedFile instead");
}
$type = image_type_to_mime_type($img_data[2]);
$this->setContentType($type);
if (!$this->getFileName()) $this->setFileName($data->getFileName());
}
}

View File

@@ -0,0 +1,500 @@
<?php
/**
* Swift Mailer MIME Library central component
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_File");
Swift_ClassLoader::load("Swift_Message_MimeException");
/**
* Mime is the underbelly for Messages, Attachments, Parts, Embedded Images, Forwarded Mail, etc
* In fact, every single component of the composed email is simply a new Mime document nested inside another
* When you piece an email together in this way you see just how straight-forward it really is
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_Message_Mime
{
/**
* Constant for plain-text emails
*/
const PLAIN = "text/plain";
/**
* Constant for HTML emails
*/
const HTML = "text/html";
/**
* Constant for miscellaneous mime type
*/
const MISC = "application/octet-stream";
/**
* Constant for MIME sections which must appear in the multipart/alternative section.
*/
const LEVEL_ALTERNATIVE = "alternative";
/**
* Constant for MIME sections which must appear in the multipart/related section.
*/
const LEVEL_RELATED = "related";
/**
* Constant for MIME sections which must appear in the multipart/mixed section.
*/
const LEVEL_MIXED = "mixed";
/**
* Constant for MIME sections which must appear in the multipart/mixed section.
*/
const LEVEL_TOP = "top";
/**
* Constant for safe line length in almost all places
*/
const SAFE_LENGTH = 1000; //RFC 2822
/**
* Constant for really safe line length
*/
const VERY_SAFE_LENGTH = 76; //For command line mail clients such as pine
/**
* The header part of this MIME document
* @var Swift_Message_Headers
*/
public $headers = null;
/**
* The body of the documented (unencoded)
* @var string data
*/
protected $data = "";
/**
* Maximum line length
* @var int
*/
protected $wrap = 1000; //RFC 2822
/**
* Nested mime parts
* @var array
*/
protected $children = array();
/**
* The boundary used to separate mime parts
* @var string
*/
protected $boundary = null;
/**
* The line ending characters needed
* @var string
*/
protected $LE = "\r\n";
/**
* An instance of Swift_Cache
* @var Swift_Cache
*/
protected $cache;
/**
* A list of used MIME boundaries after they're generated.
* @var array
*/
protected static $usedBoundaries = array();
/**
* Constructor
*/
public function __construct()
{
Swift_ClassLoader::load("Swift_Message_Headers");
$this->setHeaders(new Swift_Message_Headers());
Swift_ClassLoader::load("Swift_CacheFactory");
$this->cache = Swift_CacheFactory::getCache();
}
/**
* Compute a unique boundary
* @return string
*/
public static function generateBoundary()
{
do
{
$boundary = uniqid(rand(), true);
} while (in_array($boundary, self::$usedBoundaries));
self::$usedBoundaries[] = $boundary;
return "_=_swift-" . $boundary . "_=_";
}
/**
* Replace the current headers with new ones
* DO NOT DO THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
* @param Swift_Message_Headers The headers to use
*/
public function setHeaders($headers)
{
$this->headers = $headers;
}
/**
* Set the line ending character to use
* @param string The line ending sequence
* @return boolean
*/
public function setLE($le)
{
if (in_array($le, array("\r", "\n", "\r\n")))
{
$this->cache->clear("body");
$this->LE = $le;
//This change should be recursive
$this->headers->setLE($le);
foreach ($this->children as $id => $child)
{
$this->children[$id]->setLE($le);
}
return true;
}
else return false;
}
/**
* Get the line ending sequence
* @return string
*/
public function getLE()
{
return $this->LE;
}
/**
* Reset the entire cache state from this branch of the tree and traversing down through the children
*/
public function uncacheAll()
{
$this->cache->clear("body");
$this->cache->clear("append");
$this->cache->clear("headers");
$this->cache->clear("dbl_le");
$this->headers->uncacheAll();
foreach ($this->children as $id => $child)
{
$this->children[$id]->uncacheAll();
}
}
/**
* Set the content type of this MIME document
* @param string The content type to use in the same format as MIME 1.0 expects
*/
public function setContentType($type)
{
$this->headers->set("Content-Type", $type);
}
/**
* Get the content type which has been set
* The MIME 1.0 Content-Type is provided as a string
* @return string
*/
public function getContentType()
{
try {
return $this->headers->get("Content-Type");
} catch (Swift_Message_MimeException $e) {
return false;
}
}
/**
* Set the encoding format to be used on the body of the document
* @param string The encoding type used
* @param boolean If this encoding format should be used recursively. Note, this only takes effect if no encoding is set in the children.
* @param boolean If the encoding should only be applied when the string is not ascii.
*/
public function setEncoding($encoding, $recursive=false, $non_ascii=false)
{
$this->cache->clear("body");
switch (strtolower($encoding))
{
case "q": case "qp": case "quoted-printable":
$encoding = "quoted-printable";
break;
case "b": case "base64":
$encoding = "base64";
break;
case "7bit": case "8bit": case "binary":
$encoding = strtolower($encoding);
break;
}
$data = $this->getData();
Swift_ClassLoader::load("Swift_Message_Encoder");
if ($non_ascii && is_string($data) && strlen($data) > 0 && !Swift_Message_Encoder::instance()->is7BitAscii($data))
{
$this->headers->set("Content-Transfer-Encoding", $encoding);
}
elseif (!$non_ascii || !is_string($data))
{
$this->headers->set("Content-Transfer-Encoding", $encoding);
}
if ($recursive)
{
foreach ($this->children as $id => $child)
{
if (!$child->getEncoding()) $this->children[$id]->setEncoding($encoding, $recursive, $non_ascii);
}
}
}
/**
* Get the encoding format used in this document
* @return string
*/
public function getEncoding()
{
try {
return $this->headers->get("Content-Transfer-Encoding");
} catch (Swift_Message_MimeException $e) {
return false;
}
}
/**
* Specify the string which makes up the body of this message
* HINT: You can always nest another MIME document here if you call it's build() method.
* $data can be an object of Swift_File or a string
* @param mixed The body of the document
*/
public function setData($data)
{
$this->cache->clear("body");
if ($data instanceof Swift_File) $this->data = $data;
else $this->data = (string) $data;
}
/**
* Return the string which makes up the body of this MIME document
* @return string,Swift_File
*/
public function getData()
{
return $this->data;
}
/**
* Get the data in the format suitable for sending
* @return Swift_Cache_OutputStream
* @throws Swift_FileException If the file stream given cannot be read
* @throws Swift_Message_MimeException If some required headers have been forcefully removed
*/
public function buildData()
{
Swift_ClassLoader::load("Swift_Message_Encoder");
Swift_ClassLoader::load("Swift_Cache_JointOutputStream");
if (!empty($this->children)) //If we've got some mime parts we need to stick them onto the end of the message
{
if ($this->boundary === null) $this->boundary = self::generateBoundary();
$this->headers->setAttribute("Content-Type", "boundary", $this->boundary);
$this->cache->clear("append");
foreach ($this->children as $part)
{
$this->cache->write("append", $this->LE . "--" . $this->boundary . $this->LE);
$part_stream = $part->build();
while (false !== $bytes = $part_stream->read()) $this->cache->write("append", $bytes);
}
$this->cache->write("append", $this->LE . "--" . $this->boundary . "--" . $this->LE);
}
$joint_os = new Swift_Cache_JointOutputStream();
//Try using a cached version to save some cycles (at the expense of memory)
//if ($this->cache !== null) return $this->cache . $append;
if ($this->cache->has("body"))
{
$joint_os->addStream($this->cache->getOutputStream("body"));
$joint_os->addStream($this->cache->getOutputStream("append"));
return $joint_os;
}
$is_file = ($this->getData() instanceof Swift_File);
switch ($this->getEncoding())
{
case "quoted-printable":
if ($is_file)
{
$qp_os = Swift_Message_Encoder::instance()->QPEncodeFile($this->getData(), 76, $this->LE);
while (false !== $bytes = $qp_os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->QPEncode($this->getData(), 76, 0, false, $this->LE));
}
break;
case "base64":
if ($is_file)
{
$b64_os = Swift_Message_Encoder::instance()->base64EncodeFile($this->getData(), 76, $this->LE);
while (false !== $bytes = $b64_os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->base64Encode($this->getData(), 76, 0, false, $this->LE));
}
break;
case "binary":
if ($is_file)
{
$data = $this->getData();
while (false !== $bytes = $data->read(8192))
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", $this->getData());
}
break;
case "7bit":
if ($is_file)
{
$os = Swift_Message_Encoder::instance()->encode7BitFile($this->getData(), $this->wrap, $this->LE);
while (false !== $bytes = $os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->encode7Bit($this->getData(), $this->wrap, $this->LE));
}
break;
case "8bit": default:
if ($is_file)
{
$os = Swift_Message_Encoder::instance()->encode8BitFile($this->getData(), $this->wrap, $this->LE);
while (false !== $bytes = $os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->encode8Bit($this->getData(), $this->wrap, $this->LE));
}
break;
}
$joint_os->addStream($this->cache->getOutputStream("body"));
$joint_os->addStream($this->cache->getOutputStream("append"));
return $joint_os;
}
/**
* Set the size at which lines wrap around (includes the CRLF)
* @param int The length of a line
*/
public function setLineWrap($len)
{
$this->cache->clear("body");
$this->wrap = (int) $len;
}
/**
* Nest a child mime part in this document
* @param Swift_Message_Mime
* @param string The identifier to use, optional
* @param int Add the part before (-1) or after (+1) the other parts
* @return string The identifier for this part
*/
public function addChild(Swift_Message_Mime $mime, $id=null, $after=1)
{
if (empty($id))
{
do
{
$id = uniqid();
} while (array_key_exists($id, $this->children));
}
$id = (string) $id;
if ($after == -1) $this->children = array_merge(array($id => $mime), $this->children);
else $this->children[$id] = $mime;
return $id;
}
/**
* Check if a child exists identified by $id
* @param string Identifier to look for
* @return boolean
*/
public function hasChild($id)
{
return array_key_exists($id, $this->children);
}
/**
* Get a child document, identified by $id
* @param string The identifier for this child
* @return Swift_Message_Mime The child document
* @throws Swift_Message_MimeException If no such child exists
*/
public function getChild($id)
{
if ($this->hasChild($id))
{
return $this->children[$id];
}
else
{
throw new Swift_Message_MimeException(
"Cannot retrieve child part identified by '" . $id . "' as it does not exist. Consider using hasChild() to check.");
}
}
/**
* Remove a part from the document
* @param string The identifier of the child
* @throws Swift_Message_MimeException If no such part exists
*/
public function removeChild($id)
{
$id = (string) $id;
if (!$this->hasChild($id))
{
throw new Swift_Message_MimeException(
"Cannot remove child part identified by '" . $id . "' as it does not exist. Consider using hasChild() to check.");
}
else
{
$this->children[$id] = null;
unset($this->children[$id]);
}
}
/**
* List the IDs of all children in this document
* @return array
*/
public function listChildren()
{
return array_keys($this->children);
}
/**
* Get the total number of children present in this document
* @return int
*/
public function numChildren()
{
return count($this->children);
}
/**
* Get the level at which this mime part would appear in a document
* One of "mixed", "alternative" or "related"
* @return string
*/
abstract public function getLevel();
/**
* Compile the entire MIME document into a string
* The returned string may be used in other documents if needed.
* @return Swift_Cache_OutputStream
*/
public function build()
{
$this->preBuild();
$data = $this->buildData();
$joint_os = new Swift_Cache_JointOutputStream();
$this->cache->clear("headers");
$this->cache->write("headers", $this->headers->build());
$joint_os->addStream($this->cache->getOutputStream("headers"));
$this->cache->clear("dbl_le");
$this->cache->write("dbl_le", str_repeat($this->LE, 2));
$joint_os->addStream($this->cache->getOutputStream("dbl_le"));
$joint_os->addStream($data);
return $joint_os;
//return $this->headers->build() . str_repeat($this->LE, 2) . $data;
}
/**
* Execute any logic needed prior to building
*/
abstract public function preBuild();
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Swift MIME Exception
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Exception");
/**
* Swift MIME Exception
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_MimeException extends Swift_Exception
{
}

View File

@@ -0,0 +1,134 @@
<?php
/**
* Swift Mailer Message MIME Part
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Message_Mime");
/**
* MIME Part body component for Swift Mailer
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Message_Part extends Swift_Message_Mime
{
/**
* Constructor
* @param mixed The data to use in the body
* @param string Mime type
* @param string The encoding format used
* @param string The charset used
*/
public function __construct($data=null, $type="text/plain", $encoding=null, $charset=null)
{
parent::__construct();
$this->setContentType($type);
$this->setEncoding($encoding);
$this->setCharset($charset);
$this->setFlowed(false);
if ($data !== null)
{
$this->setData($data);
if ($charset === null)
{
Swift_ClassLoader::load("Swift_Message_Encoder");
if (is_string($data) && Swift_Message_Encoder::instance()->isUTF8($data)) $this->setCharset("utf-8");
else $this->setCharset("iso-8859-1"); //The likely encoding
}
}
}
/**
* Get the level in the MIME hierarchy at which this section should appear.
* @return string
*/
public function getLevel()
{
return Swift_Message_Mime::LEVEL_ALTERNATIVE;
}
/**
* Alias for setData()
* @param mixed Body
*/
public function setBody($body)
{
$this->setData($body);
}
/**
* Alias for getData()
* @return mixed The document body
*/
public function getBody()
{
return $this->getData();
}
/**
* Set the charset of the document
* @param string The charset used
*/
public function setCharset($charset)
{
$this->headers->setAttribute("Content-Type", "charset", $charset);
if (($this->getEncoding() == "7bit") && (strtolower($charset) == "utf-8" || strtolower($charset) == "utf8")) $this->setEncoding("8bit");
}
/**
* Get the charset used in the document
* Returns null if none is set
* @return string
*/
public function getCharset()
{
if ($this->headers->hasAttribute("Content-Type", "charset"))
{
return $this->headers->getAttribute("Content-Type", "charset");
}
else
{
return null;
}
}
/**
* Set the "format" attribute to flowed
* @param boolean On or Off
*/
public function setFlowed($flowed=true)
{
$value = null;
if ($flowed) $value = "flowed";
$this->headers->setAttribute("Content-Type", "format", $value);
}
/**
* Pre-compilation logic
*/
public function preBuild()
{
if (!($enc = $this->getEncoding())) $this->setEncoding("8bit");
$data = $this->getData();
if ($this->getCharset() === null && !$this->numChildren())
{
if (is_string($data) && Swift_Message_Encoder::instance()->isUTF8($data))
{
$this->setCharset("utf-8");
}
elseif (is_string($data) && Swift_Message_Encoder::instance()->is7BitAscii($data))
{
$this->setCharset("us-ascii");
if (!$enc) $this->setEncoding("7bit");
}
else $this->setCharset("iso-8859-1");
}
elseif ($this->numChildren())
{
$this->setCharset(null);
$this->setEncoding("7bit");
}
}
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* Swift Mailer AntiFlood Plugin
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_SendListener");
/**
* Swift AntiFlood controller.
* Closes a connection and pauses for X seconds after a number of emails have been sent.
* @package Swift_Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_AntiFlood implements Swift_Events_SendListener
{
/**
* The number of emails to send between connections
* @var int
*/
protected $threshold = null;
/**
* The number of seconds to pause for between connections
* @var int
*/
protected $waitFor = null;
/**
* Number of emails sent so far
* @var int
*/
protected $count = 0;
/**
* Constructor
* @param int Number of emails to send before re-connecting
* @param int The timeout in seconds between connections
*/
public function __construct($threshold, $wait=0)
{
$this->setThreshold($threshold);
$this->setWait($wait);
}
/**
* Set the number of emails which must be sent for a reconnection to occur
* @param int Number of emails
*/
public function setThreshold($threshold)
{
$this->threshold = (int) $threshold;
}
/**
* Get the number of emails which need to be sent for reconnection to occur
* @return int
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* Set the number of seconds the plugin should wait for before reconnecting
* @param int Time in seconds
*/
public function setWait($time)
{
$this->waitFor = (int) $time;
}
/**
* Get the number of seconds the plugin should wait for before re-connecting
* @return int
*/
public function getWait()
{
return $this->waitFor;
}
/**
* Sleep for a given number of seconds
* @param int Number of seconds to wait for
*/
public function wait($seconds)
{
if ($seconds) sleep($seconds);
}
/**
* Swift's SendEvent listener.
* Invoked when Swift sends a message
* @param Swift_Events_SendEvent The event information
* @throws Swift_ConnectionException If the connection cannot be closed/re-opened
*/
public function sendPerformed(Swift_Events_SendEvent $e)
{
$this->count++;
if ($this->count >= $this->getThreshold())
{
$e->getSwift()->disconnect();
$this->wait($this->getWait());
$e->getSwift()->connect();
$this->count = 0;
}
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Swift Mailer Bandwidth Monitoring Plugin
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_CommandListener");
Swift_ClassLoader::load("Swift_Events_ResponseListener");
/**
* Swift Bandwidth Monitor.
* Tracks bytes in and out of the connection.
* @package Swift_Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_BandwidthMonitor implements Swift_Events_CommandListener, Swift_Events_ResponseListener
{
/**
* The number of bytes received
* @var int
*/
protected $in = 0;
/**
* The number of bytes sent
* @var int
*/
protected $out = 0;
/**
* Part of the interface which is notified after a command is sent.
* @param Swift_Events_CommandEvent
*/
public function commandSent(Swift_Events_CommandEvent $e)
{
$code = $e->getCode();
$add = 0;
if ($code != -1) $add = 2;
$bytes = strlen($e->getString()) + $add;
$this->addBytesOut($bytes);
}
/**
* Part of the interface which is notified when a response is received
* @param Swift_Events_ResponseEvent
*/
public function responseReceived(Swift_Events_ResponseEvent $e)
{
$bytes = strlen($e->getString()) + 2;
$this->addBytesIn($bytes);
}
/**
* Add some bytes to the running totals for incoming bandwidth
* @param int Bytes in
*/
public function addBytesIn($num)
{
$num = abs((int)$num);
$this->setBytesIn($this->getBytesIn() + $num);
}
/**
* Add some bytes to the running totals for outgoing bandwidth
* @param int Bytes out
*/
public function addBytesOut($num)
{
$num = abs((int)$num);
$this->setBytesOut($this->getBytesOut() + $num);
}
/**
* Get the total number of bytes received
* @return int
*/
public function getBytesIn()
{
return $this->in;
}
/**
* Get the total number of bytes sent
* @return int
*/
public function getBytesOut()
{
return $this->out;
}
/**
* Set the total number of bytes received.
* Can be used to reset the counters at runtime.
* @param int The bytes in
*/
public function setBytesIn($num)
{
$this->in = abs((int)$num);
}
/**
* Set the total number of bytes sent.
* Can be used to reset the counters at runtime.
* @param int The bytes out
*/
public function setBytesOut($num)
{
$this->out = abs((int)$num);
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* Swift Mailer Rotating Connection Controller
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_SendListener");
Swift_ClassLoader::load("Swift_Events_DisconnectListener");
/**
* Swift Rotating Connection Controller
* Invokes the nextConnection() method of Swift_Connection_Rotator upon sending a given number of messages
* @package Swift_Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_ConnectionRotator implements Swift_Events_SendListener, Swift_Events_DisconnectListener
{
/**
* The number of emails which must be sent before the connection is rotated
* @var int Threshold number of emails
*/
protected $threshold = 1;
/**
* The total number of emails sent on this connection
* @var int
*/
protected $count = 0;
/**
* The connections we have used thus far
* @var array
*/
protected $used = array();
/**
* Internal check to see if this plugin has yet been invoked
* @var boolean
*/
protected $called = false;
/**
* Constructor
* @param int The number of emails to send before rotating
*/
public function __construct($threshold=1)
{
$this->setThreshold($threshold);
}
/**
* Set the number of emails to send before a connection rotation is tried
* @param int Number of emails
*/
public function setThreshold($threshold)
{
$this->threshold = (int) $threshold;
}
/**
* Get the number of emails which must be sent before a rotation occurs
* @return int
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* Swift's SendEvent listener.
* Invoked when Swift sends a message
* @param Swift_Events_SendEvent The event information
* @throws Swift_ConnectionException If the connection cannot be rotated
*/
public function sendPerformed(Swift_Events_SendEvent $e)
{
if (!method_exists($e->getSwift()->connection, "nextConnection"))
{
throw new Swift_ConnectionException("The ConnectionRotator plugin cannot be used with connections other than Swift_Connection_Rotator.");
}
if (!$this->called)
{
$this->used[] = $e->getSwift()->connection->getActive();
}
$this->count++;
if ($this->count >= $this->getThreshold())
{
$e->getSwift()->connection->nextConnection();
if (!in_array(($id = $e->getSwift()->connection->getActive()), $this->used))
{
$e->getSwift()->connect();
$this->used[] = $id;
}
$this->count = 0;
}
$this->called = true;
}
/**
* Disconnect all the other connections
* @param Swift_Events_DisconnectEvent The event info
*/
public function disconnectPerformed(Swift_Events_DisconnectEvent $e)
{
$active = $e->getConnection()->getActive();
$e->getConnection()->nextConnection();
while ($e->getConnection()->getActive() != $active)
{
$e->getSwift()->command("QUIT", 221);
$e->getConnection()->stop();
$e->getConnection()->nextConnection();
}
$this->used = array();
}
}

View File

@@ -0,0 +1,259 @@
<?php
/**
* Swift Mailer Message Decorating Plugin.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @subpackage Decorator
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_BeforeSendListener");
Swift_ClassLoader::load("Swift_Plugin_Decorator_Replacements");
/**
* Swift Decorator Plugin.
* Allows messages to be slightly different for each recipient.
* @package Swift_Plugin
* @subpackage Decorator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_Decorator implements Swift_Events_BeforeSendListener
{
/**
* The replacements object.
* @var Swift_Plugin_Decorator_Replacements
*/
protected $replacements;
/**
* Temporary storage so we can restore changes we make.
* @var array
*/
protected $store;
/**
* A list of allowed mime types to replace bodies for.
* @var array
*/
protected $permittedTypes = array("text/plain" => 1, "text/html" => 1);
/**
* True if values in the headers can be replaced
* @var boolean
*/
protected $permittedInHeaders = true;
/**
* Ctor.
* @param mixed Replacements as a 2-d array or Swift_Plugin_Decorator_Replacements instance.
*/
public function __construct($replacements=null)
{
$this->setReplacements($replacements);
}
/**
* Enable of disable the ability to replace values in the headers
* @param boolean
*/
public function setPermittedInHeaders($bool)
{
$this->permittedInHeaders = (bool) $bool;
}
/**
* Check if replacements in headers are allowed.
* @return boolean
*/
public function getPermittedInHeaders()
{
return $this->permittedInHeaders;
}
/**
* Add a mime type to the list of permitted type to replace values in the body.
* @param string The mime type (e.g. text/plain)
*/
public function addPermittedType($type)
{
$type = strtolower($type);
$this->permittedTypes[$type] = 1;
}
/**
* Remove the ability to replace values in the body of the given mime type
* @param string The mime type
*/
public function removePermittedType($type)
{
unset($this->permittedTypes[$type]);
}
/**
* Get the list of mime types for which the body can be changed.
* @return array
*/
public function getPermittedTypes()
{
return array_keys($this->permittedTypes);
}
/**
* Check if the body can be replaced in the given mime type.
* @param string The mime type
* @return boolean
*/
public function isPermittedType($type)
{
return array_key_exists(strtolower($type), $this->permittedTypes);
}
/**
* Called just before Swift sends a message.
* We perform operations on the message here.
* @param Swift_Events_SendEvent The event object for sending a message
*/
public function beforeSendPerformed(Swift_Events_SendEvent $e)
{
$message = $e->getMessage();
$this->recursiveRestore($message, $this->store); //3.3.3 bugfix
$recipients = $e->getRecipients();
$to = array_keys($recipients->getTo());
if (count($to) > 0) $to = $to[0];
else return;
$replacements = (array)$this->replacements->getReplacementsFor($to);
$this->store = array(
"headers" => array(),
"body" => false,
"children" => array()
);
$this->recursiveReplace($message, $replacements, $this->store);
}
/**
* Replace strings in the message searching through all the allowed sub-parts.
* @param Swift_Message_Mime The message (or part)
* @param array The list of replacements
* @param array The array to cache original values into where needed
*/
protected function recursiveReplace(Swift_Message_Mime $mime, $replacements, &$store)
{
//Check headers
if ($this->getPermittedInHeaders())
{
foreach ($mime->headers->getList() as $name => $value)
{
if (is_string($value) && ($replaced = $this->replace($replacements, $value)) != $value)
{
$mime->headers->set($name, $replaced);
$store["headers"][$name] = array();
$store["headers"][$name]["value"] = $value;
$store["headers"][$name]["attributes"] = array();
}
foreach ($mime->headers->listAttributes($name) as $att_name => $att_value)
{
if (is_string($att_value)
&& ($att_replaced = $this->replace($replacements, $att_value)) != $att_value)
{
if (!isset($store["headers"][$name]))
{
$store["headers"][$name] = array("value" => false, "attributes" => array());
}
$mime->headers->setAttribute($name, $att_name, $att_replaced);
$store["headers"][$name]["attributes"][$att_name] = $att_value;
}
}
}
}
//Check body
$body = $mime->getData();
if ($this->isPermittedType($mime->getContentType())
&& is_string($body) && ($replaced = $this->replace($replacements, $body)) != $body)
{
$mime->setData($replaced);
$store["body"] = $body;
}
//Check sub-parts
foreach ($mime->listChildren() as $id)
{
$store["children"][$id] = array(
"headers" => array(),
"body" => false,
"children" => array()
);
$child = $mime->getChild($id);
$this->recursiveReplace($child, $replacements, $store["children"][$id]);
}
}
/**
* Perform a str_replace() over the given value.
* @param array The list of replacements as (search => replacement)
* @param string The string to replace
* @return string
*/
protected function replace($replacements, $value)
{
return str_replace(array_keys($replacements), array_values($replacements), $value);
}
/**
* Put the original values back in the message after it was modified before sending.
* @param Swift_Message_Mime The message (or part)
* @param array The location of the stored values
*/
protected function recursiveRestore(Swift_Message_Mime $mime, &$store)
{
if (empty($store)) //3.3.3 bugfix
{
return;
}
//Restore headers
foreach ($store["headers"] as $name => $array)
{
if ($array["value"] !== false) $mime->headers->set($name, $array["value"]);
foreach ($array["attributes"] as $att_name => $att_value)
{
$mime->headers->setAttribute($name, $att_name, $att_value);
}
}
//Restore body
if ($store["body"] !== false)
{
$mime->setData($store["body"]);
}
//Restore children
foreach ($store["children"] as $id => $child_store)
{
$child = $mime->getChild($id);
$this->recursiveRestore($child, $child_store);
}
}
/**
* Set the replacements as a 2-d array or an instance of Swift_Plugin_Decorator_Replacements.
* @param mixed Array or Swift_Plugin_Decorator_Replacements
*/
public function setReplacements($replacements)
{
if ($replacements === null)
{
$r = array();
$this->replacements = new Swift_Plugin_Decorator_Replacements($r);
}
elseif (is_array($replacements))
{
$this->replacements = new Swift_Plugin_Decorator_Replacements($replacements);
}
elseif ($replacements instanceof Swift_Plugin_Decorator_Replacements)
{
$this->replacements = $replacements;
}
else
{
throw new Exception(
"Decorator replacements must be array or instance of Swift_Plugin_Decorator_Replacements.");
}
}
/**
* Get the replacements object.
* @return Swift_Plugin_Decorator_Replacements
*/
public function getReplacements()
{
return $this->replacements;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* Swift Mailer Decorator Replacements Container
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @subpackage Decorator
* @license GNU Lesser General Public License
*/
/**
* Swift Decorator Plugin Replacements.
* Provides and manages the list of replacements for the decorator plugin.
* @package Swift_Plugin
* @subpackage Decorator
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_Decorator_Replacements
{
/**
* The list of replacements as a 2-d array
* @var array,array
*/
protected $replacements;
/**
* Ctor.
* @param array The replacements as a 2-d array, optional
*/
public function __construct($replacements = array())
{
$this->setReplacements($replacements);
}
/**
* Add a list of replacements for a given address.
* @param string The e-mail address
* @param array The replacements as (search => replacement) form.
*/
public function addReplacements($address, $replacements)
{
$this->replacements[strtolower($address)] = (array)$replacements;
}
/**
* Set the complete list of replacements as a 2-d array.
* The array is formed thus (address => (search => replace), address => (search => replace))
* @param array,array The replacements.
*/
public function setReplacements($replacements)
{
$this->replacements = array_change_key_case((array) $replacements, CASE_LOWER);
}
/**
* Get the entire list of replacements as a 2-d array
* @return array,array
*/
public function getReplacements()
{
return $this->replacements;
}
/**
* Get the list of replacements for the address given.
* Returns an array where (search => replacement).
* @param string The address to get replacements for
* @return array
*/
public function getReplacementsFor($address)
{
$address = strtolower($address);
if (array_key_exists($address, $this->replacements))
{
return (array)$this->replacements[$address];
}
else return array();
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* EasySwift Response Tracker
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package EasySwift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_ResponseListener");
/**
* EasySwift, Swift Response Tracker.
* Updates properties in EasySwift when a response is received by Swift.
* @package EasySwift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_EasySwiftResponseTracker implements Swift_Events_ResponseListener
{
/**
* The target object to update
* @var EasySwift
*/
protected $target = null;
/**
* Constructor
* @param EasySwift The instance of EasySwift to run against
*/
public function __construct($obj)
{
$this->target = $obj;
}
/**
* Response listener method
* @param Swift_Events_ResponseEvent The event occured in Swift
*/
public function responseReceived(Swift_Events_ResponseEvent $e)
{
$this->target->lastResponse = $e->getString();
$this->target->responseCode = $e->getCode();
}
}

View File

@@ -0,0 +1,431 @@
<?php
/**
* A Swift Mailer plugin to download remote images and stylesheets then embed them.
* This also embeds local files from disk.
* Please read the LICENSE file
* @package Swift_Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_BeforeSendListener");
/**
* Swift FileEmbedder Plugin to embed remote files.
* Scans a Swift_Message instance for remote files and then embeds them before sending.
* This also embeds local files from disk.
* @package Swift_Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_FileEmbedder implements Swift_Events_BeforeSendListener
{
/**
* True if remote files will be embedded.
* @var boolean
*/
protected $embedRemoteFiles = true;
/**
* True if local files will be embedded.
* @var boolean
*/
protected $embedLocalFiles = true;
/**
* (X)HTML tag defintions listing allowed attributes and extensions.
* @var array
*/
protected $definitions = array(
"img" => array(
"attributes" => array("src"),
"extensions" => array("gif", "png", "jpg", "jpeg", "pjpeg")
),
"link" => array(
"attributes" => array("href"),
"extensions" => array("css")
),
"script" => array(
"attributes" => array("src"),
"extensions" => array("js")
));
/**
* Protocols which may be used to download a remote file.
* @var array
*/
protected $protocols = array(
"http" => "http",
"https" => "https",
"ftp" => "ftp"
);
/**
* A PCRE regexp which will be passed via sprintf() to produce a complete pattern.
* @var string
*/
protected $remoteFilePatternFormat = "~
(<(?:%s)\\s+[^>]*? #Opening tag followed by (possible) attributes
(?:%s)=((?:\"|')?)) #Permitted attributes followed by (possible) quotation marks
((?:%s)://[\\x01-\\x7F]*?(?:%s)?) #Remote URL (matching a permitted protocol)
(\\2[^>]*>) #Remaining attributes followed by end of tag
~isx";
/**
* A PCRE regexp which will be passed via sprintf() to produce a complete pattern.
* @var string
*/
protected $localFilePatternFormat = "~
(<(?:%s)\\s+[^>]*? #Opening tag followed by (possible) attributes
(?:%s)=((?:\"|')?)) #Permitted attributes followed by (possible) quotation marks
((?:/|[a-z]:\\\\|[a-z]:/)[\\x01-\\x7F]*?(?:%s)?) #Local, absolute path
(\\2[^>]*>) #Remaining attributes followed by end of tag
~isx";
/**
* A list of extensions mapping to their usual MIME types.
* @var array
*/
protected $mimeTypes = array(
"gif" => "image/gif",
"png" => "image/png",
"jpeg" => "image/jpeg",
"jpg" => "image/jpeg",
"pjpeg" => "image/pjpeg",
"js" => "text/javascript",
"css" => "text/css");
/**
* Child IDs of files already embedded.
* @var array
*/
protected $registeredFiles = array();
/**
* Get the MIME type based upon the extension.
* @param string The extension (sans the dot).
* @return string
*/
public function getType($ext)
{
$ext = strtolower($ext);
if (isset($this->mimeTypes[$ext]))
{
return $this->mimeTypes[$ext];
}
else return null;
}
/**
* Add a new MIME type defintion (or overwrite an existing one).
* @param string The extension (sans the dot)
* @param string The MIME type (e.g. image/jpeg)
*/
public function addType($ext, $type)
{
$this->mimeTypes[strtolower($ext)] = strtolower($type);
}
/**
* Set the PCRE pattern which finds -full- HTML tags and copies the path for a local file into a backreference.
* The pattern contains three %s replacements for sprintf().
* First replacement is the tag name (e.g. img)
* Second replacement is the attribute name (e.g. src)
* Third replacement is the file extension (e.g. jpg)
* This pattern should contain the full URL in backreference index 3.
* @param string sprintf() format string containing a PCRE regexp.
*/
public function setLocalFilePatternFormat($format)
{
$this->localFilePatternFormat = $format;
}
/**
* Gets the sprintf() format string for the PCRE pattern to scan for remote files.
* @return string
*/
public function getLocalFilePatternFormat()
{
return $this->localFilePatternFormat;
}
/**
* Set the PCRE pattern which finds -full- HTML tags and copies the URL for the remote file into a backreference.
* The pattern contains four %s replacements for sprintf().
* First replacement is the tag name (e.g. img)
* Second replacement is the attribute name (e.g. src)
* Third replacement is the protocol (e.g. http)
* Fourth replacement is the file extension (e.g. jpg)
* This pattern should contain the full URL in backreference index 3.
* @param string sprintf() format string containing a PCRE regexp.
*/
public function setRemoteFilePatternFormat($format)
{
$this->remoteFilePatternFormat = $format;
}
/**
* Gets the sprintf() format string for the PCRE pattern to scan for remote files.
* @return string
*/
public function getRemoteFilePatternFormat()
{
return $this->remoteFilePatternFormat;
}
/**
* Add a new protocol which can be used to download files.
* Protocols should not include the "://" portion. This method expects alphanumeric characters only.
* @param string The protocol name (e.g. http or ftp)
*/
public function addProtocol($prot)
{
$prot = strtolower($prot);
$this->protocols[$prot] = $prot;
}
/**
* Remove a protocol from the list of allowed protocols once added.
* @param string The name of the protocol (e.g. http)
*/
public function removeProtocol($prot)
{
unset($this->protocols[strtolower($prot)]);
}
/**
* Get a list of all registered protocols.
* @return array
*/
public function getProtocols()
{
return array_values($this->protocols);
}
/**
* Add, or modify a tag definition.
* This affects how the plugins scans for files to download.
* @param string The name of a tag to search for (e.g. img)
* @param string The name of attributes to look for (e.g. src). You can pass an array if there are multiple possibilities.
* @param array A list of extensions to allow (sans dot). If there's only one you can just pass a string.
*/
public function setTagDefinition($tag, $attributes, $extensions)
{
$tag = strtolower($tag);
$attributes = (array)$attributes;
$extensions = (array)$extensions;
if (empty($tag) || empty($attributes) || empty($extensions))
{
return null;
}
$this->definitions[$tag] = array("attributes" => $attributes, "extensions" => $extensions);
return true;
}
/**
* Remove a tag definition for remote files.
* @param string The name of the tag
*/
public function removeTagDefinition($tag)
{
unset($this->definitions[strtolower($tag)]);
}
/**
* Get a tag definition.
* Returns an array with indexes "attributes" and "extensions".
* Each element is an array listing the values within it.
* @param string The name of the tag
* @return array
*/
public function getTagDefinition($tag)
{
$tag = strtolower($tag);
if (isset($this->definitions[$tag])) return $this->definitions[$tag];
else return null;
}
/**
* Get the PCRE pattern for a remote file based on the tag name.
* @param string The name of the tag
* @return string
*/
public function getRemoteFilePattern($tag_name)
{
$tag_name = strtolower($tag_name);
$pattern_format = $this->getRemoteFilePatternFormat();
if ($def = $this->getTagDefinition($tag_name))
{
$pattern = sprintf($pattern_format, $tag_name, implode("|", $def["attributes"]),
implode("|", $this->getProtocols()), implode("|", $def["extensions"]));
return $pattern;
}
else return null;
}
/**
* Get the PCRE pattern for a local file based on the tag name.
* @param string The name of the tag
* @return string
*/
public function getLocalFilePattern($tag_name)
{
$tag_name = strtolower($tag_name);
$pattern_format = $this->getLocalFilePatternFormat();
if ($def = $this->getTagDefinition($tag_name))
{
$pattern = sprintf($pattern_format, $tag_name, implode("|", $def["attributes"]),
implode("|", $def["extensions"]));
return $pattern;
}
else return null;
}
/**
* Register a file which has been downloaded so it doesn't need to be downloaded twice.
* @param string The remote URL
* @param string The ID as attached in the message
* @param Swift_Message_EmbeddedFile The file object itself
*/
public function registerFile($url, $cid, $file)
{
$url = strtolower($url);
if (!isset($this->registeredFiles[$url])) $this->registeredFiles[$url] = array("cids" => array(), "obj" => null);
$this->registeredFiles[$url]["cids"][] = $cid;
if (empty($this->registeredFiles[$url]["obj"])) $this->registeredFiles[$url]["obj"] = $file;
}
/**
* Turn on or off remote file embedding.
* @param boolean
*/
public function setEmbedRemoteFiles($set)
{
$this->embedRemoteFiles = (bool)$set;
}
/**
* Returns true if remote files can be embedded, or false if not.
* @return boolean
*/
public function getEmbedRemoteFiles()
{
return $this->embedRemoteFiles;
}
/**
* Turn on or off local file embedding.
* @param boolean
*/
public function setEmbedLocalFiles($set)
{
$this->embedLocalFiles = (bool)$set;
}
/**
* Returns true if local files can be embedded, or false if not.
* @return boolean
*/
public function getEmbedLocalFiles()
{
return $this->embedLocalFiles;
}
/**
* Callback method for preg_replace().
* Embeds files which have been found during scanning.
* @param array Backreferences from preg_replace()
* @return string The tag with it's URL replaced with a CID
*/
protected function embedRemoteFile($matches)
{
$url = preg_replace("~^([^#]+)#.*\$~s", "\$1", $matches[3]);
$bits = parse_url($url);
$ext = preg_replace("~^.*?\\.([^\\.]+)\$~s", "\$1", $bits["path"]);
$lower_url = strtolower($url);
if (array_key_exists($lower_url, $this->registeredFiles))
{
$registered = $this->registeredFiles[$lower_url];
foreach ($registered["cids"] as $cid)
{
if ($this->message->hasChild($cid))
{
return $matches[1] . $cid . $matches[4];
}
}
//If we get here the file is downloaded, but not embedded
$cid = $this->message->attach($registered["obj"]);
$this->registerFile($url, $cid, $registered["obj"]);
return $matches[1] . $cid . $matches[4];
}
$magic_quotes = get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
$filedata = @file_get_contents($url);
set_magic_quotes_runtime($magic_quotes);
if (!$filedata)
{
return $matches[1] . $matches[3] . $matches[4];
}
$filename = preg_replace("~^.*/([^/]+)\$~s", "\$1", $url);
$att = new Swift_Message_EmbeddedFile($filedata, $filename, $this->getType($ext));
$id = $this->message->attach($att);
$this->registerFile($url, $id, $att);
return $matches[1] . $id . $matches[4];
}
/**
* Callback method for preg_replace().
* Embeds files which have been found during scanning.
* @param array Backreferences from preg_replace()
* @return string The tag with it's path replaced with a CID
*/
protected function embedLocalFile($matches)
{
$path = realpath($matches[3]);
if (!$path)
{
return $matches[1] . $matches[3] . $matches[4];
}
$ext = preg_replace("~^.*?\\.([^\\.]+)\$~s", "\$1", $path);
$lower_path = strtolower($path);
if (array_key_exists($lower_path, $this->registeredFiles))
{
$registered = $this->registeredFiles[$lower_path];
foreach ($registered["cids"] as $cid)
{
if ($this->message->hasChild($cid))
{
return $matches[1] . $cid . $matches[4];
}
}
//If we get here the file is downloaded, but not embedded
$cid = $this->message->attach($registered["obj"]);
$this->registerFile($path, $cid, $registered["obj"]);
return $matches[1] . $cid . $matches[4];
}
$filename = basename($path);
$att = new Swift_Message_EmbeddedFile(new Swift_File($path), $filename, $this->getType($ext));
$id = $this->message->attach($att);
$this->registerFile($path, $id, $att);
return $matches[1] . $id . $matches[4];
}
/**
* Empty out the cache of registered files.
*/
public function clearCache()
{
$this->registeredFiles = null;
$this->registeredFiles = array();
}
/**
* Swift's BeforeSendListener required method.
* Runs just before Swift sends a message. Here is where we do all the replacements.
* @param Swift_Events_SendEvent
*/
public function beforeSendPerformed(Swift_Events_SendEvent $e)
{
$this->message = $e->getMessage();
foreach ($this->message->listChildren() as $id)
{
$part = $this->message->getChild($id);
$body = $part->getData();
if (!is_string($body) || substr(strtolower($part->getContentType()), 0, 5) != "text/") continue;
foreach ($this->definitions as $tag_name => $def)
{
if ($this->getEmbedRemoteFiles())
{
$re = $this->getRemoteFilePattern($tag_name);
$body = preg_replace_callback($re, array($this, "embedRemoteFile"), $body);
}
if ($this->getEmbedLocalFiles())
{
$re = $this->getLocalFilePattern($tag_name);
$body = preg_replace_callback($re, array($this, "embedLocalFile"), $body);
}
}
$part->setData($body);
}
}
}

View File

@@ -0,0 +1,170 @@
<?php
/**
* Swift Mailer mail() sending plugin
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Connection
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_SendListener");
Swift_ClassLoader::load("Swift_Events_BeforeSendListener");
/**
* Swift mail() send plugin
* Sends the message using mail() when a SendEvent is fired. Using the NativeMail connection provides stub responses to allow this to happen cleanly.
* @package Swift_Connection
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_MailSend implements Swift_Events_SendListener, Swift_Events_BeforeSendListener
{
/**
* The operating system of the server
* @var string
*/
protected $OS = null;
/**
* The return path in use here
* @var string
*/
protected $returnPath = null;
/**
* The line ending before we intrusively change it
* @var string
*/
protected $oldLE = "\r\n";
/**
* 5th parameter in mail().
* @var string
*/
protected $additionalParams;
/**
* Constructor.
* @param string 5th mail() function parameter as a sprintf() formatted string where %s is the sender.
*/
public function __construct($params="-oi -f %s")
{
$this->setAdditionalParams($params);
$this->setOS(PHP_OS);
}
/**
* Set the 5th mail() function parameter as a sprintf() formatted string where %s is the sender.
* @param string
*/
public function setAdditionalParams($params)
{
$this->additionalParams = $params;
}
/**
* Get the 5th mail() function parameter as a sprintf() string.
* @return string
*/
public function getAdditionalParams()
{
return $this->additionalParams;
}
/**
* Set the operating system string (changes behaviour with LE)
* @param string The operating system
*/
public function setOS($os)
{
$this->OS = $os;
}
/**
* Get the operating system string
* @return string
*/
public function getOS()
{
return $this->OS;
}
/**
* Check if this is windows or not
* @return boolean
*/
public function isWindows()
{
return (substr($this->getOS(), 0, 3) == "WIN");
}
/**
* Swift's BeforeSendEvent listener.
* Invoked just before Swift sends a message
* @param Swift_Events_SendEvent The event information
*/
public function beforeSendPerformed(Swift_Events_SendEvent $e)
{
$message = $e->getMessage();
$message->uncacheAll();
$this->oldLE = $message->getLE();
if (!$this->isWindows() && $this->oldLE != "\n") $message->setLE("\n");
}
/**
* Swift's SendEvent listener.
* Invoked when Swift sends a message
* @param Swift_Events_SendEvent The event information
* @throws Swift_ConnectionException If mail() returns false
*/
public function sendPerformed(Swift_Events_SendEvent $e)
{
$message = $e->getMessage();
$recipients = $e->getRecipients();
$to = array();
foreach ($recipients->getTo() as $addr)
{
if ($this->isWindows()) $to[] = substr($addr->build(true), 1, -1);
else $to[] = $addr->build();
}
$to = implode(", ", $to);
$bcc_orig = $message->headers->has("Bcc") ? $message->headers->get("Bcc") : null;
$subject_orig = $message->headers->has("Subject") ? $message->headers->get("Subject") : null;
$to_orig = $message->headers->has("To") ? $message->headers->get("To") : null;
$bcc = array();
foreach ($recipients->getBcc() as $addr) $bcc[] = $addr->build();
if (!empty($bcc)) $message->headers->set("Bcc", $bcc);
$bcc = null;
$body_data = $message->buildData();
$message_body = $body_data->readFull();
$subject_enc = $message->headers->has("Subject") ? $message->headers->getEncoded("Subject") : "";
$message->headers->set("To", null);
$message->headers->set("Subject", null);
$sender = $e->getSender();
$this->returnPath = $sender->build();
if ($message->headers->has("Return-Path")) $this->returnPath = $message->headers->get("Return-Path");
if (preg_match("~<([^>]+)>[^>]*\$~", $this->returnPath, $matches)) $this->returnPath = $matches[1];
$this->doMail($to, $subject_enc, $message_body, $message->headers, sprintf($this->getAdditionalParams(), $this->returnPath));
$message->setLE($this->oldLE);
$message->headers->set("To", $to_orig);
$message->headers->set("Subject", $subject_orig);
$message->headers->set("Bcc", $bcc_orig);
}
public function doMail($to, $subject, $message, $headers, $params)
{
$original_from = @ini_get("sendmail_from");
@ini_set("sendmail_from", $this->returnPath);
$headers = $headers->build();
if (!ini_get("safe_mode")) $success = mail($to, $subject, $message, $headers, $params);
else $success = mail($to, $subject, $message, $headers);
if (!$success)
{
@ini_set("sendmail_from", $original_from);
throw new Swift_ConnectionException("Sending failed using mail() as PHP's default mail() function returned boolean FALSE.");
}
@ini_set("sendmail_from", $original_from);
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* Swift Mailer Throttling Plugin.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Plugin_BandwidthMonitor");
Swift_ClassLoader::load("Swift_Events_SendListener");
/**
* Throttler plugin for Swift Mailer.
* Restricts the speed at which Swift will operate.
* @package Swift_Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_Throttler extends Swift_Plugin_BandwidthMonitor implements Swift_Events_SendListener
{
/**
* The rate in byte-per-minute
* @var int
*/
protected $bpm = null;
/**
* The rate as emails-per-minute
* @var int
*/
protected $epm = null;
/**
* The number of emails sent so far
* @var int
*/
protected $sent = 0;
/**
* The time at the start of overall execution
* @var int
*/
protected $time = null;
/**
* Part of the interface which is notified after a command is sent.
* @param Swift_Events_CommandEvent
*/
public function commandSent(Swift_Events_CommandEvent $e)
{
parent::commandSent($e);
if (null === $rate = $this->getBytesPerMinute()) return;
$duration = $this->getTimeLapse();
$bytes_sent = $this->getBytesOut();
$bytes_per_sec = $rate / 60;
$seconds_allowed_so_far = ceil($bytes_sent / $bytes_per_sec);
$overrun = $seconds_allowed_so_far - $duration;
if ($overrun > 0)
{
$this->wait($overrun);
}
}
/**
* Part of the interface which is notified when a message has been sent.
* @param Swift_Events_SendEvent
*/
public function sendPerformed(Swift_Events_SendEvent $e)
{
$this->setSent($this->getSent() + 1);
if (null === $rate = $this->getEmailsPerMinute()) return;
$duration = $this->getTimeLapse();
$emails_sent = $this->getSent();
$emails_per_sec = $rate / 60;
$seconds_allowed_so_far = ceil($emails_sent / $emails_per_sec);
$overrun = $seconds_allowed_so_far - $duration;
if ($overrun > 0)
{
$this->wait($overrun);
}
}
/**
* Wait for $seconds before continuing
* @param int The number of seconds to wait
*/
public function wait($secs)
{
sleep($secs);
}
/**
* Set the time if it's not already set
*/
protected function setTime()
{
if ($this->time === null) $this->time = time();
}
/**
* Get the time taken thus far (full seconds).
* @return int
*/
public function getTimeLapse()
{
$this->setTime();
return time() - $this->time;
}
/**
* Set the number of emails sent
* @param int Emails sent so far
*/
public function setSent($num)
{
$this->sent = (int)$num;
}
/**
* Get the number of emails sent
* @return int
*/
public function getSent()
{
return $this->sent;
}
/**
* Set the throttling rate as bytes per minute
* @param int The maximum number of outgoing bytes in 60 seconds.
*/
public function setBytesPerMinute($bpm)
{
if ($bpm === null)
{
$this->bpm = null;
return;
}
$this->setEmailsPerMinute(null);
$this->bpm = abs((int)$bpm);
}
/**
* Get the number of bytes allowed per minute.
* Reurns NULL if not used.
* @return int
*/
public function getBytesPerMinute()
{
return $this->bpm;
}
/**
* Set the rate as emails-per-minute.
* @param int The max number of emails to send in a minute.
*/
public function setEmailsPerMinute($epm)
{
if ($epm === null)
{
$this->epm = null;
return;
}
$this->setBytesPerMinute(null);
$this->epm = abs((int)$epm);
}
/**
* Get the rate as number of emails per minute.
* Returns null if not used.
* @return int
*/
public function getEmailsPerMinute()
{
return $this->epm;
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* Swift Mailer Verbose Sending Plugin.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @subpackage VerboseSending
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_Events_SendListener");
Swift_ClassLoader::load("Swift_Plugin_VerboseSending_DefaultView");
/**
* Verbose Sending plugin for Swift Mailer.
* Displays "pass" or "fail" messages in realtime as the messages are sent.
* @package Swift_Plugin
* @subpackage VerboseSending
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_VerboseSending implements Swift_Events_SendListener
{
/**
* The view layer which displays the results.
* @var Swift_Plugin_VerboseSending_AbstractView
*/
protected $view;
/**
* Ctor.
* @param Swift_Plugin_VerboseSending_AbstractView The view object to display the result
*/
public function __construct(Swift_Plugin_VerboseSending_AbstractView $view)
{
$this->setView($view);
}
/**
* Part of the interface which is notified when a message has been sent.
* @param Swift_Events_SendEvent
*/
public function sendPerformed(Swift_Events_SendEvent $e)
{
$recipients = $e->getRecipients();
$failed = $e->getFailedRecipients();
$it = $recipients->getIterator("to");
while ($it->hasNext())
{
$it->next();
$address = $it->getValue();
$pass = !in_array($address->getAddress(), $failed);
$this->getView()->paintResult($address->getAddress(), $pass);
}
$it = $recipients->getIterator("cc");
while ($it->hasNext())
{
$it->next();
$address = $it->getValue();
$pass = !in_array($address->getAddress(), $failed);
$this->getView()->paintResult($address->getAddress(), $pass);
}
$it = $recipients->getIterator("bcc");
while ($it->hasNext())
{
$it->next();
$address = $it->getValue();
$pass = !in_array($address->getAddress(), $failed);
$this->getView()->paintResult($address->getAddress(), $pass);
}
}
/**
* Set the View component to display results.
* @param Swift_Plugin_VerboseSending_AbstractView The view object to display the result
*/
public function setView(Swift_Plugin_VerboseSending_AbstractView $view)
{
$this->view = $view;
}
/**
* Get the View component.
* @return Swift_Plugin_VerboseSending_AbstractView
*/
public function getView()
{
return $this->view;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Swift Mailer Verbose-sending Plugin View Layer.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @subpackage VerboseSending
* @license GNU Lesser General Public License
*/
/**
* The View layer for the Verbose Sending Plugin
* @package Swift_Plugin
* @subpackage VerboseSending
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_Plugin_VerboseSending_AbstractView
{
/**
* Paint the result of a send operation
* @param string The email address that was tried
* @param boolean True if the message was successfully sent
*/
abstract public function paintResult($address, $result);
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Swift Mailer Verbose-sending Plugin Default View File.
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Plugin
* @subpackage VerboseSending
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../../ClassLoader.php";
Swift_ClassLoader::load("Swift_Plugin_VerboseSending_AbstractView");
/**
* The Default View for the Verbose Sending Plugin
* @package Swift_Plugin
* @subpackage VerboseSending
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_VerboseSending_DefaultView extends Swift_Plugin_VerboseSending_AbstractView
{
/**
* Number of recipients painted
* @var int
*/
protected $count = 0;
/**
* Paint the result of a send operation
* @param string The email address that was tried
* @param boolean True if the message was successfully sent
*/
public function paintResult($address, $result)
{
$this->count++;
$color = $result ? "#51c45f" : "#d67d71";
$result_text = $result ? "PASS" : "FAIL";
?>
<div style="color: #ffffff; margin: 2px; padding: 3px;
font-weight: bold; background: <?php echo $color; ?>;">
<span style="float: right; text-decoration: underline;">
<?php echo $result_text; ?></span>
Recipient (<?php echo $this->count; ?>):
<?php echo $address; ?>
</div>
<?php
flush();
}
}

View File

@@ -0,0 +1,234 @@
<?php
/**
* Swift Mailer Recipient List Container
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/ClassLoader.php";
Swift_ClassLoader::load("Swift_Address");
Swift_ClassLoader::load("Swift_Iterator_Array");
/**
* Swift's Recipient List container. Contains To, Cc, Bcc
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_RecipientList extends Swift_AddressContainer
{
/**
* The recipients in the To: header
* @var array
*/
protected $to = array();
/**
* The recipients in the Cc: header
* @var array
*/
protected $cc = array();
/**
* The recipients in the Bcc: header
* @var array
*/
protected $bcc = array();
/**
* Iterators to use when getting lists back out.
* If any iterators are present here, their relevant "addXX()" methods will be useless.
* As per the last note, any iterators need to be pre-configured before Swift::send() is called.
* @var array,Swift_Iterator
*/
protected $iterators = array("to" => null, "cc" => null, "bcc" => null);
/**
* Add a recipient.
* @param string The address
* @param string The name
* @param string The field (to, cc or bcc)
*/
public function add($address, $name="", $where="to")
{
if ($address instanceof Swift_Address)
{
$address_str = trim(strtolower($address->getAddress()));
}
elseif (is_array($address))
{
foreach ($address as $a) $this->add($a, $name, $where);
return;
}
else
{
$address_str = (string) $address;
$address_str = trim(strtolower($address_str));
$address = new Swift_Address($address_str, $name);
}
if (in_array($where, array("to", "cc", "bcc")))
{
$container =& $this->$where;
$container[$address_str] = $address;
}
}
/**
* Remove a recipient.
* @param string The address
* @param string The field (to, cc or bcc)
*/
public function remove($address, $where="to")
{
if ($address instanceof Swift_Address)
{
$key = trim(strtolower($address->getAddress()));
}
else $key = trim(strtolower((string) $address));
if (in_array($where, array("to", "cc", "bcc")))
{
if (array_key_exists($key, $this->$where)) unset($this->{$where}[$key]);
}
}
/**
* Get an iterator object for all the recipients in the given field.
* @param string The field name (to, cc or bcc)
* @return Swift_Iterator
*/
public function getIterator($where)
{
if (!empty($this->iterators[$where]))
{
return $this->iterators[$where];
}
elseif (in_array($where, array("to", "cc", "bcc")))
{
$it = new Swift_Iterator_Array($this->$where);
return $it;
}
}
/**
* Override the loading of the default iterator (Swift_ArrayIterator) and use the one given here.
* @param Swift_Iterator The iterator to use. It must be populated already.
*/
public function setIterator(Swift_Iterator $it, $where)
{
if (in_array($where, array("to", "cc", "bcc")))
{
$this->iterators[$where] = $it;
}
}
/**
* Add a To: recipient
* @param mixed The address to add. Can be a string or Swift_Address
* @param string The personal name, optional
*/
public function addTo($address, $name=null)
{
$this->add($address, $name, "to");
}
/**
* Get an array of addresses in the To: field
* The array contains Swift_Address objects
* @return array
*/
public function getTo()
{
return $this->to;
}
/**
* Remove a To: recipient from the list
* @param mixed The address to remove. Can be Swift_Address or a string
*/
public function removeTo($address)
{
$this->remove($address, "to");
}
/**
* Empty all To: addresses
*/
public function flushTo()
{
$this->to = null;
$this->to = array();
}
/**
* Add a Cc: recipient
* @param mixed The address to add. Can be a string or Swift_Address
* @param string The personal name, optional
*/
public function addCc($address, $name=null)
{
$this->add($address, $name, "cc");
}
/**
* Get an array of addresses in the Cc: field
* The array contains Swift_Address objects
* @return array
*/
public function getCc()
{
return $this->cc;
}
/**
* Remove a Cc: recipient from the list
* @param mixed The address to remove. Can be Swift_Address or a string
*/
public function removeCc($address)
{
$this->remove($address, "cc");
}
/**
* Empty all Cc: addresses
*/
public function flushCc()
{
$this->cc = null;
$this->cc = array();
}
/**
* Add a Bcc: recipient
* @param mixed The address to add. Can be a string or Swift_Address
* @param string The personal name, optional
*/
public function addBcc($address, $name=null)
{
$this->add($address, $name, "bcc");
}
/**
* Get an array of addresses in the Bcc: field
* The array contains Swift_Address objects
* @return array
*/
public function getBcc()
{
return $this->bcc;
}
/**
* Remove a Bcc: recipient from the list
* @param mixed The address to remove. Can be Swift_Address or a string
*/
public function removeBcc($address)
{
$this->remove($address, "bcc");
}
/**
* Empty all Bcc: addresses
*/
public function flushBcc()
{
$this->bcc = null;
$this->bcc = array();
}
/**
* Empty the entire list
*/
public function flush()
{
$this->flushTo();
$this->flushCc();
$this->flushBcc();
}
}