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

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();
}
}