Fix various compatibility issues with PHP 8

This commit is contained in:
Frédéric Guillot
2022-02-08 21:44:00 -08:00
committed by Frédéric Guillot
parent f5bb55bdb8
commit 4bf3b0d459
411 changed files with 99 additions and 26046 deletions

View File

@@ -0,0 +1,739 @@
<?php
namespace Gregwar\Captcha;
use \Exception;
/**
* Builds a new captcha image
* Uses the fingerprint parameter, if one is passed, to generate the same image
*
* @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
*/
class CaptchaBuilder implements CaptchaBuilderInterface
{
/**
* @var array
*/
protected $fingerprint = array();
/**
* @var bool
*/
protected $useFingerprint = false;
/**
* @var array
*/
protected $textColor = array();
/**
* @var array
*/
protected $lineColor = null;
/**
* @var array
*/
protected $backgroundColor = null;
/**
* @var array
*/
protected $backgroundImages = array();
/**
* @var resource
*/
protected $contents = null;
/**
* @var string
*/
protected $phrase = null;
/**
* @var PhraseBuilderInterface
*/
protected $builder;
/**
* @var bool
*/
protected $distortion = true;
/**
* The maximum number of lines to draw in front of
* the image. null - use default algorithm
*/
protected $maxFrontLines = null;
/**
* The maximum number of lines to draw behind
* the image. null - use default algorithm
*/
protected $maxBehindLines = null;
/**
* The maximum angle of char
*/
protected $maxAngle = 8;
/**
* The maximum offset of char
*/
protected $maxOffset = 5;
/**
* Is the interpolation enabled ?
*
* @var bool
*/
protected $interpolation = true;
/**
* Ignore all effects
*
* @var bool
*/
protected $ignoreAllEffects = false;
/**
* Allowed image types for the background images
*
* @var array
*/
protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif');
/**
* The image contents
*/
public function getContents()
{
return $this->contents;
}
/**
* Enable/Disables the interpolation
*
* @param $interpolate bool True to enable, false to disable
*
* @return CaptchaBuilder
*/
public function setInterpolation($interpolate = true)
{
$this->interpolation = $interpolate;
return $this;
}
/**
* Temporary dir, for OCR check
*/
public $tempDir = 'temp/';
public function __construct($phrase = null, PhraseBuilderInterface $builder = null)
{
if ($builder === null) {
$this->builder = new PhraseBuilder;
} else {
$this->builder = $builder;
}
$this->phrase = is_string($phrase) ? $phrase : $this->builder->build($phrase);
}
/**
* Setting the phrase
*/
public function setPhrase($phrase)
{
$this->phrase = (string) $phrase;
}
/**
* Enables/disable distortion
*/
public function setDistortion($distortion)
{
$this->distortion = (bool) $distortion;
return $this;
}
public function setMaxBehindLines($maxBehindLines)
{
$this->maxBehindLines = $maxBehindLines;
return $this;
}
public function setMaxFrontLines($maxFrontLines)
{
$this->maxFrontLines = $maxFrontLines;
return $this;
}
public function setMaxAngle($maxAngle)
{
$this->maxAngle = $maxAngle;
return $this;
}
public function setMaxOffset($maxOffset)
{
$this->maxOffset = $maxOffset;
return $this;
}
/**
* Gets the captcha phrase
*/
public function getPhrase()
{
return $this->phrase;
}
/**
* Returns true if the given phrase is good
*/
public function testPhrase($phrase)
{
return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase()));
}
/**
* Instantiation
*/
public static function create($phrase = null)
{
return new self($phrase);
}
/**
* Sets the text color to use
*/
public function setTextColor($r, $g, $b)
{
$this->textColor = array($r, $g, $b);
return $this;
}
/**
* Sets the background color to use
*/
public function setBackgroundColor($r, $g, $b)
{
$this->backgroundColor = array($r, $g, $b);
return $this;
}
public function setLineColor($r, $g, $b)
{
$this->lineColor = array($r, $g, $b);
return $this;
}
/**
* Sets the ignoreAllEffects value
*
* @param bool $ignoreAllEffects
* @return CaptchaBuilder
*/
public function setIgnoreAllEffects($ignoreAllEffects)
{
$this->ignoreAllEffects = $ignoreAllEffects;
return $this;
}
/**
* Sets the list of background images to use (one image is randomly selected)
*/
public function setBackgroundImages(array $backgroundImages)
{
$this->backgroundImages = $backgroundImages;
return $this;
}
/**
* Draw lines over the image
*/
protected function drawLine($image, $width, $height, $tcol = null)
{
if ($this->lineColor === null) {
$red = $this->rand(100, 255);
$green = $this->rand(100, 255);
$blue = $this->rand(100, 255);
} else {
$red = $this->lineColor[0];
$green = $this->lineColor[1];
$blue = $this->lineColor[2];
}
if ($tcol === null) {
$tcol = imagecolorallocate($image, $red, $green, $blue);
}
if ($this->rand(0, 1)) { // Horizontal
$Xa = $this->rand(0, $width/2);
$Ya = $this->rand(0, $height);
$Xb = $this->rand($width/2, $width);
$Yb = $this->rand(0, $height);
} else { // Vertical
$Xa = $this->rand(0, $width);
$Ya = $this->rand(0, $height/2);
$Xb = $this->rand(0, $width);
$Yb = $this->rand($height/2, $height);
}
imagesetthickness($image, $this->rand(1, 3));
imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol);
}
/**
* Apply some post effects
*/
protected function postEffect($image)
{
if (!function_exists('imagefilter')) {
return;
}
if ($this->backgroundColor != null || $this->textColor != null) {
return;
}
// Negate ?
if ($this->rand(0, 1) == 0) {
imagefilter($image, IMG_FILTER_NEGATE);
}
// Edge ?
if ($this->rand(0, 10) == 0) {
imagefilter($image, IMG_FILTER_EDGEDETECT);
}
// Contrast
imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10));
// Colorize
if ($this->rand(0, 5) == 0) {
imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50));
}
}
/**
* Writes the phrase on the image
*/
protected function writePhrase($image, $phrase, $font, $width, $height)
{
$length = mb_strlen($phrase);
if ($length === 0) {
return \imagecolorallocate($image, 0, 0, 0);
}
// Gets the text size and start position
$size = $width / $length - $this->rand(0, 3) - 1;
$box = \imagettfbbox($size, 0, $font, $phrase);
$textWidth = $box[2] - $box[0];
$textHeight = $box[1] - $box[7];
$x = ($width - $textWidth) / 2;
$y = ($height - $textHeight) / 2 + $size;
if (!$this->textColor) {
$textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150));
} else {
$textColor = $this->textColor;
}
$col = \imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]);
// Write the letters one by one, with random angle
for ($i=0; $i<$length; $i++) {
$symbol = mb_substr($phrase, $i, 1);
$box = \imagettfbbox($size, 0, $font, $symbol);
$w = $box[2] - $box[0];
$angle = $this->rand(-$this->maxAngle, $this->maxAngle);
$offset = $this->rand(-$this->maxOffset, $this->maxOffset);
\imagettftext($image, (float) $size, (float) $angle, (int) $x, (int) $y + $offset, (int) $col, (string) $font, (string) $symbol);
$x += $w;
}
return $col;
}
/**
* Try to read the code against an OCR
*/
public function isOCRReadable()
{
if (!is_dir($this->tempDir)) {
@mkdir($this->tempDir, 0755, true);
}
$tempj = $this->tempDir . uniqid('captcha', true) . '.jpg';
$tempp = $this->tempDir . uniqid('captcha', true) . '.pgm';
$this->save($tempj);
shell_exec("convert $tempj $tempp");
$value = trim(strtolower(shell_exec("ocrad $tempp")));
@unlink($tempj);
@unlink($tempp);
return $this->testPhrase($value);
}
/**
* Builds while the code is readable against an OCR
*/
public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null)
{
do {
$this->build($width, $height, $font, $fingerprint);
} while ($this->isOCRReadable());
}
/**
* Generate the image
*/
public function build($width = 150, $height = 40, $font = null, $fingerprint = null)
{
if (null !== $fingerprint) {
$this->fingerprint = $fingerprint;
$this->useFingerprint = true;
} else {
$this->fingerprint = array();
$this->useFingerprint = false;
}
if ($font === null) {
$font = __DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf';
}
if (empty($this->backgroundImages)) {
// if background images list is not set, use a color fill as a background
$image = imagecreatetruecolor($width, $height);
if ($this->backgroundColor == null) {
$bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255));
} else {
$color = $this->backgroundColor;
$bg = imagecolorallocate($image, $color[0], $color[1], $color[2]);
}
$this->background = $bg;
imagefill($image, 0, 0, $bg);
} else {
// use a random background image
$randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)];
$imageType = $this->validateBackgroundImage($randomBackgroundImage);
$image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType);
}
// Apply effects
if (!$this->ignoreAllEffects) {
$square = $width * $height;
$effects = $this->rand($square/3000, $square/2000);
// set the maximum number of lines to draw in front of the text
if ($this->maxBehindLines != null && $this->maxBehindLines > 0) {
$effects = min($this->maxBehindLines, $effects);
}
if ($this->maxBehindLines !== 0) {
for ($e = 0; $e < $effects; $e++) {
$this->drawLine($image, $width, $height);
}
}
}
// Write CAPTCHA text
$color = $this->writePhrase($image, $this->phrase, $font, $width, $height);
// Apply effects
if (!$this->ignoreAllEffects) {
$square = $width * $height;
$effects = $this->rand($square/3000, $square/2000);
// set the maximum number of lines to draw in front of the text
if ($this->maxFrontLines != null && $this->maxFrontLines > 0) {
$effects = min($this->maxFrontLines, $effects);
}
if ($this->maxFrontLines !== 0) {
for ($e = 0; $e < $effects; $e++) {
$this->drawLine($image, $width, $height, $color);
}
}
}
// Distort the image
if ($this->distortion && !$this->ignoreAllEffects) {
$image = $this->distort($image, $width, $height, $bg);
}
// Post effects
if (!$this->ignoreAllEffects) {
$this->postEffect($image);
}
$this->contents = $image;
return $this;
}
/**
* Distorts the image
*/
public function distort($image, $width, $height, $bg)
{
$contents = imagecreatetruecolor($width, $height);
$X = $this->rand(0, $width);
$Y = $this->rand(0, $height);
$phase = $this->rand(0, 10);
$scale = 1.1 + $this->rand(0, 10000) / 30000;
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$Vx = $x - $X;
$Vy = $y - $Y;
$Vn = sqrt($Vx * $Vx + $Vy * $Vy);
if ($Vn != 0) {
$Vn2 = $Vn + 4 * sin($Vn / 30);
$nX = $X + ($Vx * $Vn2 / $Vn);
$nY = $Y + ($Vy * $Vn2 / $Vn);
} else {
$nX = $X;
$nY = $Y;
}
$nY = $nY + $scale * sin($phase + $nX * 0.2);
if ($this->interpolation) {
$p = $this->interpolate(
$nX - floor($nX),
$nY - floor($nY),
$this->getCol($image, floor($nX), floor($nY), $bg),
$this->getCol($image, ceil($nX), floor($nY), $bg),
$this->getCol($image, floor($nX), ceil($nY), $bg),
$this->getCol($image, ceil($nX), ceil($nY), $bg)
);
} else {
$p = $this->getCol($image, round($nX), round($nY), $bg);
}
if ($p == 0) {
$p = $bg;
}
imagesetpixel($contents, $x, $y, $p);
}
}
return $contents;
}
/**
* Saves the Captcha to a jpeg file
*/
public function save($filename, $quality = 90)
{
imagejpeg($this->contents, $filename, $quality);
}
/**
* Gets the image GD
*/
public function getGd()
{
return $this->contents;
}
/**
* Gets the image contents
*/
public function get($quality = 90)
{
ob_start();
$this->output($quality);
return ob_get_clean();
}
/**
* Gets the HTML inline base64
*/
public function inline($quality = 90)
{
return 'data:image/jpeg;base64,' . base64_encode($this->get($quality));
}
/**
* Outputs the image
*/
public function output($quality = 90)
{
imagejpeg($this->contents, null, $quality);
}
/**
* @return array
*/
public function getFingerprint()
{
return $this->fingerprint;
}
/**
* Returns a random number or the next number in the
* fingerprint
*/
protected function rand($min, $max)
{
if (!is_array($this->fingerprint)) {
$this->fingerprint = array();
}
if ($this->useFingerprint) {
$value = current($this->fingerprint);
next($this->fingerprint);
} else {
$value = mt_rand($min, $max);
$this->fingerprint[] = $value;
}
return $value;
}
/**
* @param $x
* @param $y
* @param $nw
* @param $ne
* @param $sw
* @param $se
*
* @return int
*/
protected function interpolate($x, $y, $nw, $ne, $sw, $se)
{
list($r0, $g0, $b0) = $this->getRGB($nw);
list($r1, $g1, $b1) = $this->getRGB($ne);
list($r2, $g2, $b2) = $this->getRGB($sw);
list($r3, $g3, $b3) = $this->getRGB($se);
$cx = 1.0 - $x;
$cy = 1.0 - $y;
$m0 = $cx * $r0 + $x * $r1;
$m1 = $cx * $r2 + $x * $r3;
$r = (int) ($cy * $m0 + $y * $m1);
$m0 = $cx * $g0 + $x * $g1;
$m1 = $cx * $g2 + $x * $g3;
$g = (int) ($cy * $m0 + $y * $m1);
$m0 = $cx * $b0 + $x * $b1;
$m1 = $cx * $b2 + $x * $b3;
$b = (int) ($cy * $m0 + $y * $m1);
return ($r << 16) | ($g << 8) | $b;
}
/**
* @param $image
* @param $x
* @param $y
*
* @return int
*/
protected function getCol($image, $x, $y, $background)
{
$L = imagesx($image);
$H = imagesy($image);
if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) {
return $background;
}
return imagecolorat($image, $x, $y);
}
/**
* @param $col
*
* @return array
*/
protected function getRGB($col)
{
return array(
(int) ($col >> 16) & 0xff,
(int) ($col >> 8) & 0xff,
(int) ($col) & 0xff,
);
}
/**
* Validate the background image path. Return the image type if valid
*
* @param string $backgroundImage
* @return string
* @throws Exception
*/
protected function validateBackgroundImage($backgroundImage)
{
// check if file exists
if (!file_exists($backgroundImage)) {
$backgroundImageExploded = explode('/', $backgroundImage);
$imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage;
throw new Exception('Invalid background image: ' . $imageFileName);
}
// check image type
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$imageType = finfo_file($finfo, $backgroundImage);
finfo_close($finfo);
if (!in_array($imageType, $this->allowedBackgroundImageTypes)) {
throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes));
}
return $imageType;
}
/**
* Create background image from type
*
* @param string $backgroundImage
* @param string $imageType
* @return resource
* @throws Exception
*/
protected function createBackgroundImageFromType($backgroundImage, $imageType)
{
switch ($imageType) {
case 'image/jpeg':
$image = imagecreatefromjpeg($backgroundImage);
break;
case 'image/png':
$image = imagecreatefrompng($backgroundImage);
break;
case 'image/gif':
$image = imagecreatefromgif($backgroundImage);
break;
default:
throw new Exception('Not supported file type for background image!');
break;
}
return $image;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Gregwar\Captcha;
/**
* A Captcha builder
*/
interface CaptchaBuilderInterface
{
/**
* Builds the code
*/
public function build($width, $height, $font, $fingerprint);
/**
* Saves the code to a file
*/
public function save($filename, $quality);
/**
* Gets the image contents
*/
public function get($quality);
/**
* Outputs the image
*/
public function output($quality);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,105 @@
<?php
namespace Gregwar\Captcha;
use Symfony\Component\Finder\Finder;
/**
* Handles actions related to captcha image files including saving and garbage collection
*
* @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy@quizzle.com>
*/
class ImageFileHandler
{
/**
* Name of folder for captcha images
* @var string
*/
protected $imageFolder;
/**
* Absolute path to public web folder
* @var string
*/
protected $webPath;
/**
* Frequency of garbage collection in fractions of 1
* @var int
*/
protected $gcFreq;
/**
* Maximum age of images in minutes
* @var int
*/
protected $expiration;
/**
* @param $imageFolder
* @param $webPath
* @param $gcFreq
* @param $expiration
*/
public function __construct($imageFolder, $webPath, $gcFreq, $expiration)
{
$this->imageFolder = $imageFolder;
$this->webPath = $webPath;
$this->gcFreq = $gcFreq;
$this->expiration = $expiration;
}
/**
* Saves the provided image content as a file
*
* @param string $contents
*
* @return string
*/
public function saveAsFile($contents)
{
$this->createFolderIfMissing();
$filename = md5(uniqid()) . '.jpg';
$filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename;
imagejpeg($contents, $filePath, 15);
return '/' . $this->imageFolder . '/' . $filename;
}
/**
* Randomly runs garbage collection on the image directory
*
* @return bool
*/
public function collectGarbage()
{
if (!mt_rand(1, $this->gcFreq) == 1) {
return false;
}
$this->createFolderIfMissing();
$finder = new Finder();
$criteria = sprintf('<= now - %s minutes', $this->expiration);
$finder->in($this->webPath . '/' . $this->imageFolder)
->date($criteria);
foreach ($finder->files() as $file) {
unlink($file->getPathname());
}
return true;
}
/**
* Creates the folder if it doesn't exist
*/
protected function createFolderIfMissing()
{
if (!file_exists($this->webPath . '/' . $this->imageFolder)) {
mkdir($this->webPath . '/' . $this->imageFolder, 0755);
}
}
}

19
libs/Captcha/LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) <2012-2017> Grégoire Passault
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,75 @@
<?php
namespace Gregwar\Captcha;
/**
* Generates random phrase
*
* @author Gregwar <g.passault@gmail.com>
*/
class PhraseBuilder implements PhraseBuilderInterface
{
/**
* @var int
*/
public $length;
/**
* @var string
*/
public $charset;
/**
* Constructs a PhraseBuilder with given parameters
*/
public function __construct($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$this->length = $length;
$this->charset = $charset;
}
/**
* Generates random phrase of given length with given charset
*/
public function build($length = null, $charset = null)
{
if ($length !== null) {
$this->length = $length;
}
if ($charset !== null) {
$this->charset = $charset;
}
$phrase = '';
$chars = str_split($this->charset);
for ($i = 0; $i < $this->length; $i++) {
$phrase .= $chars[array_rand($chars)];
}
return $phrase;
}
/**
* "Niceize" a code
*/
public function niceize($str)
{
return self::doNiceize($str);
}
/**
* A static helper to niceize
*/
public static function doNiceize($str)
{
return strtr(strtolower($str), '01', 'ol');
}
/**
* A static helper to compare
*/
public static function comparePhrases($str1, $str2)
{
return self::doNiceize($str1) === self::doNiceize($str2);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Gregwar\Captcha;
/**
* Interface for the PhraseBuilder
*
* @author Gregwar <g.passault@gmail.com>
*/
interface PhraseBuilderInterface
{
/**
* Generates random phrase of given length with given charset
*/
public function build();
/**
* "Niceize" a code
*/
public function niceize($str);
}

184
libs/ical/Component.php Normal file
View File

@@ -0,0 +1,184 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
use Eluceo\iCal\Util\ComponentUtil;
/**
* Abstract Calender Component.
*/
abstract class Component
{
/**
* Array of Components.
*
* @var Component[]
*/
protected $components = [];
/**
* The order in which the components will be rendered during build.
*
* Not defined components will be appended at the end.
*
* @var array
*/
private $componentsBuildOrder = ['VTIMEZONE', 'DAYLIGHT', 'STANDARD'];
/**
* The type of the concrete Component.
*
* @abstract
*
* @return string
*/
abstract public function getType();
/**
* Building the PropertyBag.
*
* @abstract
*
* @return PropertyBag
*/
abstract public function buildPropertyBag();
/**
* Adds a Component.
*
* If $key is given, the component at $key will be replaced else the component will be append.
*
* @param Component $component The Component that will be added
* @param null $key The key of the Component
*/
public function addComponent(self $component, $key = null)
{
if (null == $key) {
$this->components[] = $component;
} else {
$this->components[$key] = $component;
}
}
/**
* Set all Components.
*
* @param Component[] $components The array of Component that will be set
* @param null $key The key of the Component
*/
public function setComponents(array $components)
{
$this->components = $components;
return $this;
}
/**
* Renders an array containing the lines of the iCal file.
*
* @return array
*/
public function build()
{
$lines = [];
$lines[] = sprintf('BEGIN:%s', $this->getType());
/** @var $property Property */
foreach ($this->buildPropertyBag() as $property) {
foreach ($property->toLines() as $l) {
$lines[] = $l;
}
}
$this->buildComponents($lines);
$lines[] = sprintf('END:%s', $this->getType());
$ret = [];
foreach ($lines as $line) {
foreach (ComponentUtil::fold($line) as $l) {
$ret[] = $l;
}
}
return $ret;
}
/**
* Renders the output.
*
* @return string
*/
public function render()
{
return implode("\r\n", $this->build());
}
/**
* Renders the output when treating the class as a string.
*
* @return string
*/
public function __toString()
{
return $this->render();
}
/**
* @param $lines
*
* @return array
*/
private function buildComponents(array &$lines)
{
$componentsByType = [];
/** @var $component Component */
foreach ($this->components as $component) {
$type = $component->getType();
if (!isset($componentsByType[$type])) {
$componentsByType[$type] = [];
}
$componentsByType[$type][] = $component;
}
// render ordered components
foreach ($this->componentsBuildOrder as $type) {
if (!isset($componentsByType[$type])) {
continue;
}
foreach ($componentsByType[$type] as $component) {
$this->addComponentLines($lines, $component);
}
unset($componentsByType[$type]);
}
// render all other
foreach ($componentsByType as $components) {
foreach ($components as $component) {
$this->addComponentLines($lines, $component);
}
}
}
/**
* @param Component $component
*/
private function addComponentLines(array &$lines, self $component)
{
foreach ($component->build() as $l) {
$lines[] = $l;
}
}
}

View File

@@ -0,0 +1,150 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of the VALARM component.
*/
class Alarm extends Component
{
/**
* Alarm ACTION property.
*
* According to RFC 5545: 3.8.6.1. Action
*
* @see http://tools.ietf.org/html/rfc5545#section-3.8.6.1
*/
const ACTION_AUDIO = 'AUDIO';
const ACTION_DISPLAY = 'DISPLAY';
const ACTION_EMAIL = 'EMAIL';
protected $action;
protected $repeat;
protected $duration;
protected $description;
protected $attendee;
protected $trigger;
public function getType()
{
return 'VALARM';
}
public function getAction()
{
return $this->action;
}
public function getRepeat()
{
return $this->repeat;
}
public function getDuration()
{
return $this->duration;
}
public function getDescription()
{
return $this->description;
}
public function getAttendee()
{
return $this->attendee;
}
public function getTrigger()
{
return $this->trigger;
}
public function setAction($action)
{
$this->action = $action;
return $this;
}
public function setRepeat($repeat)
{
$this->repeat = $repeat;
return $this;
}
public function setDuration($duration)
{
$this->duration = $duration;
return $this;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function setAttendee($attendee)
{
$this->attendee = $attendee;
return $this;
}
public function setTrigger($trigger)
{
$this->trigger = $trigger;
return $this;
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
if (null != $this->trigger) {
$propertyBag->set('TRIGGER', $this->trigger);
}
if (null != $this->action) {
$propertyBag->set('ACTION', $this->action);
}
if (null != $this->repeat) {
$propertyBag->set('REPEAT', $this->repeat);
}
if (null != $this->duration) {
$propertyBag->set('DURATION', $this->duration);
}
if (null != $this->description) {
$propertyBag->set('DESCRIPTION', $this->description);
}
if (null != $this->attendee) {
$propertyBag->set('ATTENDEE', $this->attendee);
}
return $propertyBag;
}
}

View File

@@ -0,0 +1,324 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\PropertyBag;
class Calendar extends Component
{
/**
* Methods for calendar components.
*
* According to RFC 5545: 3.7.2. Method
*
* @see http://tools.ietf.org/html/rfc5545#section-3.7.2
*
* And then according to RFC 2446: 3 APPLICATION PROTOCOL ELEMENTS
* @see https://tools.ietf.org/html/rfc2446#section-3.2
*/
const METHOD_PUBLISH = 'PUBLISH';
const METHOD_REQUEST = 'REQUEST';
const METHOD_REPLY = 'REPLY';
const METHOD_ADD = 'ADD';
const METHOD_CANCEL = 'CANCEL';
const METHOD_REFRESH = 'REFRESH';
const METHOD_COUNTER = 'COUNTER';
const METHOD_DECLINECOUNTER = 'DECLINECOUNTER';
/**
* This property defines the calendar scale used for the calendar information specified in the iCalendar object.
*
* According to RFC 5545: 3.7.1. Calendar Scale
*
* @see http://tools.ietf.org/html/rfc5545#section-3.7
*/
const CALSCALE_GREGORIAN = 'GREGORIAN';
/**
* The Product Identifier.
*
* According to RFC 5545: 3.7.3 Product Identifier
*
* This property specifies the identifier for the product that created the Calendar object.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.7.3
*
* @var string
*/
protected $prodId = null;
protected $method = null;
protected $name = null;
protected $description = null;
protected $timezone = null;
/**
* This property defines the calendar scale used for the
* calendar information specified in the iCalendar object.
*
* Also identifies the calendar type of a non-Gregorian recurring appointment.
*
* @var string
*
* @see http://tools.ietf.org/html/rfc5545#section-3.7
* @see http://msdn.microsoft.com/en-us/library/ee237520(v=exchg.80).aspx
*/
protected $calendarScale = null;
/**
* Specifies whether or not the iCalendar file only contains one appointment.
*
* @var bool
*
* @see http://msdn.microsoft.com/en-us/library/ee203486(v=exchg.80).aspx
*/
protected $forceInspectOrOpen = false;
/**
* Specifies a globally unique identifier for the calendar.
*
* @var string
*
* @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx
*/
protected $calId = null;
/**
* Specifies a suggested iCalendar file download frequency for clients and
* servers with sync capabilities.
*
* For example you can set the value to 'P1W' if the calendar should be
* synced once a week. Use 'P3H' to sync the file every 3 hours.
*
* @var string
*
* @see http://msdn.microsoft.com/en-us/library/ee178699(v=exchg.80).aspx
*/
protected $publishedTTL = null;
/**
* Specifies a color for the calendar in calendar for Apple/Outlook.
*
* @var string
*
* @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx
*/
protected $calendarColor = null;
public function __construct($prodId)
{
if (empty($prodId)) {
throw new \UnexpectedValueException('PRODID cannot be empty');
}
$this->prodId = $prodId;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'VCALENDAR';
}
/**
* @param $method
*
* @return $this
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* @param $name
*
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @param $description
*
* @return $this
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @param $timezone
*
* @return $this
*/
public function setTimezone($timezone)
{
$this->timezone = $timezone;
return $this;
}
/**
* @param $calendarColor
*
* @return $this
*/
public function setCalendarColor($calendarColor)
{
$this->calendarColor = $calendarColor;
return $this;
}
/**
* @param $calendarScale
*
* @return $this
*/
public function setCalendarScale($calendarScale)
{
$this->calendarScale = $calendarScale;
return $this;
}
/**
* @param bool $forceInspectOrOpen
*
* @return $this
*/
public function setForceInspectOrOpen($forceInspectOrOpen)
{
$this->forceInspectOrOpen = $forceInspectOrOpen;
return $this;
}
/**
* @param string $calId
*
* @return $this
*/
public function setCalId($calId)
{
$this->calId = $calId;
return $this;
}
/**
* @param string $ttl
*
* @return $this
*/
public function setPublishedTTL($ttl)
{
$this->publishedTTL = $ttl;
return $this;
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
$propertyBag->set('VERSION', '2.0');
$propertyBag->set('PRODID', $this->prodId);
if ($this->method) {
$propertyBag->set('METHOD', $this->method);
}
if ($this->calendarColor) {
$propertyBag->set('X-APPLE-CALENDAR-COLOR', $this->calendarColor);
$propertyBag->set('X-OUTLOOK-COLOR', $this->calendarColor);
$propertyBag->set('X-FUNAMBOL-COLOR', $this->calendarColor);
}
if ($this->calendarScale) {
$propertyBag->set('CALSCALE', $this->calendarScale);
$propertyBag->set('X-MICROSOFT-CALSCALE', $this->calendarScale);
}
if ($this->name) {
$propertyBag->set('X-WR-CALNAME', $this->name);
}
if ($this->description) {
$propertyBag->set('X-WR-CALDESC', $this->description);
}
if ($this->timezone) {
if ($this->timezone instanceof Timezone) {
$propertyBag->set('X-WR-TIMEZONE', $this->timezone->getZoneIdentifier());
$this->addComponent($this->timezone);
} else {
$propertyBag->set('X-WR-TIMEZONE', $this->timezone);
$this->addComponent(new Timezone($this->timezone));
}
}
if ($this->forceInspectOrOpen) {
$propertyBag->set('X-MS-OLK-FORCEINSPECTOROPEN', $this->forceInspectOrOpen);
}
if ($this->calId) {
$propertyBag->set('X-WR-RELCALID', $this->calId);
}
if ($this->publishedTTL) {
$propertyBag->set('X-PUBLISHED-TTL', $this->publishedTTL);
}
return $propertyBag;
}
/**
* Adds an Event to the Calendar.
*
* Wrapper for addComponent()
*
* @see Eluceo\iCal::addComponent
* @deprecated Please, use public method addComponent() from abstract Component class
*/
public function addEvent(Event $event)
{
$this->addComponent($event);
}
/**
* @return string|null
*/
public function getProdId()
{
return $this->prodId;
}
public function getMethod()
{
return $this->method;
}
}

View File

@@ -0,0 +1,941 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\Property;
use Eluceo\iCal\Property\DateTimeProperty;
use Eluceo\iCal\Property\DateTimesProperty;
use Eluceo\iCal\Property\Event\Attachment;
use Eluceo\iCal\Property\Event\Attendees;
use Eluceo\iCal\Property\Event\Geo;
use Eluceo\iCal\Property\Event\Organizer;
use Eluceo\iCal\Property\Event\RecurrenceId;
use Eluceo\iCal\Property\Event\RecurrenceRule;
use Eluceo\iCal\Property\RawStringValue;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of the EVENT component.
*/
class Event extends Component
{
const TIME_TRANSPARENCY_OPAQUE = 'OPAQUE';
const TIME_TRANSPARENCY_TRANSPARENT = 'TRANSPARENT';
const STATUS_TENTATIVE = 'TENTATIVE';
const STATUS_CONFIRMED = 'CONFIRMED';
const STATUS_CANCELLED = 'CANCELLED';
const MS_BUSYSTATUS_FREE = 'FREE';
const MS_BUSYSTATUS_TENTATIVE = 'TENTATIVE';
const MS_BUSYSTATUS_BUSY = 'BUSY';
const MS_BUSYSTATUS_OOF = 'OOF';
/**
* @var string
*/
protected $uniqueId;
/**
* The property indicates the date/time that the instance of
* the iCalendar object was created.
*
* The value MUST be specified in the UTC time format.
*
* @var \DateTime
*/
protected $dtStamp;
/**
* @var \DateTime
*/
protected $dtStart;
/**
* Preferentially chosen over the duration if both are set.
*
* @var \DateTime
*/
protected $dtEnd;
/**
* @var \DateInterval
*/
protected $duration;
/**
* @var bool
*/
protected $noTime = false;
/**
* @var string
*/
protected $msBusyStatus = null;
/**
* @var string
*/
protected $url;
/**
* @var string
*/
protected $location;
/**
* @var string
*/
protected $locationTitle;
/**
* @var Geo
*/
protected $locationGeo;
/**
* @var string
*/
protected $summary;
/**
* @var Organizer
*/
protected $organizer;
/**
* @see https://tools.ietf.org/html/rfc5545#section-3.8.2.7
*
* @var string
*/
protected $transparency = self::TIME_TRANSPARENCY_OPAQUE;
/**
* If set to true the timezone will be added to the event.
*
* @var bool
*/
protected $useTimezone = false;
/**
* If set will be used as the timezone identifier.
*
* @var string
*/
protected $timezoneString = '';
/**
* @var int
*/
protected $sequence = 0;
/**
* @var Attendees
*/
protected $attendees;
/**
* @var string
*/
protected $description;
/**
* @var string
*/
protected $descriptionHTML;
/**
* @var string
*/
protected $status;
/**
* @var RecurrenceRule
*/
protected $recurrenceRule;
/**
* @var array
*/
protected $recurrenceRules = [];
/**
* This property specifies the date and time that the calendar
* information was created.
*
* The value MUST be specified in the UTC time format.
*
* @var \DateTime
*/
protected $created;
/**
* The property specifies the date and time that the information
* associated with the calendar component was last revised.
*
* The value MUST be specified in the UTC time format.
*
* @var \DateTime
*/
protected $modified;
/**
* Indicates if the UTC time should be used or not.
*
* @var bool
*/
protected $useUtc = true;
/**
* @var bool
*/
protected $cancelled;
/**
* This property is used to specify categories or subtypes
* of the calendar component. The categories are useful in searching
* for a calendar component of a particular type and category.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.1.2
*
* @var array
*/
protected $categories;
/**
* https://tools.ietf.org/html/rfc5545#section-3.8.1.3.
*
* @var bool
*/
protected $isPrivate = false;
/**
* Dates to be excluded from a series of events.
*
* @var \DateTimeInterface[]
*/
protected $exDates = [];
/**
* @var RecurrenceId
*/
protected $recurrenceId;
/**
* @var Attachment[]
*/
protected $attachments = [];
public function __construct(string $uniqueId = null)
{
if (null == $uniqueId) {
$uniqueId = uniqid();
}
$this->uniqueId = $uniqueId;
$this->attendees = new Attendees();
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'VEVENT';
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
// mandatory information
$propertyBag->set('UID', $this->uniqueId);
$propertyBag->add(new DateTimeProperty('DTSTART', $this->dtStart, $this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString));
$propertyBag->set('SEQUENCE', $this->sequence);
$propertyBag->set('TRANSP', $this->transparency);
if ($this->status) {
$propertyBag->set('STATUS', $this->status);
}
// An event can have a 'dtend' or 'duration', but not both.
if ($this->dtEnd !== null) {
$dtEnd = clone $this->dtEnd;
if ($this->noTime === true) {
$dtEnd = $dtEnd->add(new \DateInterval('P1D'));
}
$propertyBag->add(new DateTimeProperty('DTEND', $dtEnd, $this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString));
} elseif ($this->duration !== null) {
$propertyBag->set('DURATION', $this->duration->format('P%dDT%hH%iM%sS'));
}
// optional information
if (null != $this->url) {
$propertyBag->set('URL', $this->url);
}
if (null != $this->location) {
$propertyBag->set('LOCATION', $this->location);
if (null != $this->locationGeo) {
$propertyBag->add(
new Property(
'X-APPLE-STRUCTURED-LOCATION',
new RawStringValue('geo:' . $this->locationGeo->getGeoLocationAsString(',')),
[
'VALUE' => 'URI',
'X-ADDRESS' => $this->location,
'X-APPLE-RADIUS' => 49,
'X-TITLE' => $this->locationTitle,
]
)
);
}
}
if (null != $this->locationGeo) {
$propertyBag->add($this->locationGeo);
}
if (null != $this->summary) {
$propertyBag->set('SUMMARY', $this->summary);
}
if (null != $this->attendees) {
$propertyBag->add($this->attendees);
}
$propertyBag->set('CLASS', $this->isPrivate ? 'PRIVATE' : 'PUBLIC');
if (null != $this->description) {
$propertyBag->set('DESCRIPTION', $this->description);
}
if (null != $this->descriptionHTML) {
$propertyBag->add(
new Property(
'X-ALT-DESC',
$this->descriptionHTML,
[
'FMTTYPE' => 'text/html',
]
)
);
}
if (null != $this->recurrenceRule) {
$propertyBag->set('RRULE', $this->recurrenceRule);
}
foreach ($this->recurrenceRules as $recurrenceRule) {
$propertyBag->set('RRULE', $recurrenceRule);
}
if (null != $this->recurrenceId) {
$this->recurrenceId->applyTimeSettings($this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString);
$propertyBag->add($this->recurrenceId);
}
if (!empty($this->exDates)) {
$propertyBag->add(new DateTimesProperty('EXDATE', $this->exDates, $this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString));
}
if ($this->cancelled) {
$propertyBag->set('STATUS', 'CANCELLED');
}
if (null != $this->organizer) {
$propertyBag->add($this->organizer);
}
if ($this->noTime) {
$propertyBag->set('X-MICROSOFT-CDO-ALLDAYEVENT', 'TRUE');
}
if (null != $this->msBusyStatus) {
$propertyBag->set('X-MICROSOFT-CDO-BUSYSTATUS', $this->msBusyStatus);
$propertyBag->set('X-MICROSOFT-CDO-INTENDEDSTATUS', $this->msBusyStatus);
}
if (null != $this->categories) {
$propertyBag->set('CATEGORIES', $this->categories);
}
$propertyBag->add(
new DateTimeProperty('DTSTAMP', $this->dtStamp ?: new \DateTimeImmutable(), false, false, true)
);
if ($this->created) {
$propertyBag->add(new DateTimeProperty('CREATED', $this->created, false, false, true));
}
if ($this->modified) {
$propertyBag->add(new DateTimeProperty('LAST-MODIFIED', $this->modified, false, false, true));
}
foreach ($this->attachments as $attachment) {
$propertyBag->add($attachment);
}
return $propertyBag;
}
/**
* @param $dtEnd
*
* @return $this
*/
public function setDtEnd($dtEnd)
{
$this->dtEnd = $dtEnd;
return $this;
}
public function getDtEnd()
{
return $this->dtEnd;
}
public function setDtStart($dtStart)
{
$this->dtStart = $dtStart;
return $this;
}
public function getDtStart()
{
return $this->dtStart;
}
/**
* @param $dtStamp
*
* @return $this
*/
public function setDtStamp($dtStamp)
{
$this->dtStamp = $dtStamp;
return $this;
}
/**
* @param $duration
*
* @return $this
*/
public function setDuration($duration)
{
$this->duration = $duration;
return $this;
}
/**
* @param string $location
* @param string $title
* @param Geo|string $geo
*
* @return $this
*/
public function setLocation($location, $title = '', $geo = null)
{
if (is_scalar($geo)) {
$geo = Geo::fromString($geo);
} elseif (!is_null($geo) && !$geo instanceof Geo) {
$className = get_class($geo);
throw new \InvalidArgumentException("The parameter 'geo' must be a string or an instance of " . Geo::class . " but an instance of {$className} was given.");
}
$this->location = $location;
$this->locationTitle = $title;
$this->locationGeo = $geo;
return $this;
}
/**
* @return $this
*/
public function setGeoLocation(Geo $geoProperty)
{
$this->locationGeo = $geoProperty;
return $this;
}
/**
* @param $noTime
*
* @return $this
*/
public function setNoTime($noTime)
{
$this->noTime = $noTime;
return $this;
}
/**
* @param $msBusyStatus
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setMsBusyStatus($msBusyStatus)
{
$msBusyStatus = strtoupper($msBusyStatus);
if ($msBusyStatus == self::MS_BUSYSTATUS_FREE
|| $msBusyStatus == self::MS_BUSYSTATUS_TENTATIVE
|| $msBusyStatus == self::MS_BUSYSTATUS_BUSY
|| $msBusyStatus == self::MS_BUSYSTATUS_OOF
) {
$this->msBusyStatus = $msBusyStatus;
} else {
throw new \InvalidArgumentException('Invalid value for status');
}
return $this;
}
/**
* @return string|null
*/
public function getMsBusyStatus()
{
return $this->msBusyStatus;
}
/**
* @param int $sequence
*
* @return $this
*/
public function setSequence($sequence)
{
$this->sequence = $sequence;
return $this;
}
/**
* @return int
*/
public function getSequence()
{
return $this->sequence;
}
/**
* @return $this
*/
public function setOrganizer(Organizer $organizer)
{
$this->organizer = $organizer;
return $this;
}
/**
* @param $summary
*
* @return $this
*/
public function setSummary($summary)
{
$this->summary = $summary;
return $this;
}
/**
* @param $uniqueId
*
* @return $this
*/
public function setUniqueId($uniqueId)
{
$this->uniqueId = $uniqueId;
return $this;
}
/**
* @return string
*/
public function getUniqueId()
{
return $this->uniqueId;
}
/**
* @param $url
*
* @return $this
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @param $useTimezone
*
* @return $this
*/
public function setUseTimezone($useTimezone)
{
$this->useTimezone = $useTimezone;
return $this;
}
/**
* @return bool
*/
public function getUseTimezone()
{
return $this->useTimezone;
}
/**
* @param $timezoneString
*
* @return $this
*/
public function setTimezoneString($timezoneString)
{
$this->timezoneString = $timezoneString;
return $this;
}
/**
* @return bool
*/
public function getTimezoneString()
{
return $this->timezoneString;
}
/**
* @return $this
*/
public function setAttendees(Attendees $attendees)
{
$this->attendees = $attendees;
return $this;
}
/**
* @param string $attendee
* @param array $params
*
* @return $this
*/
public function addAttendee($attendee, $params = [])
{
$this->attendees->add($attendee, $params);
return $this;
}
public function getAttendees(): Attendees
{
return $this->attendees;
}
/**
* @param $description
*
* @return $this
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @param $descriptionHTML
*
* @return $this
*/
public function setDescriptionHTML($descriptionHTML)
{
$this->descriptionHTML = $descriptionHTML;
return $this;
}
/**
* @param bool $useUtc
*
* @return $this
*/
public function setUseUtc($useUtc = true)
{
$this->useUtc = $useUtc;
return $this;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @return string
*/
public function getDescriptionHTML()
{
return $this->descriptionHTML;
}
/**
* @param $status
*
* @return $this
*/
public function setCancelled($status)
{
$this->cancelled = (bool) $status;
return $this;
}
/**
* @param $transparency
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setTimeTransparency($transparency)
{
$transparency = strtoupper($transparency);
if ($transparency === self::TIME_TRANSPARENCY_OPAQUE
|| $transparency === self::TIME_TRANSPARENCY_TRANSPARENT
) {
$this->transparency = $transparency;
} else {
throw new \InvalidArgumentException('Invalid value for transparancy');
}
return $this;
}
/**
* @param $status
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setStatus($status)
{
$status = strtoupper($status);
if ($status == self::STATUS_CANCELLED
|| $status == self::STATUS_CONFIRMED
|| $status == self::STATUS_TENTATIVE
) {
$this->status = $status;
} else {
throw new \InvalidArgumentException('Invalid value for status');
}
return $this;
}
/**
* @deprecated Deprecated since version 0.11.0, to be removed in 1.0. Use addRecurrenceRule instead.
*
* @return $this
*/
public function setRecurrenceRule(RecurrenceRule $recurrenceRule)
{
@trigger_error('setRecurrenceRule() is deprecated since version 0.11.0 and will be removed in 1.0. Use addRecurrenceRule instead.', E_USER_DEPRECATED);
$this->recurrenceRule = $recurrenceRule;
return $this;
}
/**
* @deprecated Deprecated since version 0.11.0, to be removed in 1.0. Use getRecurrenceRules instead.
*
* @return RecurrenceRule
*/
public function getRecurrenceRule()
{
@trigger_error('getRecurrenceRule() is deprecated since version 0.11.0 and will be removed in 1.0. Use getRecurrenceRules instead.', E_USER_DEPRECATED);
return $this->recurrenceRule;
}
/**
* @return $this
*/
public function addRecurrenceRule(RecurrenceRule $recurrenceRule)
{
$this->recurrenceRules[] = $recurrenceRule;
return $this;
}
/**
* @return array
*/
public function getRecurrenceRules()
{
return $this->recurrenceRules;
}
/**
* @param $dtStamp
*
* @return $this
*/
public function setCreated($dtStamp)
{
$this->created = $dtStamp;
return $this;
}
/**
* @param $dtStamp
*
* @return $this
*/
public function setModified($dtStamp)
{
$this->modified = $dtStamp;
return $this;
}
/**
* @param $categories
*
* @return $this
*/
public function setCategories($categories)
{
$this->categories = $categories;
return $this;
}
/**
* Sets the event privacy.
*
* @param bool $flag
*
* @return $this
*/
public function setIsPrivate($flag)
{
$this->isPrivate = (bool) $flag;
return $this;
}
/**
* @return \Eluceo\iCal\Component\Event
*/
public function addExDate(\DateTimeInterface $dateTime)
{
$this->exDates[] = $dateTime;
return $this;
}
/**
* @return \DateTimeInterface[]
*/
public function getExDates()
{
return $this->exDates;
}
/**
* @param \DateTimeInterface[]
*
* @return \Eluceo\iCal\Component\Event
*/
public function setExDates(array $exDates)
{
$this->exDates = $exDates;
return $this;
}
/**
* @return \Eluceo\iCal\Property\Event\RecurrenceId
*/
public function getRecurrenceId()
{
return $this->recurrenceId;
}
/**
* @return \Eluceo\iCal\Component\Event
*/
public function setRecurrenceId(RecurrenceId $recurrenceId)
{
$this->recurrenceId = $recurrenceId;
return $this;
}
/**
* @param array $attachment
*
* @return $this
*/
public function addAttachment(Attachment $attachment)
{
$this->attachments[] = $attachment;
return $this;
}
/**
* @return array
*/
public function getAttachments()
{
return $this->attachments;
}
public function addUrlAttachment(string $url)
{
$this->addAttachment(new Attachment($url));
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of the TIMEZONE component.
*/
class Timezone extends Component
{
/**
* @var string
*/
protected $timezone;
public function __construct($timezone)
{
$this->timezone = $timezone;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'VTIMEZONE';
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
$propertyBag->set('TZID', $this->timezone);
$propertyBag->set('X-LIC-LOCATION', $this->timezone);
return $propertyBag;
}
public function getZoneIdentifier()
{
return $this->timezone;
}
}

View File

@@ -0,0 +1,211 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\Property\Event\RecurrenceRule;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of Standard Time and Daylight Saving Time observances (or rules)
* which define the TIMEZONE component.
*/
class TimezoneRule extends Component
{
const TYPE_DAYLIGHT = 'DAYLIGHT';
const TYPE_STANDARD = 'STANDARD';
/**
* @var string
*/
protected $type;
/**
* @var string
*/
protected $tzOffsetFrom;
/**
* @var string
*/
protected $tzOffsetTo;
/**
* @var string
*/
protected $tzName;
/**
* @var \DateTimeInterface
*/
protected $dtStart;
/**
* @var RecurrenceRule
*/
protected $recurrenceRule;
/**
* create new Timezone Rule object by giving a rule type identifier.
*
* @param string $ruleType one of DAYLIGHT or STANDARD
*
* @throws \InvalidArgumentException
*/
public function __construct($ruleType)
{
$ruleType = strtoupper($ruleType);
if ($ruleType === self::TYPE_DAYLIGHT || $ruleType === self::TYPE_STANDARD) {
$this->type = $ruleType;
} else {
throw new \InvalidArgumentException('Invalid value for timezone rule type');
}
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
if ($this->getTzName()) {
$propertyBag->set('TZNAME', $this->getTzName());
}
if ($this->getTzOffsetFrom()) {
$propertyBag->set('TZOFFSETFROM', $this->getTzOffsetFrom());
}
if ($this->getTzOffsetTo()) {
$propertyBag->set('TZOFFSETTO', $this->getTzOffsetTo());
}
if ($this->getDtStart()) {
$propertyBag->set('DTSTART', $this->getDtStart());
}
if ($this->recurrenceRule) {
$propertyBag->set('RRULE', $this->recurrenceRule);
}
return $propertyBag;
}
/**
* @param $offset
*
* @return $this
*/
public function setTzOffsetFrom($offset)
{
$this->tzOffsetFrom = $offset;
return $this;
}
/**
* @param $offset
*
* @return $this
*/
public function setTzOffsetTo($offset)
{
$this->tzOffsetTo = $offset;
return $this;
}
/**
* @param $name
*
* @return $this
*/
public function setTzName($name)
{
$this->tzName = $name;
return $this;
}
/**
* @return $this
*/
public function setDtStart(\DateTimeInterface $dtStart)
{
$this->dtStart = $dtStart;
return $this;
}
/**
* @return $this
*/
public function setRecurrenceRule(RecurrenceRule $recurrenceRule)
{
$this->recurrenceRule = $recurrenceRule;
return $this;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return $this->type;
}
/**
* @return string
*/
public function getTzOffsetFrom()
{
return $this->tzOffsetFrom;
}
/**
* @return string
*/
public function getTzOffsetTo()
{
return $this->tzOffsetTo;
}
/**
* @return string
*/
public function getTzName()
{
return $this->tzName;
}
/**
* @return RecurrenceRule
*/
public function getRecurrenceRule()
{
return $this->recurrenceRule;
}
/**
* @return mixed return string representation of start date or NULL if no date was given
*/
public function getDtStart()
{
if ($this->dtStart) {
return $this->dtStart->format('Ymd\THis');
}
return;
}
}

19
libs/ical/LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2012-2019 Markus Poerschke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

107
libs/ical/ParameterBag.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
class ParameterBag
{
/**
* The params.
*
* @var array
*/
protected $params;
public function __construct($params = [])
{
$this->params = $params;
}
/**
* @param string $name
* @param mixed $value
*/
public function setParam($name, $value)
{
$this->params[$name] = $value;
}
/**
* @param $name
*
* @return array|mixed
*/
public function getParam($name)
{
if (isset($this->params[$name])) {
return $this->params[$name];
}
return null;
}
/**
* Checks if there are any params.
*/
public function hasParams(): bool
{
return count($this->params) > 0;
}
public function toString(): string
{
$line = '';
foreach ($this->params as $param => $paramValues) {
if (!is_array($paramValues)) {
$paramValues = [$paramValues];
}
foreach ($paramValues as $k => $v) {
$paramValues[$k] = $this->escapeParamValue($v);
}
if ('' != $line) {
$line .= ';';
}
$line .= $param . '=' . implode(',', $paramValues);
}
return $line;
}
/**
* Returns an escaped string for a param value.
*
* @param string $value
*
* @return string
*/
private function escapeParamValue($value)
{
$count = 0;
$value = str_replace('\\', '\\\\', $value);
$value = str_replace('"', '\"', $value, $count);
$value = str_replace("\n", '\\n', $value);
if (false !== strpos($value, ';') || false !== strpos($value, ',') || false !== strpos($value, ':') || $count) {
$value = '"' . $value . '"';
}
return $value;
}
/**
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

147
libs/ical/Property.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
use Eluceo\iCal\Property\ArrayValue;
use Eluceo\iCal\Property\StringValue;
use Eluceo\iCal\Property\ValueInterface;
/**
* The Property Class represents a property as defined in RFC 5545.
*
* The content of a line (unfolded) will be rendered in this class.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.5
*/
class Property
{
/**
* The value of the Property.
*
* @var ValueInterface
*/
protected $value;
/**
* The params of the Property.
*
* @var ParameterBag
*/
protected $parameterBag;
/**
* @var string
*/
protected $name;
/**
* @param $name
* @param $value
* @param array $params
*/
public function __construct($name, $value, $params = [])
{
$this->name = $name;
$this->setValue($value);
$this->parameterBag = new ParameterBag($params);
}
/**
* Renders an unfolded line.
*
* @return string
*/
public function toLine()
{
// Property-name
$line = $this->getName();
// Adding params
//@todo added check for $this->parameterBag because doctrine/orm proxies won't execute constructor - ok?
if ($this->parameterBag && $this->parameterBag->hasParams()) {
$line .= ';' . $this->parameterBag->toString();
}
// Property value
$line .= ':' . $this->value->getEscapedValue();
return $line;
}
/**
* Get all unfolded lines.
*
* @return array
*/
public function toLines()
{
return [$this->toLine()];
}
/**
* @param string $name
* @param mixed $value
*
* @return $this
*/
public function setParam($name, $value)
{
$this->parameterBag->setParam($name, $value);
return $this;
}
/**
* @param $name
*/
public function getParam($name)
{
return $this->parameterBag->getParam($name);
}
/**
* @param mixed $value
*
* @return $this
*
* @throws \Exception
*/
public function setValue($value)
{
if (is_scalar($value)) {
$this->value = new StringValue($value);
} elseif (is_array($value)) {
$this->value = new ArrayValue($value);
} else {
if (!$value instanceof ValueInterface) {
throw new \Exception('The value must implement the ValueInterface.');
} else {
$this->value = $value;
}
}
return $this;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
public function getName(): string
{
return $this->name;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
class ArrayValue implements ValueInterface
{
/**
* The value.
*
* @var array
*/
protected $values;
public function __construct(array $values)
{
$this->values = $values;
}
public function setValues(array $values)
{
$this->values = $values;
return $this;
}
public function getEscapedValue(): string
{
return implode(',', array_map(function (string $value): string {
return (new StringValue($value))->getEscapedValue();
}, $this->values));
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
use Eluceo\iCal\Property;
use Eluceo\iCal\Util\DateUtil;
class DateTimeProperty extends Property
{
/**
* @param string $name
* @param \DateTimeInterface $dateTime
* @param bool $noTime
* @param bool $useTimezone
* @param bool $useUtc
* @param string $timezoneString
*/
public function __construct(
$name,
\DateTimeInterface $dateTime = null,
$noTime = false,
$useTimezone = false,
$useUtc = false,
$timezoneString = ''
) {
$dateString = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc);
$params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone, $timezoneString);
parent::__construct($name, $dateString, $params);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
use Eluceo\iCal\Property;
use Eluceo\iCal\Util\DateUtil;
class DateTimesProperty extends Property
{
/**
* @param string $name
* @param \DateTimeInterface[] $dateTimes
* @param bool $noTime
* @param bool $useTimezone
* @param bool $useUtc
* @param string $timezoneString
*/
public function __construct(
$name,
$dateTimes = [],
$noTime = false,
$useTimezone = false,
$useUtc = false,
$timezoneString = ''
) {
$dates = [];
$dateTime = new \DateTimeImmutable();
foreach ($dateTimes as $dateTime) {
$dates[] = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc);
}
//@todo stop this triggering an E_NOTICE when $dateTimes is empty
$params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone, $timezoneString);
parent::__construct($name, $dates, $params);
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
/**
* Class Attachment.
*/
class Attachment extends Property
{
/**
* @param string $value
* @param array $params
*/
public function __construct($value, $params = [])
{
parent::__construct('ATTACH', $value, $params);
}
/**
* @param $url
*
* @throws \Exception
*/
public function setUrl($url)
{
$this->setValue($url);
}
}

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
class Attendees extends Property
{
/**
* @var Property[]
*/
protected $attendees = [];
public function __construct()
{
$this->name = 'ATTENDEES';
// prevent super constructor to be called
}
/**
* @param $value
* @param array $params
*
* @return $this
*/
public function add($value, $params = [])
{
$this->attendees[] = new Property('ATTENDEE', $value, $params);
return $this;
}
/**
* @param Property[] $value
*
* @return $this
*/
public function setValue($value)
{
$this->attendees = $value;
return $this;
}
/**
* @return Property[]
*/
public function getValue()
{
return $this->attendees;
}
/**
* {@inheritdoc}
*/
public function toLines()
{
$lines = [];
foreach ($this->attendees as $attendee) {
$lines[] = $attendee->toLine();
}
return $lines;
}
/**
* @param string $name
* @param mixed $value
*
* @throws \BadMethodCallException
*/
public function setParam($name, $value)
{
throw new \BadMethodCallException('Cannot call setParam on Attendees Property');
}
/**
* @param $name
*
* @throws \BadMethodCallException
*/
public function getParam($name)
{
throw new \BadMethodCallException('Cannot call getParam on Attendees Property');
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
/**
* GEO property.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.1.6
*/
class Geo extends Property
{
/**
* @var float
*/
private $latitude;
/**
* @var float
*/
private $longitude;
public function __construct(float $latitude, float $longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
if ($this->latitude < -90 || $this->latitude > 90) {
throw new \InvalidArgumentException("The geographical latitude must be a value between -90 and 90 degrees. '{$this->latitude}' was given.");
}
if ($this->longitude < -180 || $this->longitude > 180) {
throw new \InvalidArgumentException("The geographical longitude must be a value between -180 and 180 degrees. '{$this->longitude}' was given.");
}
parent::__construct('GEO', new Property\RawStringValue($this->getGeoLocationAsString()));
}
/**
* @deprecated This method is used to allow backwards compatibility for Event::setLocation
*
* @return Geo
*/
public static function fromString(string $geoLocationString): self
{
$geoLocationString = str_replace(',', ';', $geoLocationString);
$geoLocationString = str_replace('GEO:', '', $geoLocationString);
$parts = explode(';', $geoLocationString);
return new static((float) $parts[0], (float) $parts[1]);
}
/**
* Returns the coordinates as a string.
*
* @example 37.386013;-122.082932
*/
public function getGeoLocationAsString(string $separator = ';'): string
{
return number_format($this->latitude, 6) . $separator . number_format($this->longitude, 6);
}
public function getLatitude(): float
{
return $this->latitude;
}
public function getLongitude(): float
{
return $this->longitude;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
/**
* Class Organizer.
*/
class Organizer extends Property
{
/**
* @param string $value
* @param array $params
*/
public function __construct($value, $params = [])
{
parent::__construct('ORGANIZER', $value, $params);
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\ParameterBag;
use Eluceo\iCal\Property;
use Eluceo\iCal\Property\ValueInterface;
use Eluceo\iCal\Util\DateUtil;
/**
* Implementation of Recurrence Id.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.4.4
*/
class RecurrenceId extends Property
{
/**
* The effective range of recurrence instances from the instance
* specified by the recurrence identifier specified by the property.
*/
const RANGE_THISANDPRIOR = 'THISANDPRIOR';
const RANGE_THISANDFUTURE = 'THISANDFUTURE';
/**
* The dateTime to identify a particular instance of a recurring event which is getting modified.
*
* @var \DateTimeInterface
*/
protected $dateTime;
/**
* Specify the effective range of recurrence instances from the instance.
*
* @var string
*/
protected $range;
public function __construct(\DateTimeInterface $dateTime = null)
{
$this->name = 'RECURRENCE-ID';
$this->parameterBag = new ParameterBag();
if (isset($dateTime)) {
$this->dateTime = $dateTime;
}
}
public function applyTimeSettings($noTime = false, $useTimezone = false, $useUtc = false, $timezoneString = '')
{
$params = DateUtil::getDefaultParams($this->dateTime, $noTime, $useTimezone, $timezoneString);
foreach ($params as $name => $value) {
$this->parameterBag->setParam($name, $value);
}
if ($this->range) {
$this->parameterBag->setParam('RANGE', $this->range);
}
$this->setValue(DateUtil::getDateString($this->dateTime, $noTime, $useTimezone, $useUtc));
}
/**
* @return \DateTimeInterface
*/
public function getDatetime()
{
return $this->dateTime;
}
/**
* @return \Eluceo\iCal\Property\Event\RecurrenceId
*/
public function setDatetime(\DateTimeInterface $dateTime)
{
$this->dateTime = $dateTime;
return $this;
}
/**
* @return string
*/
public function getRange()
{
return $this->range;
}
/**
* @param string $range
*
* @return \Eluceo\iCal\Property\Event\RecurrenceId
*/
public function setRange($range)
{
$this->range = $range;
return $this;
}
/**
* Get all unfolded lines.
*
* @return array
*/
public function toLines()
{
if (!$this->value instanceof ValueInterface) {
throw new \Exception('The value must implement the ValueInterface. Call RecurrenceId::applyTimeSettings() before adding RecurrenceId.');
} else {
return parent::toLines();
}
}
}

View File

@@ -0,0 +1,546 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\ParameterBag;
use Eluceo\iCal\Property\ValueInterface;
use InvalidArgumentException;
/**
* Implementation of Recurrence Rule.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.5.3
*/
class RecurrenceRule implements ValueInterface
{
const FREQ_YEARLY = 'YEARLY';
const FREQ_MONTHLY = 'MONTHLY';
const FREQ_WEEKLY = 'WEEKLY';
const FREQ_DAILY = 'DAILY';
const FREQ_HOURLY = 'HOURLY';
const FREQ_MINUTELY = 'MINUTELY';
const FREQ_SECONDLY = 'SECONDLY';
const WEEKDAY_SUNDAY = 'SU';
const WEEKDAY_MONDAY = 'MO';
const WEEKDAY_TUESDAY = 'TU';
const WEEKDAY_WEDNESDAY = 'WE';
const WEEKDAY_THURSDAY = 'TH';
const WEEKDAY_FRIDAY = 'FR';
const WEEKDAY_SATURDAY = 'SA';
/**
* The frequency of an Event.
*
* @var string
*/
protected $freq = self::FREQ_YEARLY;
/**
* BYSETPOS must require use of other BY*.
*
* @var bool
*/
protected $canUseBySetPos = false;
/**
* @var int|null
*/
protected $interval = 1;
/**
* @var int|null
*/
protected $count = null;
/**
* @var \DateTimeInterface|null
*/
protected $until = null;
/**
* @var string|null
*/
protected $wkst;
/**
* @var array|null
*/
protected $bySetPos = null;
/**
* @var string|null
*/
protected $byMonth;
/**
* @var string|null
*/
protected $byWeekNo;
/**
* @var string|null
*/
protected $byYearDay;
/**
* @var string|null
*/
protected $byMonthDay;
/**
* @var string|null
*/
protected $byDay;
/**
* @var string|null
*/
protected $byHour;
/**
* @var string|null
*/
protected $byMinute;
/**
* @var string|null
*/
protected $bySecond;
public function getEscapedValue(): string
{
return $this->buildParameterBag()->toString();
}
/**
* @return ParameterBag
*/
protected function buildParameterBag()
{
$parameterBag = new ParameterBag();
$parameterBag->setParam('FREQ', $this->freq);
if (null !== $this->interval) {
$parameterBag->setParam('INTERVAL', $this->interval);
}
if (null !== $this->count) {
$parameterBag->setParam('COUNT', $this->count);
}
if (null != $this->until) {
$parameterBag->setParam('UNTIL', $this->until->format('Ymd\THis\Z'));
}
if (null !== $this->wkst) {
$parameterBag->setParam('WKST', $this->wkst);
}
if (null !== $this->bySetPos && $this->canUseBySetPos) {
$parameterBag->setParam('BYSETPOS', $this->bySetPos);
}
if (null !== $this->byMonth) {
$parameterBag->setParam('BYMONTH', explode(',', $this->byMonth));
}
if (null !== $this->byWeekNo) {
$parameterBag->setParam('BYWEEKNO', explode(',', $this->byWeekNo));
}
if (null !== $this->byYearDay) {
$parameterBag->setParam('BYYEARDAY', explode(',', $this->byYearDay));
}
if (null !== $this->byMonthDay) {
$parameterBag->setParam('BYMONTHDAY', explode(',', $this->byMonthDay));
}
if (null !== $this->byDay) {
$parameterBag->setParam('BYDAY', explode(',', $this->byDay));
}
if (null !== $this->byHour) {
$parameterBag->setParam('BYHOUR', explode(',', $this->byHour));
}
if (null !== $this->byMinute) {
$parameterBag->setParam('BYMINUTE', explode(',', $this->byMinute));
}
if (null !== $this->bySecond) {
$parameterBag->setParam('BYSECOND', explode(',', $this->bySecond));
}
return $parameterBag;
}
/**
* @param int|null $count
*
* @return $this
*/
public function setCount($count)
{
$this->count = $count;
return $this;
}
/**
* @return int|null
*/
public function getCount()
{
return $this->count;
}
/**
* @return $this
*/
public function setUntil(\DateTimeInterface $until = null)
{
$this->until = $until;
return $this;
}
/**
* @return \DateTimeInterface|null
*/
public function getUntil()
{
return $this->until;
}
/**
* The FREQ rule part identifies the type of recurrence rule. This
* rule part MUST be specified in the recurrence rule. Valid values
* include.
*
* SECONDLY, to specify repeating events based on an interval of a second or more;
* MINUTELY, to specify repeating events based on an interval of a minute or more;
* HOURLY, to specify repeating events based on an interval of an hour or more;
* DAILY, to specify repeating events based on an interval of a day or more;
* WEEKLY, to specify repeating events based on an interval of a week or more;
* MONTHLY, to specify repeating events based on an interval of a month or more;
* YEARLY, to specify repeating events based on an interval of a year or more.
*
* @param string $freq
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setFreq($freq)
{
if (@constant('static::FREQ_' . $freq) !== null) {
$this->freq = $freq;
} else {
throw new \InvalidArgumentException("The Frequency {$freq} is not supported.");
}
return $this;
}
/**
* @return string
*/
public function getFreq()
{
return $this->freq;
}
/**
* The INTERVAL rule part contains a positive integer representing at
* which intervals the recurrence rule repeats.
*
* @param int|null $interval
*
* @return $this
*/
public function setInterval($interval)
{
$this->interval = $interval;
return $this;
}
/**
* @return int|null
*/
public function getInterval()
{
return $this->interval;
}
/**
* The WKST rule part specifies the day on which the workweek starts.
* Valid values are MO, TU, WE, TH, FR, SA, and SU.
*
* @param string $value
*
* @return $this
*/
public function setWkst($value)
{
$this->wkst = $value;
return $this;
}
/**
* The BYSETPOS filters one interval of events by the specified position.
* A positive position will start from the beginning and go forward while
* a negative position will start at the end and move backward.
*
* Valid values are a comma separated string or an array of integers
* from 1 to 366 or negative integers from -1 to -366.
*
* @param int|string|array|null $value
*
* @throws InvalidArgumentException
*
* @return $this
*/
public function setBySetPos($value)
{
if (null === $value) {
$this->bySetPos = $value;
return $this;
}
if (!(is_string($value) || is_array($value) || is_int($value))) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$list = $value;
if (is_int($value)) {
if ($value === 0 || $value < -366 || $value > 366) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$this->bySetPos = [$value];
return $this;
}
if (is_string($value)) {
$list = explode(',', $value);
}
$output = [];
foreach ($list as $item) {
if (is_string($item)) {
if (!preg_match('/^ *-?[0-9]* *$/', $item)) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$item = intval($item);
}
if (!is_int($item) || $item === 0 || $item < -366 || $item > 366) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$output[] = $item;
}
$this->bySetPos = $output;
return $this;
}
/**
* The BYMONTH rule part specifies a COMMA-separated list of months of the year.
* Valid values are 1 to 12.
*
* @param int $month
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setByMonth($month)
{
if (!is_integer($month) || $month <= 0 || $month > 12) {
throw new InvalidArgumentException('Invalid value for BYMONTH');
}
$this->byMonth = $month;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYWEEKNO rule part specifies a COMMA-separated list of ordinals specifying weeks of the year.
* Valid values are 1 to 53 or -53 to -1.
*
* @param int $value
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setByWeekNo($value)
{
if (!is_integer($value) || $value > 53 || $value < -53 || $value === 0) {
throw new InvalidArgumentException('Invalid value for BYWEEKNO');
}
$this->byWeekNo = $value;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYYEARDAY rule part specifies a COMMA-separated list of days of the year.
* Valid values are 1 to 366 or -366 to -1.
*
* @param int $day
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setByYearDay($day)
{
if (!is_integer($day) || $day > 366 || $day < -366 || $day === 0) {
throw new InvalidArgumentException('Invalid value for BYYEARDAY');
}
$this->byYearDay = $day;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYMONTHDAY rule part specifies a COMMA-separated list of days of the month.
* Valid values are 1 to 31 or -31 to -1.
*
* @param int $day
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setByMonthDay($day)
{
if (!is_integer($day) || $day > 31 || $day < -31 || $day === 0) {
throw new InvalidArgumentException('Invalid value for BYMONTHDAY');
}
$this->byMonthDay = $day;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYDAY rule part specifies a COMMA-separated list of days of the week;.
*
* SU indicates Sunday; MO indicates Monday; TU indicates Tuesday;
* WE indicates Wednesday; TH indicates Thursday; FR indicates Friday; and SA indicates Saturday.
*
* Each BYDAY value can also be preceded by a positive (+n) or negative (-n) integer.
* If present, this indicates the nth occurrence of a specific day within the MONTHLY or YEARLY "RRULE".
*
* @return $this
*/
public function setByDay(string $day)
{
$this->byDay = $day;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYHOUR rule part specifies a COMMA-separated list of hours of the day.
* Valid values are 0 to 23.
*
* @param int $value
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setByHour($value)
{
if (!is_integer($value) || $value < 0 || $value > 23) {
throw new \InvalidArgumentException('Invalid value for BYHOUR');
}
$this->byHour = $value;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYMINUTE rule part specifies a COMMA-separated list of minutes within an hour.
* Valid values are 0 to 59.
*
* @param int $value
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setByMinute($value)
{
if (!is_integer($value) || $value < 0 || $value > 59) {
throw new \InvalidArgumentException('Invalid value for BYMINUTE');
}
$this->byMinute = $value;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYSECOND rule part specifies a COMMA-separated list of seconds within a minute.
* Valid values are 0 to 60.
*
* @param int $value
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setBySecond($value)
{
if (!is_integer($value) || $value < 0 || $value > 60) {
throw new \InvalidArgumentException('Invalid value for BYSECOND');
}
$this->bySecond = $value;
$this->canUseBySetPos = true;
return $this;
}
}

View File

@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
class RawStringValue extends StringValue
{
public function getEscapedValue(): string
{
return $this->getValue();
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
class StringValue implements ValueInterface
{
/**
* The value.
*
* @var string
*/
protected $value;
public function __construct($value)
{
$this->value = $value;
}
public function getEscapedValue(): string
{
$value = $this->value;
$value = str_replace('\\', '\\\\', $value);
$value = str_replace('"', '\\"', $value);
$value = str_replace(',', '\\,', $value);
$value = str_replace(';', '\\;', $value);
$value = str_replace("\n", '\\n', $value);
$value = str_replace([
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", /* \n*/ "\x0B", "\x0C", "\x0D", "\x0E", "\x0F",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F",
"\x7F",
], '', $value);
return $value;
}
/**
* @param string $value
*
* @return $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
interface ValueInterface
{
/**
* Return the value of the Property as an escaped string.
*
* Escape values as per RFC 5545.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.3.11
*/
public function getEscapedValue(): string;
}

74
libs/ical/PropertyBag.php Normal file
View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
class PropertyBag implements \IteratorAggregate
{
/**
* @var array
*/
protected $elements = [];
/**
* Creates a new Property with $name, $value and $params.
*
* @param $name
* @param $value
* @param array $params
*
* @return $this
*/
public function set($name, $value, $params = [])
{
$this->add(new Property($name, $value, $params));
return $this;
}
/**
* @return Property|null
*/
public function get(string $name)
{
if (isset($this->elements[$name])) {
return $this->elements[$name];
}
return null;
}
/**
* Adds a Property. If Property already exists an Exception will be thrown.
*
* @return $this
*
* @throws \Exception
*/
public function add(Property $property)
{
$name = $property->getName();
if (isset($this->elements[$name])) {
throw new \Exception("Property with name '{$name}' already exists");
}
$this->elements[$name] = $property;
return $this;
}
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayObject($this->elements);
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Util;
class ComponentUtil
{
/**
* Folds a single line.
*
* According to RFC 5545, all lines longer than 75 characters should be folded
*
* @see https://tools.ietf.org/html/rfc5545#section-5
* @see https://tools.ietf.org/html/rfc5545#section-3.1
*
* @param string $string
*
* @return array
*/
public static function fold($string)
{
$lines = [];
if (function_exists('mb_strcut')) {
while (strlen($string) > 0) {
if (strlen($string) > 75) {
$lines[] = mb_strcut($string, 0, 75, 'utf-8');
$string = ' ' . mb_strcut($string, 75, strlen($string), 'utf-8');
} else {
$lines[] = $string;
$string = '';
break;
}
}
} else {
$array = preg_split('/(?<!^)(?!$)/u', $string);
$line = '';
$lineNo = 0;
foreach ($array as $char) {
$charLen = strlen($char);
$lineLen = strlen($line);
if ($lineLen + $charLen > 75) {
$line = ' ' . $char;
++$lineNo;
} else {
$line .= $char;
}
$lines[$lineNo] = $line;
}
}
return $lines;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Util;
class DateUtil
{
public static function getDefaultParams(\DateTimeInterface $dateTime = null, $noTime = false, $useTimezone = false, $timezoneString = '')
{
$params = [];
if ($useTimezone && $noTime === false) {
$timeZone = $timezoneString === '' ? $dateTime->getTimezone()->getName() : $timezoneString;
$params['TZID'] = $timeZone;
}
if ($noTime) {
$params['VALUE'] = 'DATE';
}
return $params;
}
/**
* Returns a formatted date string.
*
* @param \DateTimeInterface|null $dateTime The DateTime object
* @param bool $noTime Indicates if the time will be added
* @param bool $useTimezone
* @param bool $useUtc
*
* @return mixed
*/
public static function getDateString(\DateTimeInterface $dateTime = null, $noTime = false, $useTimezone = false, $useUtc = false)
{
if (empty($dateTime)) {
$dateTime = new \DateTimeImmutable();
}
// Only convert the DateTime to UTC if there is a time present. For date-only the
// timezone is meaningless and converting it might shift it to the wrong date.
// Do not convert DateTime to UTC if a timezone it specified, as it should be local time.
if (!$noTime && $useUtc && !$useTimezone) {
$dateTime = clone $dateTime;
$dateTime = $dateTime->setTimezone(new \DateTimeZone('UTC'));
}
return $dateTime->format(self::getDateFormat($noTime, $useTimezone, $useUtc));
}
/**
* Returns the date format that can be passed to DateTime::format().
*
* @param bool $noTime Indicates if the time will be added
* @param bool $useTimezone
* @param bool $useUtc
*
* @return string
*/
public static function getDateFormat($noTime = false, $useTimezone = false, $useUtc = false)
{
// Do not use UTC time (Z) if timezone support is enabled.
if ($useTimezone || !$useUtc) {
return $noTime ? 'Ymd' : 'Ymd\THis';
}
return $noTime ? 'Ymd' : 'Ymd\THis\Z';
}
}

19
libs/swiftmailer/LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2013-2016 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,80 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* General utility class in Swift Mailer, not to be instantiated.
*
*
* @author Chris Corbyn
*/
abstract class Swift
{
/** Swift Mailer Version number generated during dist release process */
const VERSION = '@SWIFT_VERSION_NUMBER@';
public static $initialized = false;
public static $inits = array();
/**
* Registers an initializer callable that will be called the first time
* a SwiftMailer class is autoloaded.
*
* This enables you to tweak the default configuration in a lazy way.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function init($callable)
{
self::$inits[] = $callable;
}
/**
* Internal autoloader for spl_autoload_register().
*
* @param string $class
*/
public static function autoload($class)
{
// Don't interfere with other autoloaders
if (0 !== strpos($class, 'Swift_')) {
return;
}
$path = __DIR__.'/'.str_replace('_', '/', $class).'.php';
if (!file_exists($path)) {
return;
}
require $path;
if (self::$inits && !self::$initialized) {
self::$initialized = true;
foreach (self::$inits as $init) {
call_user_func($init);
}
}
}
/**
* Configure autoloading using Swift Mailer.
*
* This is designed to play nicely with other autoloaders.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function registerAutoload($callable = null)
{
if (null !== $callable) {
self::$inits[] = $callable;
}
spl_autoload_register(array('Swift', 'autoload'));
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Attachment class for attaching files to a {@link Swift_Mime_Message}.
*
* @author Chris Corbyn
*/
class Swift_Attachment extends Swift_Mime_Attachment
{
/**
* Create a new Attachment.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
call_user_func_array(
array($this, 'Swift_Mime_Attachment::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.attachment')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new Attachment.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return Swift_Mime_Attachment
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new Attachment from a filesystem path.
*
* @param string $path
* @param string $contentType optional
*
* @return Swift_Mime_Attachment
*/
public static function fromPath($path, $contentType = null)
{
return self::newInstance()->setFile(
new Swift_ByteStream_FileByteStream($path),
$contentType
);
}
}

View File

@@ -0,0 +1,181 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides the base functionality for an InputStream supporting filters.
*
* @author Chris Corbyn
*/
abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_InputByteStream, Swift_Filterable
{
/**
* Write sequence.
*/
protected $_sequence = 0;
/**
* StreamFilters.
*
* @var Swift_StreamFilter[]
*/
private $_filters = array();
/**
* A buffer for writing.
*/
private $_writeBuffer = '';
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $_mirrors = array();
/**
* Commit the given bytes to the storage medium immediately.
*
* @param string $bytes
*/
abstract protected function _commit($bytes);
/**
* Flush any buffers/content with immediate effect.
*/
abstract protected function _flush();
/**
* Add a StreamFilter to this InputByteStream.
*
* @param Swift_StreamFilter $filter
* @param string $key
*/
public function addFilter(Swift_StreamFilter $filter, $key)
{
$this->_filters[$key] = $filter;
}
/**
* Remove an already present StreamFilter based on its $key.
*
* @param string $key
*/
public function removeFilter($key)
{
unset($this->_filters[$key]);
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*
* @throws Swift_IoException
*
* @return int
*/
public function write($bytes)
{
$this->_writeBuffer .= $bytes;
foreach ($this->_filters as $filter) {
if ($filter->shouldBuffer($this->_writeBuffer)) {
return;
}
}
$this->_doWrite($this->_writeBuffer);
return ++$this->_sequence;
}
/**
* For any bytes that are currently buffered inside the stream, force them
* off the buffer.
*
* @throws Swift_IoException
*/
public function commit()
{
$this->_doWrite($this->_writeBuffer);
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is)
{
$this->_mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->_mirrors as $k => $stream) {
if ($is === $stream) {
if ($this->_writeBuffer !== '') {
$stream->write($this->_writeBuffer);
}
unset($this->_mirrors[$k]);
}
}
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*
* @throws Swift_IoException
*/
public function flushBuffers()
{
if ($this->_writeBuffer !== '') {
$this->_doWrite($this->_writeBuffer);
}
$this->_flush();
foreach ($this->_mirrors as $stream) {
$stream->flushBuffers();
}
}
/** Run $bytes through all filters */
private function _filter($bytes)
{
foreach ($this->_filters as $filter) {
$bytes = $filter->filter($bytes);
}
return $bytes;
}
/** Just write the bytes to the stream */
private function _doWrite($bytes)
{
$this->_commit($this->_filter($bytes));
foreach ($this->_mirrors as $stream) {
$stream->write($bytes);
}
$this->_writeBuffer = '';
}
}

View File

@@ -0,0 +1,182 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from an array.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_OutputByteStream
{
/**
* The internal stack of bytes.
*
* @var string[]
*/
private $_array = array();
/**
* The size of the stack.
*
* @var int
*/
private $_arraySize = 0;
/**
* The internal pointer offset.
*
* @var int
*/
private $_offset = 0;
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $_mirrors = array();
/**
* Create a new ArrayByteStream.
*
* If $stack is given the stream will be populated with the bytes it contains.
*
* @param mixed $stack of bytes in string or array form, optional
*/
public function __construct($stack = null)
{
if (is_array($stack)) {
$this->_array = $stack;
$this->_arraySize = count($stack);
} elseif (is_string($stack)) {
$this->write($stack);
} else {
$this->_array = array();
}
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_offset == $this->_arraySize) {
return false;
}
// Don't use array slice
$end = $length + $this->_offset;
$end = $this->_arraySize < $end ? $this->_arraySize : $end;
$ret = '';
for (; $this->_offset < $end; ++$this->_offset) {
$ret .= $this->_array[$this->_offset];
}
return $ret;
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*/
public function write($bytes)
{
$to_add = str_split($bytes);
foreach ($to_add as $value) {
$this->_array[] = $value;
}
$this->_arraySize = count($this->_array);
foreach ($this->_mirrors as $stream) {
$stream->write($bytes);
}
}
/**
* Not used.
*/
public function commit()
{
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is)
{
$this->_mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->_mirrors as $k => $stream) {
if ($is === $stream) {
unset($this->_mirrors[$k]);
}
}
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if ($byteOffset > $this->_arraySize) {
$byteOffset = $this->_arraySize;
} elseif ($byteOffset < 0) {
$byteOffset = 0;
}
$this->_offset = $byteOffset;
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*/
public function flushBuffers()
{
$this->_offset = 0;
$this->_array = array();
$this->_arraySize = 0;
foreach ($this->_mirrors as $stream) {
$stream->flushBuffers();
}
}
}

View File

@@ -0,0 +1,231 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from a file.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream
{
/** The internal pointer offset */
private $_offset = 0;
/** The path to the file */
private $_path;
/** The mode this file is opened in for writing */
private $_mode;
/** A lazy-loaded resource handle for reading the file */
private $_reader;
/** A lazy-loaded resource handle for writing the file */
private $_writer;
/** If magic_quotes_runtime is on, this will be true */
private $_quotes = false;
/** If stream is seekable true/false, or null if not known */
private $_seekable = null;
/**
* Create a new FileByteStream for $path.
*
* @param string $path
* @param bool $writable if true
*/
public function __construct($path, $writable = false)
{
if (empty($path)) {
throw new Swift_IoException('The path cannot be empty');
}
$this->_path = $path;
$this->_mode = $writable ? 'w+b' : 'rb';
if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
$this->_quotes = true;
}
}
/**
* Get the complete path to the file.
*
* @return string
*/
public function getPath()
{
return $this->_path;
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @throws Swift_IoException
*
* @return string|bool
*/
public function read($length)
{
$fp = $this->_getReadHandle();
if (!feof($fp)) {
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 0);
}
$bytes = fread($fp, $length);
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 1);
}
$this->_offset = ftell($fp);
// If we read one byte after reaching the end of the file
// feof() will return false and an empty string is returned
if ($bytes === '' && feof($fp)) {
$this->_resetReadHandle();
return false;
}
return $bytes;
}
$this->_resetReadHandle();
return false;
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if (isset($this->_reader)) {
$this->_seekReadStreamToPosition($byteOffset);
}
$this->_offset = $byteOffset;
}
/** Just write the bytes to the file */
protected function _commit($bytes)
{
fwrite($this->_getWriteHandle(), $bytes);
$this->_resetReadHandle();
}
/** Not used */
protected function _flush()
{
}
/** Get the resource for reading */
private function _getReadHandle()
{
if (!isset($this->_reader)) {
$pointer = @fopen($this->_path, 'rb');
if (!$pointer) {
throw new Swift_IoException(
'Unable to open file for reading ['.$this->_path.']'
);
}
$this->_reader = $pointer;
if ($this->_offset != 0) {
$this->_getReadStreamSeekableStatus();
$this->_seekReadStreamToPosition($this->_offset);
}
}
return $this->_reader;
}
/** Get the resource for writing */
private function _getWriteHandle()
{
if (!isset($this->_writer)) {
if (!$this->_writer = fopen($this->_path, $this->_mode)) {
throw new Swift_IoException(
'Unable to open file for writing ['.$this->_path.']'
);
}
}
return $this->_writer;
}
/** Force a reload of the resource for reading */
private function _resetReadHandle()
{
if (isset($this->_reader)) {
fclose($this->_reader);
$this->_reader = null;
}
}
/** Check if ReadOnly Stream is seekable */
private function _getReadStreamSeekableStatus()
{
$metas = stream_get_meta_data($this->_reader);
$this->_seekable = $metas['seekable'];
}
/** Streams in a readOnly stream ensuring copy if needed */
private function _seekReadStreamToPosition($offset)
{
if ($this->_seekable === null) {
$this->_getReadStreamSeekableStatus();
}
if ($this->_seekable === false) {
$currentPos = ftell($this->_reader);
if ($currentPos < $offset) {
$toDiscard = $offset - $currentPos;
fread($this->_reader, $toDiscard);
return;
}
$this->_copyReadStream();
}
fseek($this->_reader, $offset, SEEK_SET);
}
/** Copy a readOnly Stream to ensure seekability */
private function _copyReadStream()
{
if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) {
/* We have opened a php:// Stream Should work without problem */
} elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) {
/* We have opened a tmpfile */
} else {
throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
}
$currentPos = ftell($this->_reader);
fclose($this->_reader);
$source = fopen($this->_path, 'rb');
if (!$source) {
throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']');
}
fseek($tmpFile, 0, SEEK_SET);
while (!feof($source)) {
fwrite($tmpFile, fread($source, 4096));
}
fseek($tmpFile, $currentPos, SEEK_SET);
fclose($source);
$this->_reader = $tmpFile;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @author Romain-Geissler
*/
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream
{
public function __construct()
{
$filePath = tempnam(sys_get_temp_dir(), 'FileByteStream');
if ($filePath === false) {
throw new Swift_IoException('Failed to retrieve temporary file name.');
}
parent::__construct($filePath, true);
}
public function getContent()
{
if (($content = file_get_contents($this->getPath())) === false) {
throw new Swift_IoException('Failed to get temporary file content.');
}
return $content;
}
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes characters for a specific character set.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
interface Swift_CharacterReader
{
const MAP_TYPE_INVALID = 0x01;
const MAP_TYPE_FIXED_LEN = 0x02;
const MAP_TYPE_POSITIONS = 0x03;
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars);
/**
* Returns the mapType, see constants.
*
* @return int
*/
public function getMapType();
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param int[] $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size);
/**
* Returns the number of bytes which should be read to start each character.
*
* For fixed width character sets this should be the number of octets-per-character.
* For multibyte character sets this will probably be 1.
*
* @return int
*/
public function getInitialByteSize();
}

View File

@@ -0,0 +1,97 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides fixed-width byte sizes for reading fixed-width character sets.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader
{
/**
* The number of bytes in a single character.
*
* @var int
*/
private $_width;
/**
* Creates a new GenericFixedWidthReader using $width bytes per character.
*
* @param int $width
*/
public function __construct($width)
{
$this->_width = $width;
}
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = strlen($string);
// % and / are CPU intensive, so, maybe find a better way
$ignored = $strlen % $this->_width;
$ignoredChars = $ignored ? substr($string, -$ignored) : '';
$currentMap = $this->_width;
return ($strlen - $ignored) / $this->_width;
}
/**
* Returns the mapType.
*
* @return int
*/
public function getMapType()
{
return self::MAP_TYPE_FIXED_LEN;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$needed = $this->_width - $size;
return $needed > -1 ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return $this->_width;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes US-ASCII characters.
*
* @author Chris Corbyn
*/
class Swift_CharacterReader_UsAsciiReader implements Swift_CharacterReader
{
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param string $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = strlen($string);
$ignoredChars = '';
for ($i = 0; $i < $strlen; ++$i) {
if ($string[$i] > "\x07F") {
// Invalid char
$currentMap[$i + $startOffset] = $string[$i];
}
}
return $strlen;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_INVALID;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$byte = reset($bytes);
if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) {
return 0;
}
return -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}

View File

@@ -0,0 +1,176 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes UTF-8 characters.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader
{
/** Pre-computed for optimization */
private static $length_map = array(
// N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x2N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x4N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x5N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x6N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x7N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x8N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x9N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xAN
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xBN
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xCN
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xDN
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEN
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // 0xFN
);
private static $s_length_map = array(
"\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1,
"\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1,
"\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1,
"\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1,
"\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1,
"\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1,
"\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1,
"\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1,
"\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1,
"\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1,
"\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1,
"\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1,
"\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1,
"\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1,
"\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1,
"\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1,
"\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0,
"\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0,
"\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0,
"\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0,
"\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0,
"\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0,
"\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0,
"\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0,
"\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2,
"\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2,
"\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2,
"\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2,
"\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3,
"\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3,
"\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4,
"\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0,
);
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
if (!isset($currentMap['i']) || !isset($currentMap['p'])) {
$currentMap['p'] = $currentMap['i'] = array();
}
$strlen = strlen($string);
$charPos = count($currentMap['p']);
$foundChars = 0;
$invalid = false;
for ($i = 0; $i < $strlen; ++$i) {
$char = $string[$i];
$size = self::$s_length_map[$char];
if ($size == 0) {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue;
} else {
if ($invalid == true) {
/* We mark the chars as invalid and start a new char */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i;
$currentMap['i'][$charPos + $foundChars] = true;
++$foundChars;
$invalid = false;
}
if (($i + $size) > $strlen) {
$ignoredChars = substr($string, $i);
break;
}
for ($j = 1; $j < $size; ++$j) {
$char = $string[$i + $j];
if ($char > "\x7F" && $char < "\xC0") {
// Valid - continue parsing
} else {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue 2;
}
}
/* Ok we got a complete char here */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size;
$i += $j - 1;
++$foundChars;
}
}
return $foundChars;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_POSITIONS;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
if ($size < 1) {
return -1;
}
$needed = self::$length_map[$bytes[0]] - $size;
return $needed > -1 ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
interface Swift_CharacterReaderFactory
{
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset);
}

View File

@@ -0,0 +1,124 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Standard factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift_CharacterReaderFactory
{
/**
* A map of charset patterns to their implementation classes.
*
* @var array
*/
private static $_map = array();
/**
* Factories which have already been loaded.
*
* @var Swift_CharacterReaderFactory[]
*/
private static $_loaded = array();
/**
* Creates a new CharacterReaderFactory.
*/
public function __construct()
{
$this->init();
}
public function __wakeup()
{
$this->init();
}
public function init()
{
if (count(self::$_map) > 0) {
return;
}
$prefix = 'Swift_CharacterReader_';
$singleByte = array(
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => array(1),
);
$doubleByte = array(
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => array(2),
);
$fourBytes = array(
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => array(4),
);
// Utf-8
self::$_map['utf-?8'] = array(
'class' => $prefix.'Utf8Reader',
'constructor' => array(),
);
//7-8 bit charsets
self::$_map['(us-)?ascii'] = $singleByte;
self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte;
self::$_map['windows-?125[0-9]'] = $singleByte;
self::$_map['cp-?[0-9]+'] = $singleByte;
self::$_map['ansi'] = $singleByte;
self::$_map['macintosh'] = $singleByte;
self::$_map['koi-?7'] = $singleByte;
self::$_map['koi-?8-?.+'] = $singleByte;
self::$_map['mik'] = $singleByte;
self::$_map['(cork|t1)'] = $singleByte;
self::$_map['v?iscii'] = $singleByte;
//16 bits
self::$_map['(ucs-?2|utf-?16)'] = $doubleByte;
//32 bits
self::$_map['(ucs-?4|utf-?32)'] = $fourBytes;
// Fallback
self::$_map['.*'] = $singleByte;
}
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset)
{
$charset = trim(strtolower($charset));
foreach (self::$_map as $pattern => $spec) {
$re = '/^'.$pattern.'$/D';
if (preg_match($re, $charset)) {
if (!array_key_exists($pattern, self::$_loaded)) {
$reflector = new ReflectionClass($spec['class']);
if ($reflector->getConstructor()) {
$reader = $reflector->newInstanceArgs($spec['constructor']);
} else {
$reader = $reflector->newInstance();
}
self::$_loaded[$pattern] = $reader;
}
return self::$_loaded[$pattern];
}
}
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract means of reading and writing data in terms of characters as opposed
* to bytes.
*
* Classes implementing this interface may use a subsystem which requires less
* memory than working with large strings of data.
*
* @author Chris Corbyn
*/
interface Swift_CharacterStream
{
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset);
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory);
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os);
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string);
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length);
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length);
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars);
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset);
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents();
}

View File

@@ -0,0 +1,293 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Chris Corbyn
*/
class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream
{
/** A map of byte values and their respective characters */
private static $_charMap;
/** A map of characters and their derivative byte values */
private static $_byteMap;
/** The char reader (lazy-loaded) for the current charset */
private $_charReader;
/** A factory for creating CharacterReader instances */
private $_charReaderFactory;
/** The character set this stream is using */
private $_charset;
/** Array of characters */
private $_array = array();
/** Size of the array of character */
private $_array_size = array();
/** The current character offset in the stream */
private $_offset = 0;
/**
* Create a new CharacterStream with the given $chars, if set.
*
* @param Swift_CharacterReaderFactory $factory for loading validators
* @param string $charset used in the stream
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
self::_initializeMaps();
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->_charset = $charset;
$this->_charReader = null;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->_charReaderFactory = $factory;
}
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory
->getReaderFor($this->_charset);
}
$startLength = $this->_charReader->getInitialByteSize();
while (false !== $bytes = $os->read($startLength)) {
$c = array();
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$c[] = self::$_byteMap[$bytes[$i]];
}
$size = count($c);
$need = $this->_charReader
->validateByteSequence($c, $size);
if ($need > 0 &&
false !== $bytes = $os->read($need)) {
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$c[] = self::$_byteMap[$bytes[$i]];
}
}
$this->_array[] = $c;
++$this->_array_size;
}
}
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_offset == $this->_array_size) {
return false;
}
// Don't use array slice
$arrays = array();
$end = $length + $this->_offset;
for ($i = $this->_offset; $i < $end; ++$i) {
if (!isset($this->_array[$i])) {
break;
}
$arrays[] = $this->_array[$i];
}
$this->_offset += $i - $this->_offset; // Limit function calls
$chars = false;
foreach ($arrays as $array) {
$chars .= implode('', array_map('chr', $array));
}
return $chars;
}
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length)
{
if ($this->_offset == $this->_array_size) {
return false;
}
$arrays = array();
$end = $length + $this->_offset;
for ($i = $this->_offset; $i < $end; ++$i) {
if (!isset($this->_array[$i])) {
break;
}
$arrays[] = $this->_array[$i];
}
$this->_offset += ($i - $this->_offset); // Limit function calls
return call_user_func_array('array_merge', $arrays);
}
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory->getReaderFor(
$this->_charset);
}
$startLength = $this->_charReader->getInitialByteSize();
$fp = fopen('php://memory', 'w+b');
fwrite($fp, $chars);
unset($chars);
fseek($fp, 0, SEEK_SET);
$buffer = array(0);
$buf_pos = 1;
$buf_len = 1;
$has_datas = true;
do {
$bytes = array();
// Buffer Filing
if ($buf_len - $buf_pos < $startLength) {
$buf = array_splice($buffer, $buf_pos);
$new = $this->_reloadBuffer($fp, 100);
if ($new) {
$buffer = array_merge($buf, $new);
$buf_len = count($buffer);
$buf_pos = 0;
} else {
$has_datas = false;
}
}
if ($buf_len - $buf_pos > 0) {
$size = 0;
for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) {
++$size;
$bytes[] = $buffer[$buf_pos++];
}
$need = $this->_charReader->validateByteSequence(
$bytes, $size);
if ($need > 0) {
if ($buf_len - $buf_pos < $need) {
$new = $this->_reloadBuffer($fp, $need);
if ($new) {
$buffer = array_merge($buffer, $new);
$buf_len = count($buffer);
}
}
for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) {
$bytes[] = $buffer[$buf_pos++];
}
}
$this->_array[] = $bytes;
++$this->_array_size;
}
} while ($has_datas);
fclose($fp);
}
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($charOffset > $this->_array_size) {
$charOffset = $this->_array_size;
} elseif ($charOffset < 0) {
$charOffset = 0;
}
$this->_offset = $charOffset;
}
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents()
{
$this->_offset = 0;
$this->_array = array();
$this->_array_size = 0;
}
private function _reloadBuffer($fp, $len)
{
if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) {
$buf = array();
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$buf[] = self::$_byteMap[$bytes[$i]];
}
return $buf;
}
return false;
}
private static function _initializeMaps()
{
if (!isset(self::$_charMap)) {
self::$_charMap = array();
for ($byte = 0; $byte < 256; ++$byte) {
self::$_charMap[$byte] = chr($byte);
}
self::$_byteMap = array_flip(self::$_charMap);
}
}
}

View File

@@ -0,0 +1,267 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream
{
/**
* The char reader (lazy-loaded) for the current charset.
*
* @var Swift_CharacterReader
*/
private $_charReader;
/**
* A factory for creating CharacterReader instances.
*
* @var Swift_CharacterReaderFactory
*/
private $_charReaderFactory;
/**
* The character set this stream is using.
*
* @var string
*/
private $_charset;
/**
* The data's stored as-is.
*
* @var string
*/
private $_datas = '';
/**
* Number of bytes in the stream.
*
* @var int
*/
private $_datasSize = 0;
/**
* Map.
*
* @var mixed
*/
private $_map;
/**
* Map Type.
*
* @var int
*/
private $_mapType = 0;
/**
* Number of characters in the stream.
*
* @var int
*/
private $_charCount = 0;
/**
* Position in the stream.
*
* @var int
*/
private $_currentPos = 0;
/**
* Constructor.
*
* @param Swift_CharacterReaderFactory $factory
* @param string $charset
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/* -- Changing parameters of the stream -- */
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->_charset = $charset;
$this->_charReader = null;
$this->_mapType = 0;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->_charReaderFactory = $factory;
}
/**
* @see Swift_CharacterStream::flushContents()
*/
public function flushContents()
{
$this->_datas = null;
$this->_map = null;
$this->_charCount = 0;
$this->_currentPos = 0;
$this->_datasSize = 0;
}
/**
* @see Swift_CharacterStream::importByteStream()
*
* @param Swift_OutputByteStream $os
*/
public function importByteStream(Swift_OutputByteStream $os)
{
$this->flushContents();
$blocks = 512;
$os->setReadPointer(0);
while (false !== ($read = $os->read($blocks))) {
$this->write($read);
}
}
/**
* @see Swift_CharacterStream::importString()
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* @see Swift_CharacterStream::read()
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_currentPos >= $this->_charCount) {
return false;
}
$ret = false;
$length = $this->_currentPos + $length > $this->_charCount ? $this->_charCount - $this->_currentPos : $length;
switch ($this->_mapType) {
case Swift_CharacterReader::MAP_TYPE_FIXED_LEN:
$len = $length * $this->_map;
$ret = substr($this->_datas,
$this->_currentPos * $this->_map,
$len);
$this->_currentPos += $length;
break;
case Swift_CharacterReader::MAP_TYPE_INVALID:
$ret = '';
for (; $this->_currentPos < $length; ++$this->_currentPos) {
if (isset($this->_map[$this->_currentPos])) {
$ret .= '?';
} else {
$ret .= $this->_datas[$this->_currentPos];
}
}
break;
case Swift_CharacterReader::MAP_TYPE_POSITIONS:
$end = $this->_currentPos + $length;
$end = $end > $this->_charCount ? $this->_charCount : $end;
$ret = '';
$start = 0;
if ($this->_currentPos > 0) {
$start = $this->_map['p'][$this->_currentPos - 1];
}
$to = $start;
for (; $this->_currentPos < $end; ++$this->_currentPos) {
if (isset($this->_map['i'][$this->_currentPos])) {
$ret .= substr($this->_datas, $start, $to - $start).'?';
$start = $this->_map['p'][$this->_currentPos];
} else {
$to = $this->_map['p'][$this->_currentPos];
}
}
$ret .= substr($this->_datas, $start, $to - $start);
break;
}
return $ret;
}
/**
* @see Swift_CharacterStream::readBytes()
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length)
{
$read = $this->read($length);
if ($read !== false) {
$ret = array_map('ord', str_split($read, 1));
return $ret;
}
return false;
}
/**
* @see Swift_CharacterStream::setPointer()
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($this->_charCount < $charOffset) {
$charOffset = $this->_charCount;
}
$this->_currentPos = $charOffset;
}
/**
* @see Swift_CharacterStream::write()
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory->getReaderFor(
$this->_charset);
$this->_map = array();
$this->_mapType = $this->_charReader->getMapType();
}
$ignored = '';
$this->_datas .= $chars;
$this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored);
if ($ignored !== false) {
$this->_datasSize = strlen($this->_datas) - strlen($ignored);
} else {
$this->_datasSize = strlen($this->_datas);
}
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Base class for Spools (implements time and message limits).
*
* @author Fabien Potencier
*/
abstract class Swift_ConfigurableSpool implements Swift_Spool
{
/** The maximum number of messages to send per flush */
private $_message_limit;
/** The time limit per flush */
private $_time_limit;
/**
* Sets the maximum number of messages to send per flush.
*
* @param int $limit
*/
public function setMessageLimit($limit)
{
$this->_message_limit = (int) $limit;
}
/**
* Gets the maximum number of messages to send per flush.
*
* @return int The limit
*/
public function getMessageLimit()
{
return $this->_message_limit;
}
/**
* Sets the time limit (in seconds) per flush.
*
* @param int $limit The limit
*/
public function setTimeLimit($limit)
{
$this->_time_limit = (int) $limit;
}
/**
* Gets the time limit (in seconds) per flush.
*
* @return int The limit
*/
public function getTimeLimit()
{
return $this->_time_limit;
}
}

View File

@@ -0,0 +1,373 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Dependency Injection container.
*
* @author Chris Corbyn
*/
class Swift_DependencyContainer
{
/** Constant for literal value types */
const TYPE_VALUE = 0x0001;
/** Constant for new instance types */
const TYPE_INSTANCE = 0x0010;
/** Constant for shared instance types */
const TYPE_SHARED = 0x0100;
/** Constant for aliases */
const TYPE_ALIAS = 0x1000;
/** Singleton instance */
private static $_instance = null;
/** The data container */
private $_store = array();
/** The current endpoint in the data container */
private $_endPoint;
/**
* Constructor should not be used.
*
* Use {@link getInstance()} instead.
*/
public function __construct()
{
}
/**
* Returns a singleton of the DependencyContainer.
*
* @return self
*/
public static function getInstance()
{
if (!isset(self::$_instance)) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* List the names of all items stored in the Container.
*
* @return array
*/
public function listItems()
{
return array_keys($this->_store);
}
/**
* Test if an item is registered in this container with the given name.
*
* @see register()
*
* @param string $itemName
*
* @return bool
*/
public function has($itemName)
{
return array_key_exists($itemName, $this->_store)
&& isset($this->_store[$itemName]['lookupType']);
}
/**
* Lookup the item with the given $itemName.
*
* @see register()
*
* @param string $itemName
*
* @throws Swift_DependencyException If the dependency is not found
*
* @return mixed
*/
public function lookup($itemName)
{
if (!$this->has($itemName)) {
throw new Swift_DependencyException(
'Cannot lookup dependency "'.$itemName.'" since it is not registered.'
);
}
switch ($this->_store[$itemName]['lookupType']) {
case self::TYPE_ALIAS:
return $this->_createAlias($itemName);
case self::TYPE_VALUE:
return $this->_getValue($itemName);
case self::TYPE_INSTANCE:
return $this->_createNewInstance($itemName);
case self::TYPE_SHARED:
return $this->_createSharedInstance($itemName);
}
}
/**
* Create an array of arguments passed to the constructor of $itemName.
*
* @param string $itemName
*
* @return array
*/
public function createDependenciesFor($itemName)
{
$args = array();
if (isset($this->_store[$itemName]['args'])) {
$args = $this->_resolveArgs($this->_store[$itemName]['args']);
}
return $args;
}
/**
* Register a new dependency with $itemName.
*
* This method returns the current DependencyContainer instance because it
* requires the use of the fluid interface to set the specific details for the
* dependency.
*
* @see asNewInstanceOf(), asSharedInstanceOf(), asValue()
*
* @param string $itemName
*
* @return $this
*/
public function register($itemName)
{
$this->_store[$itemName] = array();
$this->_endPoint = &$this->_store[$itemName];
return $this;
}
/**
* Specify the previously registered item as a literal value.
*
* {@link register()} must be called before this will work.
*
* @param mixed $value
*
* @return $this
*/
public function asValue($value)
{
$endPoint = &$this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_VALUE;
$endPoint['value'] = $value;
return $this;
}
/**
* Specify the previously registered item as an alias of another item.
*
* @param string $lookup
*
* @return $this
*/
public function asAliasOf($lookup)
{
$endPoint = &$this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_ALIAS;
$endPoint['ref'] = $lookup;
return $this;
}
/**
* Specify the previously registered item as a new instance of $className.
*
* {@link register()} must be called before this will work.
* Any arguments can be set with {@link withDependencies()},
* {@link addConstructorValue()} or {@link addConstructorLookup()}.
*
* @see withDependencies(), addConstructorValue(), addConstructorLookup()
*
* @param string $className
*
* @return $this
*/
public function asNewInstanceOf($className)
{
$endPoint = &$this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_INSTANCE;
$endPoint['className'] = $className;
return $this;
}
/**
* Specify the previously registered item as a shared instance of $className.
*
* {@link register()} must be called before this will work.
*
* @param string $className
*
* @return $this
*/
public function asSharedInstanceOf($className)
{
$endPoint = &$this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_SHARED;
$endPoint['className'] = $className;
return $this;
}
/**
* Specify a list of injected dependencies for the previously registered item.
*
* This method takes an array of lookup names.
*
* @see addConstructorValue(), addConstructorLookup()
*
* @param array $lookups
*
* @return $this
*/
public function withDependencies(array $lookups)
{
$endPoint = &$this->_getEndPoint();
$endPoint['args'] = array();
foreach ($lookups as $lookup) {
$this->addConstructorLookup($lookup);
}
return $this;
}
/**
* Specify a literal (non looked up) value for the constructor of the
* previously registered item.
*
* @see withDependencies(), addConstructorLookup()
*
* @param mixed $value
*
* @return $this
*/
public function addConstructorValue($value)
{
$endPoint = &$this->_getEndPoint();
if (!isset($endPoint['args'])) {
$endPoint['args'] = array();
}
$endPoint['args'][] = array('type' => 'value', 'item' => $value);
return $this;
}
/**
* Specify a dependency lookup for the constructor of the previously
* registered item.
*
* @see withDependencies(), addConstructorValue()
*
* @param string $lookup
*
* @return $this
*/
public function addConstructorLookup($lookup)
{
$endPoint = &$this->_getEndPoint();
if (!isset($this->_endPoint['args'])) {
$endPoint['args'] = array();
}
$endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup);
return $this;
}
/** Get the literal value with $itemName */
private function _getValue($itemName)
{
return $this->_store[$itemName]['value'];
}
/** Resolve an alias to another item */
private function _createAlias($itemName)
{
return $this->lookup($this->_store[$itemName]['ref']);
}
/** Create a fresh instance of $itemName */
private function _createNewInstance($itemName)
{
$reflector = new ReflectionClass($this->_store[$itemName]['className']);
if ($reflector->getConstructor()) {
return $reflector->newInstanceArgs(
$this->createDependenciesFor($itemName)
);
}
return $reflector->newInstance();
}
/** Create and register a shared instance of $itemName */
private function _createSharedInstance($itemName)
{
if (!isset($this->_store[$itemName]['instance'])) {
$this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName);
}
return $this->_store[$itemName]['instance'];
}
/** Get the current endpoint in the store */
private function &_getEndPoint()
{
if (!isset($this->_endPoint)) {
throw new BadMethodCallException(
'Component must first be registered by calling register()'
);
}
return $this->_endPoint;
}
/** Get an argument list with dependencies resolved */
private function _resolveArgs(array $args)
{
$resolved = array();
foreach ($args as $argDefinition) {
switch ($argDefinition['type']) {
case 'lookup':
$resolved[] = $this->_lookupRecursive($argDefinition['item']);
break;
case 'value':
$resolved[] = $argDefinition['item'];
break;
}
}
return $resolved;
}
/** Resolve a single dependency with an collections */
private function _lookupRecursive($item)
{
if (is_array($item)) {
$collection = array();
foreach ($item as $k => $v) {
$collection[$k] = $this->_lookupRecursive($v);
}
return $collection;
}
return $this->lookup($item);
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* DependencyException gets thrown when a requested dependency is missing.
*
* @author Chris Corbyn
*/
class Swift_DependencyException extends Swift_SwiftException
{
/**
* Create a new DependencyException with $message.
*
* @param string $message
*/
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,69 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An embedded file, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile
{
/**
* Create a new EmbeddedFile.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
call_user_func_array(
array($this, 'Swift_Mime_EmbeddedFile::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.embeddedfile')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new EmbeddedFile.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return Swift_Mime_EmbeddedFile
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new EmbeddedFile from a filesystem path.
*
* @param string $path
*
* @return Swift_Mime_EmbeddedFile
*/
public static function fromPath($path)
{
return self::newInstance()->setFile(
new Swift_ByteStream_FileByteStream($path)
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Encoder schemes.
*
* @author Chris Corbyn
*/
interface Swift_Encoder extends Swift_Mime_CharsetObserver
{
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset if first line needs to be shorter
* @param int $maxLineLength - 0 indicates the default length for this encoding
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0);
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base 64 Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Base64Encoder implements Swift_Encoder
{
/**
* Takes an unencoded string and produces a Base64 encoded string from it.
*
* Base64 encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 76 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if (0 >= $maxLineLength || 76 < $maxLineLength) {
$maxLineLength = 76;
}
$encodedString = base64_encode($string);
$firstLine = '';
if (0 != $firstLineOffset) {
$firstLine = substr(
$encodedString, 0, $maxLineLength - $firstLineOffset
)."\r\n";
$encodedString = substr(
$encodedString, $maxLineLength - $firstLineOffset
);
}
return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n"));
}
/**
* Does nothing.
*/
public function charsetChanged($charset)
{
}
}

View File

@@ -0,0 +1,300 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Quoted Printable (QP) Encoding in Swift Mailer.
*
* Possibly the most accurate RFC 2045 QP implementation found in PHP.
*
* @author Chris Corbyn
*/
class Swift_Encoder_QpEncoder implements Swift_Encoder
{
/**
* The CharacterStream used for reading characters (as opposed to bytes).
*
* @var Swift_CharacterStream
*/
protected $_charStream;
/**
* A filter used if input should be canonicalized.
*
* @var Swift_StreamFilter
*/
protected $_filter;
/**
* Pre-computed QP for HUGE optimization.
*
* @var string[]
*/
protected static $_qpMap = array(
0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04',
5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09',
10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E',
15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13',
20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18',
25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D',
30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22',
35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27',
40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C',
45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31',
50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36',
55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B',
60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40',
65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45',
70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A',
75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F',
80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54',
85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59',
90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E',
95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63',
100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68',
105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D',
110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72',
115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77',
120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C',
125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81',
130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86',
135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B',
140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90',
145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95',
150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A',
155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F',
160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4',
165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9',
170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE',
175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3',
180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8',
185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD',
190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2',
195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7',
200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC',
205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1',
210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6',
215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB',
220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0',
225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5',
230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA',
235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF',
240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4',
245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9',
250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE',
255 => '=FF',
);
protected static $_safeMapShare = array();
/**
* A map of non-encoded ascii characters.
*
* @var string[]
*/
protected $_safeMap = array();
/**
* Creates a new QpEncoder for the given CharacterStream.
*
* @param Swift_CharacterStream $charStream to use for reading characters
* @param Swift_StreamFilter $filter if input should be canonicalized
*/
public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null)
{
$this->_charStream = $charStream;
if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) {
$this->initSafeMap();
self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
} else {
$this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
}
$this->_filter = $filter;
}
public function __sleep()
{
return array('_charStream', '_filter');
}
public function __wakeup()
{
if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) {
$this->initSafeMap();
self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
} else {
$this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
}
}
protected function getSafeMapShareId()
{
return get_class($this);
}
protected function initSafeMap()
{
foreach (array_merge(
array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) {
$this->_safeMap[$byte] = chr($byte);
}
}
/**
* Takes an unencoded string and produces a QP encoded string from it.
*
* QP encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset, optional
* @param int $maxLineLength, optional 0 indicates the default of 76 chars
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if ($maxLineLength > 76 || $maxLineLength <= 0) {
$maxLineLength = 76;
}
$thisLineLength = $maxLineLength - $firstLineOffset;
$lines = array();
$lNo = 0;
$lines[$lNo] = '';
$currentLine = &$lines[$lNo++];
$size = $lineLen = 0;
$this->_charStream->flushContents();
$this->_charStream->importString($string);
// Fetching more than 4 chars at one is slower, as is fetching fewer bytes
// Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6
// bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes
while (false !== $bytes = $this->_nextSequence()) {
// If we're filtering the input
if (isset($this->_filter)) {
// If we can't filter because we need more bytes
while ($this->_filter->shouldBuffer($bytes)) {
// Then collect bytes into the buffer
if (false === $moreBytes = $this->_nextSequence(1)) {
break;
}
foreach ($moreBytes as $b) {
$bytes[] = $b;
}
}
// And filter them
$bytes = $this->_filter->filter($bytes);
}
$enc = $this->_encodeByteSequence($bytes, $size);
$i = strpos($enc, '=0D=0A');
$newLineLength = $lineLen + ($i === false ? $size : $i);
if ($currentLine && $newLineLength >= $thisLineLength) {
$lines[$lNo] = '';
$currentLine = &$lines[$lNo++];
$thisLineLength = $maxLineLength;
$lineLen = 0;
}
$currentLine .= $enc;
if ($i === false) {
$lineLen += $size;
} else {
// 6 is the length of '=0D=0A'.
$lineLen = $size - strrpos($enc, '=0D=0A') - 6;
}
}
return $this->_standardize(implode("=\r\n", $lines));
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->_charStream->setCharacterSet($charset);
}
/**
* Encode the given byte array into a verbatim QP form.
*
* @param int[] $bytes
* @param int $size
*
* @return string
*/
protected function _encodeByteSequence(array $bytes, &$size)
{
$ret = '';
$size = 0;
foreach ($bytes as $b) {
if (isset($this->_safeMap[$b])) {
$ret .= $this->_safeMap[$b];
++$size;
} else {
$ret .= self::$_qpMap[$b];
$size += 3;
}
}
return $ret;
}
/**
* Get the next sequence of bytes to read from the char stream.
*
* @param int $size number of bytes to read
*
* @return int[]
*/
protected function _nextSequence($size = 4)
{
return $this->_charStream->readBytes($size);
}
/**
* Make sure CRLF is correct and HT/SPACE are in valid places.
*
* @param string $string
*
* @return string
*/
protected function _standardize($string)
{
$string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'),
array("=09\r\n", "=20\r\n", "\r\n"), $string
);
switch ($end = ord(substr($string, -1))) {
case 0x09:
case 0x20:
$string = substr_replace($string, self::$_qpMap[$end], -1);
}
return $string;
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->_charStream = clone $this->_charStream;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles RFC 2231 specified Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder
{
/**
* A character stream to use when reading a string as characters instead of bytes.
*
* @var Swift_CharacterStream
*/
private $_charStream;
/**
* Creates a new Rfc2231Encoder using the given character stream instance.
*
* @param Swift_CharacterStream
*/
public function __construct(Swift_CharacterStream $charStream)
{
$this->_charStream = $charStream;
}
/**
* Takes an unencoded string and produces a string encoded according to
* RFC 2231 from it.
*
* @param string $string
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 75 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
$lines = array();
$lineCount = 0;
$lines[] = '';
$currentLine = &$lines[$lineCount++];
if (0 >= $maxLineLength) {
$maxLineLength = 75;
}
$this->_charStream->flushContents();
$this->_charStream->importString($string);
$thisLineLength = $maxLineLength - $firstLineOffset;
while (false !== $char = $this->_charStream->read(4)) {
$encodedChar = rawurlencode($char);
if (0 != strlen($currentLine)
&& strlen($currentLine.$encodedChar) > $thisLineLength) {
$lines[] = '';
$currentLine = &$lines[$lineCount++];
$thisLineLength = $maxLineLength;
}
$currentLine .= $encodedChar;
}
return implode("\r\n", $lines);
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->_charStream->setCharacterSet($charset);
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->_charStream = clone $this->_charStream;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides quick access to each encoding type.
*
* @author Chris Corbyn
*/
class Swift_Encoding
{
/**
* Get the Encoder that provides 7-bit encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function get7BitEncoding()
{
return self::_lookup('mime.7bitcontentencoder');
}
/**
* Get the Encoder that provides 8-bit encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function get8BitEncoding()
{
return self::_lookup('mime.8bitcontentencoder');
}
/**
* Get the Encoder that provides Quoted-Printable (QP) encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function getQpEncoding()
{
return self::_lookup('mime.qpcontentencoder');
}
/**
* Get the Encoder that provides Base64 encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function getBase64Encoding()
{
return self::_lookup('mime.base64contentencoder');
}
private static function _lookup($key)
{
return Swift_DependencyContainer::getInstance()->lookup($key);
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a command is sent over an SMTP connection.
*
* @author Chris Corbyn
*/
class Swift_Events_CommandEvent extends Swift_Events_EventObject
{
/**
* The command sent to the server.
*
* @var string
*/
private $_command;
/**
* An array of codes which a successful response will contain.
*
* @var int[]
*/
private $_successCodes = array();
/**
* Create a new CommandEvent for $source with $command.
*
* @param Swift_Transport $source
* @param string $command
* @param array $successCodes
*/
public function __construct(Swift_Transport $source, $command, $successCodes = array())
{
parent::__construct($source);
$this->_command = $command;
$this->_successCodes = $successCodes;
}
/**
* Get the command which was sent to the server.
*
* @return string
*/
public function getCommand()
{
return $this->_command;
}
/**
* Get the numeric response codes which indicate success for this command.
*
* @return int[]
*/
public function getSuccessCodes()
{
return $this->_successCodes;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for Transports to send commands to the server.
*
* @author Chris Corbyn
*/
interface Swift_Events_CommandListener extends Swift_Events_EventListener
{
/**
* Invoked immediately following a command being sent.
*
* @param Swift_Events_CommandEvent $evt
*/
public function commandSent(Swift_Events_CommandEvent $evt);
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The minimum interface for an Event.
*
* @author Chris Corbyn
*/
interface Swift_Events_Event
{
/**
* Get the source object of this event.
*
* @return object
*/
public function getSource();
/**
* Prevent this Event from bubbling any further up the stack.
*
* @param bool $cancel, optional
*/
public function cancelBubble($cancel = true);
/**
* Returns true if this Event will not bubble any further up the stack.
*
* @return bool
*/
public function bubbleCancelled();
}

View File

@@ -0,0 +1,83 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for the EventDispatcher which handles the event dispatching layer.
*
* @author Chris Corbyn
*/
interface Swift_Events_EventDispatcher
{
/**
* Create a new SendEvent for $source and $message.
*
* @param Swift_Transport $source
* @param Swift_Mime_Message
*
* @return Swift_Events_SendEvent
*/
public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message);
/**
* Create a new CommandEvent for $source and $command.
*
* @param Swift_Transport $source
* @param string $command That will be executed
* @param array $successCodes That are needed
*
* @return Swift_Events_CommandEvent
*/
public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array());
/**
* Create a new ResponseEvent for $source and $response.
*
* @param Swift_Transport $source
* @param string $response
* @param bool $valid If the response is valid
*
* @return Swift_Events_ResponseEvent
*/
public function createResponseEvent(Swift_Transport $source, $response, $valid);
/**
* Create a new TransportChangeEvent for $source.
*
* @param Swift_Transport $source
*
* @return Swift_Events_TransportChangeEvent
*/
public function createTransportChangeEvent(Swift_Transport $source);
/**
* Create a new TransportExceptionEvent for $source.
*
* @param Swift_Transport $source
* @param Swift_TransportException $ex
*
* @return Swift_Events_TransportExceptionEvent
*/
public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex);
/**
* Bind an event listener to this dispatcher.
*
* @param Swift_Events_EventListener $listener
*/
public function bindEventListener(Swift_Events_EventListener $listener);
/**
* Dispatch the given Event to all suitable listeners.
*
* @param Swift_Events_EventObject $evt
* @param string $target method
*/
public function dispatchEvent(Swift_Events_EventObject $evt, $target);
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An identity interface which all EventListeners must extend.
*
* @author Chris Corbyn
*/
interface Swift_Events_EventListener
{
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A base Event which all Event classes inherit from.
*
* @author Chris Corbyn
*/
class Swift_Events_EventObject implements Swift_Events_Event
{
/** The source of this Event */
private $_source;
/** The state of this Event (should it bubble up the stack?) */
private $_bubbleCancelled = false;
/**
* Create a new EventObject originating at $source.
*
* @param object $source
*/
public function __construct($source)
{
$this->_source = $source;
}
/**
* Get the source object of this event.
*
* @return object
*/
public function getSource()
{
return $this->_source;
}
/**
* Prevent this Event from bubbling any further up the stack.
*
* @param bool $cancel, optional
*/
public function cancelBubble($cancel = true)
{
$this->_bubbleCancelled = $cancel;
}
/**
* Returns true if this Event will not bubble any further up the stack.
*
* @return bool
*/
public function bubbleCancelled()
{
return $this->_bubbleCancelled;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a response is received on a SMTP connection.
*
* @author Chris Corbyn
*/
class Swift_Events_ResponseEvent extends Swift_Events_EventObject
{
/**
* The overall result.
*
* @var bool
*/
private $_valid;
/**
* The response received from the server.
*
* @var string
*/
private $_response;
/**
* Create a new ResponseEvent for $source and $response.
*
* @param Swift_Transport $source
* @param string $response
* @param bool $valid
*/
public function __construct(Swift_Transport $source, $response, $valid = false)
{
parent::__construct($source);
$this->_response = $response;
$this->_valid = $valid;
}
/**
* Get the response which was received from the server.
*
* @return string
*/
public function getResponse()
{
return $this->_response;
}
/**
* Get the success status of this Event.
*
* @return bool
*/
public function isValid()
{
return $this->_valid;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for responses from a remote SMTP server.
*
* @author Chris Corbyn
*/
interface Swift_Events_ResponseListener extends Swift_Events_EventListener
{
/**
* Invoked immediately following a response coming back.
*
* @param Swift_Events_ResponseEvent $evt
*/
public function responseReceived(Swift_Events_ResponseEvent $evt);
}

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a message is being sent.
*
* @author Chris Corbyn
*/
class Swift_Events_SendEvent extends Swift_Events_EventObject
{
/** Sending has yet to occur */
const RESULT_PENDING = 0x0001;
/** Email is spooled, ready to be sent */
const RESULT_SPOOLED = 0x0011;
/** Sending was successful */
const RESULT_SUCCESS = 0x0010;
/** Sending worked, but there were some failures */
const RESULT_TENTATIVE = 0x0100;
/** Sending failed */
const RESULT_FAILED = 0x1000;
/**
* The Message being sent.
*
* @var Swift_Mime_Message
*/
private $_message;
/**
* Any recipients which failed after sending.
*
* @var string[]
*/
private $_failedRecipients = array();
/**
* The overall result as a bitmask from the class constants.
*
* @var int
*/
private $_result;
/**
* Create a new SendEvent for $source and $message.
*
* @param Swift_Transport $source
* @param Swift_Mime_Message $message
*/
public function __construct(Swift_Transport $source, Swift_Mime_Message $message)
{
parent::__construct($source);
$this->_message = $message;
$this->_result = self::RESULT_PENDING;
}
/**
* Get the Transport used to send the Message.
*
* @return Swift_Transport
*/
public function getTransport()
{
return $this->getSource();
}
/**
* Get the Message being sent.
*
* @return Swift_Mime_Message
*/
public function getMessage()
{
return $this->_message;
}
/**
* Set the array of addresses that failed in sending.
*
* @param array $recipients
*/
public function setFailedRecipients($recipients)
{
$this->_failedRecipients = $recipients;
}
/**
* Get an recipient addresses which were not accepted for delivery.
*
* @return string[]
*/
public function getFailedRecipients()
{
return $this->_failedRecipients;
}
/**
* Set the result of sending.
*
* @param int $result
*/
public function setResult($result)
{
$this->_result = $result;
}
/**
* Get the result of this Event.
*
* The return value is a bitmask from
* {@see RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED}
*
* @return int
*/
public function getResult()
{
return $this->_result;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for Messages being sent from within the Transport system.
*
* @author Chris Corbyn
*/
interface Swift_Events_SendListener extends Swift_Events_EventListener
{
/**
* Invoked immediately before the Message is sent.
*
* @param Swift_Events_SendEvent $evt
*/
public function beforeSendPerformed(Swift_Events_SendEvent $evt);
/**
* Invoked immediately after the Message is sent.
*
* @param Swift_Events_SendEvent $evt
*/
public function sendPerformed(Swift_Events_SendEvent $evt);
}

View File

@@ -0,0 +1,156 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The EventDispatcher which handles the event dispatching layer.
*
* @author Chris Corbyn
*/
class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher
{
/** A map of event types to their associated listener types */
private $_eventMap = array();
/** Event listeners bound to this dispatcher */
private $_listeners = array();
/** Listeners queued to have an Event bubbled up the stack to them */
private $_bubbleQueue = array();
/**
* Create a new EventDispatcher.
*/
public function __construct()
{
$this->_eventMap = array(
'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener',
'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener',
'Swift_Events_SendEvent' => 'Swift_Events_SendListener',
'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener',
'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener',
);
}
/**
* Create a new SendEvent for $source and $message.
*
* @param Swift_Transport $source
* @param Swift_Mime_Message
*
* @return Swift_Events_SendEvent
*/
public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message)
{
return new Swift_Events_SendEvent($source, $message);
}
/**
* Create a new CommandEvent for $source and $command.
*
* @param Swift_Transport $source
* @param string $command That will be executed
* @param array $successCodes That are needed
*
* @return Swift_Events_CommandEvent
*/
public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array())
{
return new Swift_Events_CommandEvent($source, $command, $successCodes);
}
/**
* Create a new ResponseEvent for $source and $response.
*
* @param Swift_Transport $source
* @param string $response
* @param bool $valid If the response is valid
*
* @return Swift_Events_ResponseEvent
*/
public function createResponseEvent(Swift_Transport $source, $response, $valid)
{
return new Swift_Events_ResponseEvent($source, $response, $valid);
}
/**
* Create a new TransportChangeEvent for $source.
*
* @param Swift_Transport $source
*
* @return Swift_Events_TransportChangeEvent
*/
public function createTransportChangeEvent(Swift_Transport $source)
{
return new Swift_Events_TransportChangeEvent($source);
}
/**
* Create a new TransportExceptionEvent for $source.
*
* @param Swift_Transport $source
* @param Swift_TransportException $ex
*
* @return Swift_Events_TransportExceptionEvent
*/
public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex)
{
return new Swift_Events_TransportExceptionEvent($source, $ex);
}
/**
* Bind an event listener to this dispatcher.
*
* @param Swift_Events_EventListener $listener
*/
public function bindEventListener(Swift_Events_EventListener $listener)
{
foreach ($this->_listeners as $l) {
// Already loaded
if ($l === $listener) {
return;
}
}
$this->_listeners[] = $listener;
}
/**
* Dispatch the given Event to all suitable listeners.
*
* @param Swift_Events_EventObject $evt
* @param string $target method
*/
public function dispatchEvent(Swift_Events_EventObject $evt, $target)
{
$this->_prepareBubbleQueue($evt);
$this->_bubble($evt, $target);
}
/** Queue listeners on a stack ready for $evt to be bubbled up it */
private function _prepareBubbleQueue(Swift_Events_EventObject $evt)
{
$this->_bubbleQueue = array();
$evtClass = get_class($evt);
foreach ($this->_listeners as $listener) {
if (array_key_exists($evtClass, $this->_eventMap)
&& ($listener instanceof $this->_eventMap[$evtClass])) {
$this->_bubbleQueue[] = $listener;
}
}
}
/** Bubble $evt up the stack calling $target() on each listener */
private function _bubble(Swift_Events_EventObject $evt, $target)
{
if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) {
$listener->$target($evt);
$this->_bubble($evt, $target);
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when the state of a Transport is changed (i.e. stopped/started).
*
* @author Chris Corbyn
*/
class Swift_Events_TransportChangeEvent extends Swift_Events_EventObject
{
/**
* Get the Transport.
*
* @return Swift_Transport
*/
public function getTransport()
{
return $this->getSource();
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for changes within the Transport system.
*
* @author Chris Corbyn
*/
interface Swift_Events_TransportChangeListener extends Swift_Events_EventListener
{
/**
* Invoked just before a Transport is started.
*
* @param Swift_Events_TransportChangeEvent $evt
*/
public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt);
/**
* Invoked immediately after the Transport is started.
*
* @param Swift_Events_TransportChangeEvent $evt
*/
public function transportStarted(Swift_Events_TransportChangeEvent $evt);
/**
* Invoked just before a Transport is stopped.
*
* @param Swift_Events_TransportChangeEvent $evt
*/
public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt);
/**
* Invoked immediately after the Transport is stopped.
*
* @param Swift_Events_TransportChangeEvent $evt
*/
public function transportStopped(Swift_Events_TransportChangeEvent $evt);
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a TransportException is thrown from the Transport system.
*
* @author Chris Corbyn
*/
class Swift_Events_TransportExceptionEvent extends Swift_Events_EventObject
{
/**
* The Exception thrown.
*
* @var Swift_TransportException
*/
private $_exception;
/**
* Create a new TransportExceptionEvent for $transport.
*
* @param Swift_Transport $transport
* @param Swift_TransportException $ex
*/
public function __construct(Swift_Transport $transport, Swift_TransportException $ex)
{
parent::__construct($transport);
$this->_exception = $ex;
}
/**
* Get the TransportException thrown.
*
* @return Swift_TransportException
*/
public function getException()
{
return $this->_exception;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for Exceptions thrown from within the Transport system.
*
* @author Chris Corbyn
*/
interface Swift_Events_TransportExceptionListener extends Swift_Events_EventListener
{
/**
* Invoked as a TransportException is thrown in the Transport system.
*
* @param Swift_Events_TransportExceptionEvent $evt
*/
public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt);
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Contains a list of redundant Transports so when one fails, the next is used.
*
* @author Chris Corbyn
*/
class Swift_FailoverTransport extends Swift_Transport_FailoverTransport
{
/**
* Creates a new FailoverTransport with $transports.
*
* @param Swift_Transport[] $transports
*/
public function __construct($transports = array())
{
call_user_func_array(
array($this, 'Swift_Transport_FailoverTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.failover')
);
$this->setTransports($transports);
}
/**
* Create a new FailoverTransport instance.
*
* @param Swift_Transport[] $transports
*
* @return self
*/
public static function newInstance($transports = array())
{
return new self($transports);
}
}

View File

@@ -0,0 +1,208 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Stores Messages on the filesystem.
*
* @author Fabien Potencier
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_FileSpool extends Swift_ConfigurableSpool
{
/** The spool directory */
private $_path;
/**
* File WriteRetry Limit.
*
* @var int
*/
private $_retryLimit = 10;
/**
* Create a new FileSpool.
*
* @param string $path
*
* @throws Swift_IoException
*/
public function __construct($path)
{
$this->_path = $path;
if (!file_exists($this->_path)) {
if (!mkdir($this->_path, 0777, true)) {
throw new Swift_IoException(sprintf('Unable to create path "%s".', $this->_path));
}
}
}
/**
* Tests if this Spool mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return true;
}
/**
* Starts this Spool mechanism.
*/
public function start()
{
}
/**
* Stops this Spool mechanism.
*/
public function stop()
{
}
/**
* Allow to manage the enqueuing retry limit.
*
* Default, is ten and allows over 64^20 different fileNames
*
* @param int $limit
*/
public function setRetryLimit($limit)
{
$this->_retryLimit = $limit;
}
/**
* Queues a message.
*
* @param Swift_Mime_Message $message The message to store
*
* @throws Swift_IoException
*
* @return bool
*/
public function queueMessage(Swift_Mime_Message $message)
{
$ser = serialize($message);
$fileName = $this->_path.'/'.$this->getRandomString(10);
for ($i = 0; $i < $this->_retryLimit; ++$i) {
/* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */
$fp = @fopen($fileName.'.message', 'x');
if (false !== $fp) {
if (false === fwrite($fp, $ser)) {
return false;
}
return fclose($fp);
} else {
/* The file already exists, we try a longer fileName */
$fileName .= $this->getRandomString(1);
}
}
throw new Swift_IoException(sprintf('Unable to create a file for enqueuing Message in "%s".', $this->_path));
}
/**
* Execute a recovery if for any reason a process is sending for too long.
*
* @param int $timeout in second Defaults is for very slow smtp responses
*/
public function recover($timeout = 900)
{
foreach (new DirectoryIterator($this->_path) as $file) {
$file = $file->getRealPath();
if (substr($file, -16) == '.message.sending') {
$lockedtime = filectime($file);
if ((time() - $lockedtime) > $timeout) {
rename($file, substr($file, 0, -8));
}
}
}
}
/**
* Sends messages using the given transport instance.
*
* @param Swift_Transport $transport A transport instance
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent e-mail's
*/
public function flushQueue(Swift_Transport $transport, &$failedRecipients = null)
{
$directoryIterator = new DirectoryIterator($this->_path);
/* Start the transport only if there are queued files to send */
if (!$transport->isStarted()) {
foreach ($directoryIterator as $file) {
if (substr($file->getRealPath(), -8) == '.message') {
$transport->start();
break;
}
}
}
$failedRecipients = (array) $failedRecipients;
$count = 0;
$time = time();
foreach ($directoryIterator as $file) {
$file = $file->getRealPath();
if (substr($file, -8) != '.message') {
continue;
}
/* We try a rename, it's an atomic operation, and avoid locking the file */
if (rename($file, $file.'.sending')) {
$message = unserialize(file_get_contents($file.'.sending'));
$count += $transport->send($message, $failedRecipients);
unlink($file.'.sending');
} else {
/* This message has just been catched by another process */
continue;
}
if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) {
break;
}
if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) {
break;
}
}
return $count;
}
/**
* Returns a random string needed to generate a fileName for the queue.
*
* @param int $count
*
* @return string
*/
protected function getRandomString($count)
{
// This string MUST stay FS safe, avoid special chars
$base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
$ret = '';
$strlen = strlen($base);
for ($i = 0; $i < $count; ++$i) {
$ret .= $base[((int) rand(0, $strlen - 1))];
}
return $ret;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An OutputByteStream which specifically reads from a file.
*
* @author Chris Corbyn
*/
interface Swift_FileStream extends Swift_OutputByteStream
{
/**
* Get the complete path to the file.
*
* @return string
*/
public function getPath();
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows StreamFilters to operate on a stream.
*
* @author Chris Corbyn
*/
interface Swift_Filterable
{
/**
* Add a new StreamFilter, referenced by $key.
*
* @param Swift_StreamFilter $filter
* @param string $key
*/
public function addFilter(Swift_StreamFilter $filter, $key);
/**
* Remove an existing filter using $key.
*
* @param string $key
*/
public function removeFilter($key);
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An image, embedded in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Image extends Swift_EmbeddedFile
{
/**
* Create a new EmbeddedFile.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
parent::__construct($data, $filename, $contentType);
}
/**
* Create a new Image.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return self
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new Image from a filesystem path.
*
* @param string $path
*
* @return self
*/
public static function fromPath($path)
{
return self::newInstance()->setFile(new Swift_ByteStream_FileByteStream($path));
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract means of writing data.
*
* Classes implementing this interface may use a subsystem which requires less
* memory than working with large strings of data.
*
* @author Chris Corbyn
*/
interface Swift_InputByteStream
{
/**
* Writes $bytes to the end of the stream.
*
* Writing may not happen immediately if the stream chooses to buffer. If
* you want to write these bytes with immediate effect, call {@link commit()}
* after calling write().
*
* This method returns the sequence ID of the write (i.e. 1 for first, 2 for
* second, etc etc).
*
* @param string $bytes
*
* @throws Swift_IoException
*
* @return int
*/
public function write($bytes);
/**
* For any bytes that are currently buffered inside the stream, force them
* off the buffer.
*
* @throws Swift_IoException
*/
public function commit();
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is);
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is);
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*
* @throws Swift_IoException
*/
public function flushBuffers();
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* I/O Exception class.
*
* @author Chris Corbyn
*/
class Swift_IoException extends Swift_SwiftException
{
/**
* Create a new IoException with $message.
*
* @param string $message
* @param int $code
* @param Exception $previous
*/
public function __construct($message, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,105 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides a mechanism for storing data using two keys.
*
* @author Chris Corbyn
*/
interface Swift_KeyCache
{
/** Mode for replacing existing cached data */
const MODE_WRITE = 1;
/** Mode for appending data to the end of existing cached data */
const MODE_APPEND = 2;
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*/
public function setString($nsKey, $itemKey, $string, $mode);
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_OutputByteStream $os
* @param int $mode
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode);
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
* If the optional third parameter is passed all writes will go through $is.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is optional input stream
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $is = null);
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @return string
*/
public function getString($nsKey, $itemKey);
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is stream to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is);
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey);
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey);
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey);
}

View File

@@ -0,0 +1,206 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A basic KeyCache backed by an array.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache
{
/**
* Cache contents.
*
* @var array
*/
private $_contents = array();
/**
* An InputStream for cloning.
*
* @var Swift_KeyCache_KeyCacheInputStream
*/
private $_stream;
/**
* Create a new ArrayKeyCache with the given $stream for cloning to make
* InputByteStreams.
*
* @param Swift_KeyCache_KeyCacheInputStream $stream
*/
public function __construct(Swift_KeyCache_KeyCacheInputStream $stream)
{
$this->_stream = $stream;
}
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*/
public function setString($nsKey, $itemKey, $string, $mode)
{
$this->_prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$this->_contents[$nsKey][$itemKey] = $string;
break;
case self::MODE_APPEND:
if (!$this->hasKey($nsKey, $itemKey)) {
$this->_contents[$nsKey][$itemKey] = '';
}
$this->_contents[$nsKey][$itemKey] .= $string;
break;
default:
throw new Swift_SwiftException(
'Invalid mode ['.$mode.'] used to set nsKey='.
$nsKey.', itemKey='.$itemKey
);
}
}
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_OutputByteStream $os
* @param int $mode
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
{
$this->_prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$this->clearKey($nsKey, $itemKey);
case self::MODE_APPEND:
if (!$this->hasKey($nsKey, $itemKey)) {
$this->_contents[$nsKey][$itemKey] = '';
}
while (false !== $bytes = $os->read(8192)) {
$this->_contents[$nsKey][$itemKey] .= $bytes;
}
break;
default:
throw new Swift_SwiftException(
'Invalid mode ['.$mode.'] used to set nsKey='.
$nsKey.', itemKey='.$itemKey
);
}
}
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $writeThrough
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
{
$is = clone $this->_stream;
$is->setKeyCache($this);
$is->setNsKey($nsKey);
$is->setItemKey($itemKey);
if (isset($writeThrough)) {
$is->setWriteThroughStream($writeThrough);
}
return $is;
}
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @return string
*/
public function getString($nsKey, $itemKey)
{
$this->_prepareCache($nsKey);
if ($this->hasKey($nsKey, $itemKey)) {
return $this->_contents[$nsKey][$itemKey];
}
}
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
{
$this->_prepareCache($nsKey);
$is->write($this->getString($nsKey, $itemKey));
}
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey)
{
$this->_prepareCache($nsKey);
return array_key_exists($itemKey, $this->_contents[$nsKey]);
}
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey)
{
unset($this->_contents[$nsKey][$itemKey]);
}
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey)
{
unset($this->_contents[$nsKey]);
}
/**
* Initialize the namespace of $nsKey if needed.
*
* @param string $nsKey
*/
private function _prepareCache($nsKey)
{
if (!array_key_exists($nsKey, $this->_contents)) {
$this->_contents[$nsKey] = array();
}
}
}

View File

@@ -0,0 +1,321 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A KeyCache which streams to and from disk.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache
{
/** Signal to place pointer at start of file */
const POSITION_START = 0;
/** Signal to place pointer at end of file */
const POSITION_END = 1;
/** Signal to leave pointer in whatever position it currently is */
const POSITION_CURRENT = 2;
/**
* An InputStream for cloning.
*
* @var Swift_KeyCache_KeyCacheInputStream
*/
private $_stream;
/**
* A path to write to.
*
* @var string
*/
private $_path;
/**
* Stored keys.
*
* @var array
*/
private $_keys = array();
/**
* Will be true if magic_quotes_runtime is turned on.
*
* @var bool
*/
private $_quotes = false;
/**
* Create a new DiskKeyCache with the given $stream for cloning to make
* InputByteStreams, and the given $path to save to.
*
* @param Swift_KeyCache_KeyCacheInputStream $stream
* @param string $path to save to
*/
public function __construct(Swift_KeyCache_KeyCacheInputStream $stream, $path)
{
$this->_stream = $stream;
$this->_path = $path;
if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
$this->_quotes = true;
}
}
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*
* @throws Swift_IoException
*/
public function setString($nsKey, $itemKey, $string, $mode)
{
$this->_prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START);
break;
case self::MODE_APPEND:
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END);
break;
default:
throw new Swift_SwiftException(
'Invalid mode ['.$mode.'] used to set nsKey='.
$nsKey.', itemKey='.$itemKey
);
break;
}
fwrite($fp, $string);
$this->_freeHandle($nsKey, $itemKey);
}
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_OutputByteStream $os
* @param int $mode
*
* @throws Swift_IoException
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
{
$this->_prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START);
break;
case self::MODE_APPEND:
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END);
break;
default:
throw new Swift_SwiftException(
'Invalid mode ['.$mode.'] used to set nsKey='.
$nsKey.', itemKey='.$itemKey
);
break;
}
while (false !== $bytes = $os->read(8192)) {
fwrite($fp, $bytes);
}
$this->_freeHandle($nsKey, $itemKey);
}
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $writeThrough
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
{
$is = clone $this->_stream;
$is->setKeyCache($this);
$is->setNsKey($nsKey);
$is->setItemKey($itemKey);
if (isset($writeThrough)) {
$is->setWriteThroughStream($writeThrough);
}
return $is;
}
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @throws Swift_IoException
*
* @return string
*/
public function getString($nsKey, $itemKey)
{
$this->_prepareCache($nsKey);
if ($this->hasKey($nsKey, $itemKey)) {
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START);
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 0);
}
$str = '';
while (!feof($fp) && false !== $bytes = fread($fp, 8192)) {
$str .= $bytes;
}
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 1);
}
$this->_freeHandle($nsKey, $itemKey);
return $str;
}
}
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
{
if ($this->hasKey($nsKey, $itemKey)) {
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START);
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 0);
}
while (!feof($fp) && false !== $bytes = fread($fp, 8192)) {
$is->write($bytes);
}
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 1);
}
$this->_freeHandle($nsKey, $itemKey);
}
}
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey)
{
return is_file($this->_path.'/'.$nsKey.'/'.$itemKey);
}
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey)
{
if ($this->hasKey($nsKey, $itemKey)) {
$this->_freeHandle($nsKey, $itemKey);
unlink($this->_path.'/'.$nsKey.'/'.$itemKey);
}
}
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey)
{
if (array_key_exists($nsKey, $this->_keys)) {
foreach ($this->_keys[$nsKey] as $itemKey => $null) {
$this->clearKey($nsKey, $itemKey);
}
if (is_dir($this->_path.'/'.$nsKey)) {
rmdir($this->_path.'/'.$nsKey);
}
unset($this->_keys[$nsKey]);
}
}
/**
* Initialize the namespace of $nsKey if needed.
*
* @param string $nsKey
*/
private function _prepareCache($nsKey)
{
$cacheDir = $this->_path.'/'.$nsKey;
if (!is_dir($cacheDir)) {
if (!mkdir($cacheDir)) {
throw new Swift_IoException('Failed to create cache directory '.$cacheDir);
}
$this->_keys[$nsKey] = array();
}
}
/**
* Get a file handle on the cache item.
*
* @param string $nsKey
* @param string $itemKey
* @param int $position
*
* @return resource
*/
private function _getHandle($nsKey, $itemKey, $position)
{
if (!isset($this->_keys[$nsKey][$itemKey])) {
$openMode = $this->hasKey($nsKey, $itemKey) ? 'r+b' : 'w+b';
$fp = fopen($this->_path.'/'.$nsKey.'/'.$itemKey, $openMode);
$this->_keys[$nsKey][$itemKey] = $fp;
}
if (self::POSITION_START == $position) {
fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET);
} elseif (self::POSITION_END == $position) {
fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END);
}
return $this->_keys[$nsKey][$itemKey];
}
private function _freeHandle($nsKey, $itemKey)
{
$fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT);
fclose($fp);
$this->_keys[$nsKey][$itemKey] = null;
}
/**
* Destructor.
*/
public function __destruct()
{
foreach ($this->_keys as $nsKey => $null) {
$this->clearAll($nsKey);
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Writes data to a KeyCache using a stream.
*
* @author Chris Corbyn
*/
interface Swift_KeyCache_KeyCacheInputStream extends Swift_InputByteStream
{
/**
* Set the KeyCache to wrap.
*
* @param Swift_KeyCache $keyCache
*/
public function setKeyCache(Swift_KeyCache $keyCache);
/**
* Set the nsKey which will be written to.
*
* @param string $nsKey
*/
public function setNsKey($nsKey);
/**
* Set the itemKey which will be written to.
*
* @param string $itemKey
*/
public function setItemKey($itemKey);
/**
* Specify a stream to write through for each write().
*
* @param Swift_InputByteStream $is
*/
public function setWriteThroughStream(Swift_InputByteStream $is);
/**
* Any implementation should be cloneable, allowing the clone to access a
* separate $nsKey and $itemKey.
*/
public function __clone();
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A null KeyCache that does not cache at all.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_NullKeyCache implements Swift_KeyCache
{
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*/
public function setString($nsKey, $itemKey, $string, $mode)
{
}
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_OutputByteStream $os
* @param int $mode
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
{
}
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $writeThrough
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
{
}
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @return string
*/
public function getString($nsKey, $itemKey)
{
}
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
{
}
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey)
{
return false;
}
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey)
{
}
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey)
{
}
}

View File

@@ -0,0 +1,127 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Writes data to a KeyCache using a stream.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCacheInputStream
{
/** The KeyCache being written to */
private $_keyCache;
/** The nsKey of the KeyCache being written to */
private $_nsKey;
/** The itemKey of the KeyCache being written to */
private $_itemKey;
/** A stream to write through on each write() */
private $_writeThrough = null;
/**
* Set the KeyCache to wrap.
*
* @param Swift_KeyCache $keyCache
*/
public function setKeyCache(Swift_KeyCache $keyCache)
{
$this->_keyCache = $keyCache;
}
/**
* Specify a stream to write through for each write().
*
* @param Swift_InputByteStream $is
*/
public function setWriteThroughStream(Swift_InputByteStream $is)
{
$this->_writeThrough = $is;
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
* @param Swift_InputByteStream $is optional
*/
public function write($bytes, Swift_InputByteStream $is = null)
{
$this->_keyCache->setString(
$this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND
);
if (isset($is)) {
$is->write($bytes);
}
if (isset($this->_writeThrough)) {
$this->_writeThrough->write($bytes);
}
}
/**
* Not used.
*/
public function commit()
{
}
/**
* Not used.
*/
public function bind(Swift_InputByteStream $is)
{
}
/**
* Not used.
*/
public function unbind(Swift_InputByteStream $is)
{
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*/
public function flushBuffers()
{
$this->_keyCache->clearKey($this->_nsKey, $this->_itemKey);
}
/**
* Set the nsKey which will be written to.
*
* @param string $nsKey
*/
public function setNsKey($nsKey)
{
$this->_nsKey = $nsKey;
}
/**
* Set the itemKey which will be written to.
*
* @param string $itemKey
*/
public function setItemKey($itemKey)
{
$this->_itemKey = $itemKey;
}
/**
* Any implementation should be cloneable, allowing the clone to access a
* separate $nsKey and $itemKey.
*/
public function __clone()
{
$this->_writeThrough = null;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Redundantly and rotationally uses several Transport implementations when sending.
*
* @author Chris Corbyn
*/
class Swift_LoadBalancedTransport extends Swift_Transport_LoadBalancedTransport
{
/**
* Creates a new LoadBalancedTransport with $transports.
*
* @param array $transports
*/
public function __construct($transports = array())
{
call_user_func_array(
array($this, 'Swift_Transport_LoadBalancedTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.loadbalanced')
);
$this->setTransports($transports);
}
/**
* Create a new LoadBalancedTransport instance.
*
* @param array $transports
*
* @return self
*/
public static function newInstance($transports = array())
{
return new self($transports);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Sends Messages using the mail() function.
*
* @author Chris Corbyn
*
* @deprecated since 5.4.5 (to be removed in 6.0)
*/
class Swift_MailTransport extends Swift_Transport_MailTransport
{
/**
* Create a new MailTransport, optionally specifying $extraParams.
*
* @param string $extraParams
*/
public function __construct($extraParams = '-f%s')
{
call_user_func_array(
array($this, 'Swift_Transport_MailTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.mail')
);
$this->setExtraParams($extraParams);
}
/**
* Create a new MailTransport instance.
*
* @param string $extraParams To be passed to mail()
*
* @return self
*/
public static function newInstance($extraParams = '-f%s')
{
return new self($extraParams);
}
}

View File

@@ -0,0 +1,114 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Swift Mailer class.
*
* @author Chris Corbyn
*/
class Swift_Mailer
{
/** The Transport used to send messages */
private $_transport;
/**
* Create a new Mailer using $transport for delivery.
*
* @param Swift_Transport $transport
*/
public function __construct(Swift_Transport $transport)
{
$this->_transport = $transport;
}
/**
* Create a new Mailer instance.
*
* @param Swift_Transport $transport
*
* @return self
*/
public static function newInstance(Swift_Transport $transport)
{
return new self($transport);
}
/**
* Create a new class instance of one of the message services.
*
* For example 'mimepart' would create a 'message.mimepart' instance
*
* @param string $service
*
* @return object
*/
public function createMessage($service = 'message')
{
return Swift_DependencyContainer::getInstance()
->lookup('message.'.$service);
}
/**
* Send the given Message like it would be sent in a mail client.
*
* All recipients (with the exception of Bcc) will be able to see the other
* recipients this message was sent to.
*
* Recipient/sender data will be retrieved from the Message object.
*
* The return value is the number of recipients who were accepted for
* delivery.
*
* @param Swift_Mime_Message $message
* @param array $failedRecipients An array of failures by-reference
*
* @return int The number of successful recipients. Can be 0 which indicates failure
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$failedRecipients = (array) $failedRecipients;
if (!$this->_transport->isStarted()) {
$this->_transport->start();
}
$sent = 0;
try {
$sent = $this->_transport->send($message, $failedRecipients);
} catch (Swift_RfcComplianceException $e) {
foreach ($message->getTo() as $address => $name) {
$failedRecipients[] = $address;
}
}
return $sent;
}
/**
* Register a plugin using a known unique key (e.g. myPlugin).
*
* @param Swift_Events_EventListener $plugin
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
$this->_transport->registerPlugin($plugin);
}
/**
* The Transport used to send messages.
*
* @return Swift_Transport
*/
public function getTransport()
{
return $this->_transport;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wraps a standard PHP array in an iterator.
*
* @author Chris Corbyn
*/
class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientIterator
{
/**
* The list of recipients.
*
* @var array
*/
private $_recipients = array();
/**
* Create a new ArrayRecipientIterator from $recipients.
*
* @param array $recipients
*/
public function __construct(array $recipients)
{
$this->_recipients = $recipients;
}
/**
* Returns true only if there are more recipients to send to.
*
* @return bool
*/
public function hasNext()
{
return !empty($this->_recipients);
}
/**
* Returns an array where the keys are the addresses of recipients and the
* values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL).
*
* @return array
*/
public function nextRecipient()
{
return array_splice($this->_recipients, 0, 1);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides an abstract way of specifying recipients for batch sending.
*
* @author Chris Corbyn
*/
interface Swift_Mailer_RecipientIterator
{
/**
* Returns true only if there are more recipients to send to.
*
* @return bool
*/
public function hasNext();
/**
* Returns an array where the keys are the addresses of recipients and the
* values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL).
*
* @return array
*/
public function nextRecipient();
}

View File

@@ -0,0 +1,110 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2011 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Stores Messages in memory.
*
* @author Fabien Potencier
*/
class Swift_MemorySpool implements Swift_Spool
{
protected $messages = array();
private $flushRetries = 3;
/**
* Tests if this Transport mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return true;
}
/**
* Starts this Transport mechanism.
*/
public function start()
{
}
/**
* Stops this Transport mechanism.
*/
public function stop()
{
}
/**
* @param int $retries
*/
public function setFlushRetries($retries)
{
$this->flushRetries = $retries;
}
/**
* Stores a message in the queue.
*
* @param Swift_Mime_Message $message The message to store
*
* @return bool Whether the operation has succeeded
*/
public function queueMessage(Swift_Mime_Message $message)
{
//clone the message to make sure it is not changed while in the queue
$this->messages[] = clone $message;
return true;
}
/**
* Sends messages using the given transport instance.
*
* @param Swift_Transport $transport A transport instance
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function flushQueue(Swift_Transport $transport, &$failedRecipients = null)
{
if (!$this->messages) {
return 0;
}
if (!$transport->isStarted()) {
$transport->start();
}
$count = 0;
$retries = $this->flushRetries;
while ($retries--) {
try {
while ($message = array_pop($this->messages)) {
$count += $transport->send($message, $failedRecipients);
}
} catch (Swift_TransportException $exception) {
if ($retries) {
// re-queue the message at the end of the queue to give a chance
// to the other messages to be sent, in case the failure was due to
// this message and not just the transport failing
array_unshift($this->messages, $message);
// wait half a second before we try again
usleep(500000);
} else {
throw $exception;
}
}
}
return $count;
}
}

View File

@@ -0,0 +1,289 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Message class for building emails.
*
* @author Chris Corbyn
*/
class Swift_Message extends Swift_Mime_SimpleMessage
{
/**
* @var Swift_Signers_HeaderSigner[]
*/
private $headerSigners = array();
/**
* @var Swift_Signers_BodySigner[]
*/
private $bodySigners = array();
/**
* @var array
*/
private $savedMessage = array();
/**
* Create a new Message.
*
* Details may be optionally passed into the constructor.
*
* @param string $subject
* @param string $body
* @param string $contentType
* @param string $charset
*/
public function __construct($subject = null, $body = null, $contentType = null, $charset = null)
{
call_user_func_array(
array($this, 'Swift_Mime_SimpleMessage::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.message')
);
if (!isset($charset)) {
$charset = Swift_DependencyContainer::getInstance()
->lookup('properties.charset');
}
$this->setSubject($subject);
$this->setBody($body);
$this->setCharset($charset);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new Message.
*
* @param string $subject
* @param string $body
* @param string $contentType
* @param string $charset
*
* @return $this
*/
public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null)
{
return new self($subject, $body, $contentType, $charset);
}
/**
* Add a MimePart to this Message.
*
* @param string|Swift_OutputByteStream $body
* @param string $contentType
* @param string $charset
*
* @return $this
*/
public function addPart($body, $contentType = null, $charset = null)
{
return $this->attach(Swift_MimePart::newInstance($body, $contentType, $charset)->setEncoder($this->getEncoder()));
}
/**
* Detach a signature handler from a message.
*
* @param Swift_Signer $signer
*
* @return $this
*/
public function attachSigner(Swift_Signer $signer)
{
if ($signer instanceof Swift_Signers_HeaderSigner) {
$this->headerSigners[] = $signer;
} elseif ($signer instanceof Swift_Signers_BodySigner) {
$this->bodySigners[] = $signer;
}
return $this;
}
/**
* Attach a new signature handler to the message.
*
* @param Swift_Signer $signer
*
* @return $this
*/
public function detachSigner(Swift_Signer $signer)
{
if ($signer instanceof Swift_Signers_HeaderSigner) {
foreach ($this->headerSigners as $k => $headerSigner) {
if ($headerSigner === $signer) {
unset($this->headerSigners[$k]);
return $this;
}
}
} elseif ($signer instanceof Swift_Signers_BodySigner) {
foreach ($this->bodySigners as $k => $bodySigner) {
if ($bodySigner === $signer) {
unset($this->bodySigners[$k]);
return $this;
}
}
}
return $this;
}
/**
* Get this message as a complete string.
*
* @return string
*/
public function toString()
{
if (empty($this->headerSigners) && empty($this->bodySigners)) {
return parent::toString();
}
$this->saveMessage();
$this->doSign();
$string = parent::toString();
$this->restoreMessage();
return $string;
}
/**
* Write this message to a {@link Swift_InputByteStream}.
*
* @param Swift_InputByteStream $is
*/
public function toByteStream(Swift_InputByteStream $is)
{
if (empty($this->headerSigners) && empty($this->bodySigners)) {
parent::toByteStream($is);
return;
}
$this->saveMessage();
$this->doSign();
parent::toByteStream($is);
$this->restoreMessage();
}
public function __wakeup()
{
Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message');
}
/**
* loops through signers and apply the signatures.
*/
protected function doSign()
{
foreach ($this->bodySigners as $signer) {
$altered = $signer->getAlteredHeaders();
$this->saveHeaders($altered);
$signer->signMessage($this);
}
foreach ($this->headerSigners as $signer) {
$altered = $signer->getAlteredHeaders();
$this->saveHeaders($altered);
$signer->reset();
$signer->setHeaders($this->getHeaders());
$signer->startBody();
$this->_bodyToByteStream($signer);
$signer->endBody();
$signer->addSignature($this->getHeaders());
}
}
/**
* save the message before any signature is applied.
*/
protected function saveMessage()
{
$this->savedMessage = array('headers' => array());
$this->savedMessage['body'] = $this->getBody();
$this->savedMessage['children'] = $this->getChildren();
if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') {
$this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children']));
$this->setBody('');
}
}
/**
* save the original headers.
*
* @param array $altered
*/
protected function saveHeaders(array $altered)
{
foreach ($altered as $head) {
$lc = strtolower($head);
if (!isset($this->savedMessage['headers'][$lc])) {
$this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head);
}
}
}
/**
* Remove or restore altered headers.
*/
protected function restoreHeaders()
{
foreach ($this->savedMessage['headers'] as $name => $savedValue) {
$headers = $this->getHeaders()->getAll($name);
foreach ($headers as $key => $value) {
if (!isset($savedValue[$key])) {
$this->getHeaders()->remove($name, $key);
}
}
}
}
/**
* Restore message body.
*/
protected function restoreMessage()
{
$this->setBody($this->savedMessage['body']);
$this->setChildren($this->savedMessage['children']);
$this->restoreHeaders();
$this->savedMessage = array();
}
/**
* Clone Message Signers.
*
* @see Swift_Mime_SimpleMimeEntity::__clone()
*/
public function __clone()
{
parent::__clone();
foreach ($this->bodySigners as $key => $bodySigner) {
$this->bodySigners[$key] = clone $bodySigner;
}
foreach ($this->headerSigners as $key => $headerSigner) {
$this->headerSigners[$key] = clone $headerSigner;
}
}
}

View File

@@ -0,0 +1,149 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An attachment, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity
{
/** Recognized MIME types */
private $_mimeTypes = array();
/**
* Create a new Attachment with $headers, $encoder and $cache.
*
* @param Swift_Mime_HeaderSet $headers
* @param Swift_Mime_ContentEncoder $encoder
* @param Swift_KeyCache $cache
* @param Swift_Mime_Grammar $grammar
* @param array $mimeTypes optional
*/
public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $mimeTypes = array())
{
parent::__construct($headers, $encoder, $cache, $grammar);
$this->setDisposition('attachment');
$this->setContentType('application/octet-stream');
$this->_mimeTypes = $mimeTypes;
}
/**
* Get the nesting level used for this attachment.
*
* Always returns {@link LEVEL_MIXED}.
*
* @return int
*/
public function getNestingLevel()
{
return self::LEVEL_MIXED;
}
/**
* Get the Content-Disposition of this attachment.
*
* By default attachments have a disposition of "attachment".
*
* @return string
*/
public function getDisposition()
{
return $this->_getHeaderFieldModel('Content-Disposition');
}
/**
* Set the Content-Disposition of this attachment.
*
* @param string $disposition
*
* @return $this
*/
public function setDisposition($disposition)
{
if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) {
$this->getHeaders()->addParameterizedHeader('Content-Disposition', $disposition);
}
return $this;
}
/**
* Get the filename of this attachment when downloaded.
*
* @return string
*/
public function getFilename()
{
return $this->_getHeaderParameter('Content-Disposition', 'filename');
}
/**
* Set the filename of this attachment.
*
* @param string $filename
*
* @return $this
*/
public function setFilename($filename)
{
$this->_setHeaderParameter('Content-Disposition', 'filename', $filename);
$this->_setHeaderParameter('Content-Type', 'name', $filename);
return $this;
}
/**
* Get the file size of this attachment.
*
* @return int
*/
public function getSize()
{
return $this->_getHeaderParameter('Content-Disposition', 'size');
}
/**
* Set the file size of this attachment.
*
* @param int $size
*
* @return $this
*/
public function setSize($size)
{
$this->_setHeaderParameter('Content-Disposition', 'size', $size);
return $this;
}
/**
* Set the file that this attachment is for.
*
* @param Swift_FileStream $file
* @param string $contentType optional
*
* @return $this
*/
public function setFile(Swift_FileStream $file, $contentType = null)
{
$this->setFilename(basename($file->getPath()));
$this->setBody($file, $contentType);
if (!isset($contentType)) {
$extension = strtolower(substr($file->getPath(), strrpos($file->getPath(), '.') + 1));
if (array_key_exists($extension, $this->_mimeTypes)) {
$this->setContentType($this->_mimeTypes[$extension]);
}
}
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Observes changes in an Mime entity's character set.
*
* @author Chris Corbyn
*/
interface Swift_Mime_CharsetObserver
{
/**
* Notify this observer that the entity's charset has changed.
*
* @param string $charset
*/
public function charsetChanged($charset);
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Transfer Encoding schemes.
*
* @author Chris Corbyn
*/
interface Swift_Mime_ContentEncoder extends Swift_Encoder
{
/**
* Encode $in to $out.
*
* @param Swift_OutputByteStream $os to read from
* @param Swift_InputByteStream $is to write to
* @param int $firstLineOffset
* @param int $maxLineLength - 0 indicates the default length for this encoding
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0);
/**
* Get the MIME name of this content encoding scheme.
*
* @return string
*/
public function getName();
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base 64 Transfer Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Mime_ContentEncoder_Base64ContentEncoder extends Swift_Encoder_Base64Encoder implements Swift_Mime_ContentEncoder
{
/**
* Encode stream $in to stream $out.
*
* @param Swift_OutputByteStream $os
* @param Swift_InputByteStream $is
* @param int $firstLineOffset
* @param int $maxLineLength, optional, 0 indicates the default of 76 bytes
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
if (0 >= $maxLineLength || 76 < $maxLineLength) {
$maxLineLength = 76;
}
$remainder = 0;
$base64ReadBufferRemainderBytes = null;
// To reduce memory usage, the output buffer is streamed to the input buffer like so:
// Output Stream => base64encode => wrap line length => Input Stream
// HOWEVER it's important to note that base64_encode() should only be passed whole triplets of data (except for the final chunk of data)
// otherwise it will assume the input data has *ended* and it will incorrectly pad/terminate the base64 data mid-stream.
// We use $base64ReadBufferRemainderBytes to carry over 1-2 "remainder" bytes from the each chunk from OutputStream and pre-pend those onto the
// chunk of bytes read in the next iteration.
// When the OutputStream is empty, we must flush any remainder bytes.
while (true) {
$readBytes = $os->read(8192);
$atEOF = ($readBytes === false);
if ($atEOF) {
$streamTheseBytes = $base64ReadBufferRemainderBytes;
} else {
$streamTheseBytes = $base64ReadBufferRemainderBytes.$readBytes;
}
$base64ReadBufferRemainderBytes = null;
$bytesLength = strlen($streamTheseBytes);
if ($bytesLength === 0) { // no data left to encode
break;
}
// if we're not on the last block of the ouput stream, make sure $streamTheseBytes ends with a complete triplet of data
// and carry over remainder 1-2 bytes to the next loop iteration
if (!$atEOF) {
$excessBytes = $bytesLength % 3;
if ($excessBytes !== 0) {
$base64ReadBufferRemainderBytes = substr($streamTheseBytes, -$excessBytes);
$streamTheseBytes = substr($streamTheseBytes, 0, $bytesLength - $excessBytes);
}
}
$encoded = base64_encode($streamTheseBytes);
$encodedTransformed = '';
$thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset;
while ($thisMaxLineLength < strlen($encoded)) {
$encodedTransformed .= substr($encoded, 0, $thisMaxLineLength)."\r\n";
$firstLineOffset = 0;
$encoded = substr($encoded, $thisMaxLineLength);
$thisMaxLineLength = $maxLineLength;
$remainder = 0;
}
if (0 < $remainingLength = strlen($encoded)) {
$remainder += $remainingLength;
$encodedTransformed .= $encoded;
$encoded = null;
}
$is->write($encodedTransformed);
if ($atEOF) {
break;
}
}
}
/**
* Get the name of this encoding scheme.
* Returns the string 'base64'.
*
* @return string
*/
public function getName()
{
return 'base64';
}
}

Some files were not shown because too many files have changed in this diff Show More