Updated to Symfony 2.1 BETA3

This commit is contained in:
Polonkai Gergely 2012-07-16 21:40:19 +02:00
parent 7a06301624
commit 9d0d2ce524
1551 changed files with 157774 additions and 5177 deletions

View File

@ -431,8 +431,8 @@ class SymfonyRequirements extends RequirementCollection
if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) {
$this->addRequirement(
(in_array(date_default_timezone_get(), DateTimeZone::listIdentifiers())),
sprintf('Default timezone is deprecated (%s)', date_default_timezone_get()),
'Fix your <strong>php.ini</strong> file (list of deprecated timezones http://us.php.net/manual/en/timezones.others.php).'
sprintf('Default timezone "%s" is not supported by your installation of PHP', date_default_timezone_get()),
'Fix your <strong>php.ini</strong> file (check for typos and have a look at the list of deprecated timezones http://php.net/manual/en/timezones.others.php).'
);
}
@ -492,6 +492,12 @@ class SymfonyRequirements extends RequirementCollection
/* optional recommendations follow */
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.4', '>='),
sprintf('Your project might not work properly ("Notice: Trying to get property of non-object") due to the PHP bug #52083 before PHP 5.3.4 (%s installed)', $installedPhpVersion),
'Install PHP 5.3.4 or newer'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.8', '>='),
sprintf('Annotations might not work properly due to the PHP bug #55156 before PHP 5.3.8 (%s installed)', $installedPhpVersion),

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\DependencyInjection
interface ContainerAwareInterface
{
function setContainer(ContainerInterface $container = null);
public function setContainer(ContainerInterface $container = null);
}
}
@ -35,37 +35,37 @@ interface ContainerInterface
const SCOPE_PROTOTYPE = 'prototype';
function set($id, $service, $scope = self::SCOPE_CONTAINER);
public function set($id, $service, $scope = self::SCOPE_CONTAINER);
function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE);
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE);
function has($id);
public function has($id);
function getParameter($name);
public function getParameter($name);
function hasParameter($name);
public function hasParameter($name);
function setParameter($name, $value);
public function setParameter($name, $value);
function enterScope($name);
public function enterScope($name);
function leaveScope($name);
public function leaveScope($name);
function addScope(ScopeInterface $scope);
public function addScope(ScopeInterface $scope);
function hasScope($name);
public function hasScope($name);
function isScopeActive($name);
public function isScopeActive($name);
}
}
@ -79,7 +79,7 @@ namespace Symfony\Component\DependencyInjection
interface IntrospectableContainerInterface extends ContainerInterface
{
function initialized($id);
public function initialized($id);
}
}
@ -340,13 +340,13 @@ class Container implements IntrospectableContainerInterface
}
static public function camelize($id)
public static function camelize($id)
{
return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $id);
}
static public function underscore($id)
public static function underscore($id)
{
return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.')));
}
@ -356,23 +356,6 @@ class Container implements IntrospectableContainerInterface
namespace Symfony\Component\HttpKernel
{
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
interface TerminableInterface
{
function terminate(Request $request, Response $response);
}
}
namespace Symfony\Component\HttpKernel
{
@ -386,7 +369,7 @@ interface HttpKernelInterface
const SUB_REQUEST = 2;
function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
}
}
@ -405,55 +388,72 @@ use Symfony\Component\Config\Loader\LoaderInterface;
interface KernelInterface extends HttpKernelInterface, \Serializable
{
function registerBundles();
public function registerBundles();
function registerContainerConfiguration(LoaderInterface $loader);
public function registerContainerConfiguration(LoaderInterface $loader);
function boot();
public function boot();
function shutdown();
public function shutdown();
function getBundles();
public function getBundles();
function isClassInActiveBundle($class);
public function isClassInActiveBundle($class);
function getBundle($name, $first = true);
public function getBundle($name, $first = true);
function locateResource($name, $dir = null, $first = true);
public function locateResource($name, $dir = null, $first = true);
function getName();
public function getName();
function getEnvironment();
public function getEnvironment();
function isDebug();
public function isDebug();
function getRootDir();
public function getRootDir();
function getContainer();
public function getContainer();
function getStartTime();
public function getStartTime();
function getCacheDir();
public function getCacheDir();
function getLogDir();
public function getLogDir();
function getCharset();
public function getCharset();
}
}
namespace Symfony\Component\HttpKernel
{
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
interface TerminableInterface
{
public function terminate(Request $request, Response $response);
}
}
@ -502,12 +502,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $classes;
protected $errorReportingLevel;
const VERSION = '2.1.0-BETA2';
const VERSION = '2.1.0-DEV';
const VERSION_ID = '20100';
const MAJOR_VERSION = '2';
const MINOR_VERSION = '1';
const RELEASE_VERSION = '0';
const EXTRA_VERSION = 'BETA';
const EXTRA_VERSION = 'DEV';
public function __construct($environment, $debug)
@ -516,7 +516,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$this->debug = (Boolean) $debug;
$this->booted = false;
$this->rootDir = $this->getRootDir();
$this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
$this->name = $this->getName();
$this->classes = array();
if ($this->debug) {
@ -706,6 +706,10 @@ abstract class Kernel implements KernelInterface, TerminableInterface
public function getName()
{
if (null === $this->name) {
$this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
}
return $this->name;
}
@ -975,7 +979,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
}
static public function stripComments($source)
public static function stripComments($source)
{
if (!function_exists('token_get_all')) {
return $source;
@ -1084,28 +1088,28 @@ use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
interface BundleInterface extends ContainerAwareInterface
{
function boot();
public function boot();
function shutdown();
public function shutdown();
function build(ContainerBuilder $container);
public function build(ContainerBuilder $container);
function getContainerExtension();
public function getContainerExtension();
function getParent();
public function getParent();
function getName();
public function getName();
function getNamespace();
public function getNamespace();
function getPath();
public function getPath();
}
}
@ -1348,6 +1352,7 @@ namespace Symfony\Component\HttpKernel
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@ -1450,14 +1455,27 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
$event = new GetResponseForExceptionEvent($this, $request, $type, $e);
$this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event);
$e = $event->getException();
if (!$event->hasResponse()) {
throw $e;
}
$response = $event->getResponse();
if (!$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {
if ($e instanceof HttpExceptionInterface) {
$response->setStatusCode($e->getStatusCode());
$response->headers->add($e->getHeaders());
} else {
$response->setStatusCode(500);
}
}
try {
return $this->filterResponse($event->getResponse(), $request, $type);
return $this->filterResponse($response, $request, $type);
} catch (\Exception $e) {
return $event->getResponse();
return $response;
}
}

View File

@ -17,7 +17,8 @@
"sensio/framework-extra-bundle": "dev-master",
"sensio/generator-bundle": "dev-master",
"jms/security-extra-bundle": "1.1.*",
"jms/di-extra-bundle": "1.0.*"
"jms/di-extra-bundle": "1.0.*",
"egeloen/ckeditor-bundle": "2.0.*"
},
"scripts": {
"post-install-cmd": [

59
composer.lock generated
View File

@ -1,5 +1,5 @@
{
"hash": "ab7ccf177098603cace942212179431f",
"hash": "0890891a613a13d9980307e458190353",
"packages": [
{
"package": "doctrine/common",
@ -8,20 +8,23 @@
{
"package": "doctrine/dbal",
"version": "2.2.x-dev",
"source-reference": "b961a3fce6bf220f1dca47d7d747b9074bea4730",
"commit-date": "1341779435"
"source-reference": "b961a3fce6bf220f1dca47d7d747b9074bea4730"
},
{
"package": "doctrine/doctrine-bundle",
"version": "dev-master",
"source-reference": "c9ea46d1f0c48bb88bb87b44214fe44e03c0c692",
"commit-date": "1341405737"
"source-reference": "c9ea46d1f0c48bb88bb87b44214fe44e03c0c692"
},
{
"package": "doctrine/orm",
"version": "2.2.x-dev",
"source-reference": "5d2a3bcb3b467f41ee58575764f3ba84937f76e4",
"commit-date": "1341676080"
"source-reference": "5d2a3bcb3b467f41ee58575764f3ba84937f76e4"
},
{
"package": "egeloen/ckeditor-bundle",
"version": "2.0.x-dev",
"source-reference": "ca9d4a631577d7195c6517dd89ae8a3cc02b85ba",
"commit-date": "1337272707"
},
{
"package": "jms/aop-bundle",
@ -52,8 +55,7 @@
{
"package": "kriswallsmith/assetic",
"version": "dev-master",
"source-reference": "d6f89a3170c5280ad554347dc113eb25fdf00ad7",
"commit-date": "1339515714"
"source-reference": "d6f89a3170c5280ad554347dc113eb25fdf00ad7"
},
{
"package": "monolog/monolog",
@ -62,20 +64,20 @@
{
"package": "sensio/distribution-bundle",
"version": "dev-master",
"source-reference": "9a7dbd867fd5061e4bfd660a175aa66122f53d25",
"commit-date": "1341741741"
"source-reference": "5886adae1613c0a72fbb95259a83ae798e86c0d3",
"commit-date": "1342347850"
},
{
"package": "sensio/framework-extra-bundle",
"version": "dev-master",
"source-reference": "4f54e5d5fb3b54fb107892684018f3704934c48d",
"commit-date": "1341126219"
"source-reference": "e9ac8f1a911ed29e30296c7f1549f53d4c94eddf",
"commit-date": "1342084432"
},
{
"package": "sensio/generator-bundle",
"version": "dev-master",
"source-reference": "43ed45c48db18e4a0e48aec0c098f42e56e22d36",
"commit-date": "1340138445"
"source-reference": "c17dbddefe517e3adfcbeaa1ab7cef3e572d29c6",
"commit-date": "1342195488"
},
{
"package": "swiftmailer/swiftmailer",
@ -86,25 +88,25 @@
{
"package": "swiftmailer/swiftmailer",
"version": "dev-master",
"source-reference": "f51b5f33c83b48faea75f1285f99a2e8f1c66f75",
"commit-date": "1341746460"
"source-reference": "8b2aa953f87da228ba413e8fb1372e49c1374050",
"commit-date": "1342198037"
},
{
"package": "symfony/assetic-bundle",
"version": "dev-master",
"source-reference": "8fe7b898b08103c1d6fce64c3e279a7afd61adfc",
"commit-date": "1340234971"
"source-reference": "ed933dcfa45f00b6bc6d7727007403f3ff429e5a",
"commit-date": "1342126277"
},
{
"package": "symfony/monolog-bundle",
"version": "dev-master",
"source-reference": "7fe7f711bb04b86ad7f45a9e11a7f8cbaf9bc1a5",
"source-reference": "v2.1.0-BETA3",
"commit-date": "1341078487"
},
{
"package": "symfony/swiftmailer-bundle",
"version": "dev-master",
"source-reference": "e1d413ce27fd1696bdc82ad9525f1b874664530e",
"source-reference": "v2.1.0-BETA3",
"commit-date": "1341509614"
},
{
@ -116,14 +118,13 @@
{
"package": "symfony/symfony",
"version": "dev-master",
"source-reference": "v2.1.0-BETA2",
"commit-date": "1341820358"
"source-reference": "151b79a6ce3f459e2fa8815cd9f2786eef750993",
"commit-date": "1342376835"
},
{
"package": "twig/extensions",
"version": "dev-master",
"source-reference": "feb6d3f10c411e2631997c0a905aa581c80305c1",
"commit-date": "1337599699"
"source-reference": "feb6d3f10c411e2631997c0a905aa581c80305c1"
},
{
"package": "twig/twig",
@ -134,13 +135,11 @@
{
"package": "twig/twig",
"version": "dev-master",
"source-reference": "26eb0a2653eade50dffdd31b11ca454232dea8cf",
"commit-date": "1341423205"
"source-reference": "d45664045194ef62573c153c424508527105041e",
"commit-date": "1342424564"
}
],
"packages-dev": [
],
"packages-dev": null,
"aliases": [
],

BIN
composer.phar Executable file

Binary file not shown.

View File

@ -21,6 +21,7 @@ return array(
'JMS\\SecurityExtraBundle' => $vendorDir . '/jms/security-extra-bundle/',
'JMS\\DiExtraBundle' => $vendorDir . '/jms/di-extra-bundle/',
'JMS\\AopBundle' => $vendorDir . '/jms/aop-bundle/',
'Ivory\\CKEditorBundle' => $vendorDir . '/egeloen/ckeditor-bundle/',
'Doctrine\\ORM' => $vendorDir . '/doctrine/orm/lib/',
'Doctrine\\DBAL' => $vendorDir . '/doctrine/dbal/lib/',
'Doctrine\\Common' => $vendorDir . '/doctrine/common/lib/',

View File

@ -3,7 +3,7 @@
"name": "jms/metadata",
"version": "1.1.1",
"version_normalized": "1.1.1.0",
"time": "2011-12-30 10:32:49",
"time": "2011-12-29 22:32:49",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/metadata",
@ -47,7 +47,7 @@
"name": "jms/cg",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"time": "2011-12-30 09:40:52",
"time": "2011-12-29 21:40:52",
"source": {
"type": "git",
"url": "git://github.com/schmittjoh/cg-library.git",
@ -89,7 +89,7 @@
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"target-dir": "JMS/AopBundle",
"time": "2011-12-30 09:50:26",
"time": "2011-12-29 21:50:26",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/JMSAopBundle",
@ -133,7 +133,7 @@
"version": "1.1.0",
"version_normalized": "1.1.0.0",
"target-dir": "JMS/SecurityExtraBundle",
"time": "2011-12-30 13:38:12",
"time": "2011-12-30 01:38:12",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/JMSSecurityExtraBundle",
@ -178,7 +178,7 @@
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"target-dir": "JMS/DiExtraBundle",
"time": "2012-02-25 05:01:54",
"time": "2012-02-24 17:01:54",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/JMSDiExtraBundle",
@ -221,7 +221,7 @@
"name": "doctrine/common",
"version": "2.2.2",
"version_normalized": "2.2.2.0",
"time": "2012-04-08 03:46:44",
"time": "2012-04-07 03:46:44",
"source": {
"type": "git",
"url": "https://github.com/doctrine/common",
@ -287,7 +287,7 @@
"name": "monolog/monolog",
"version": "1.1.0",
"version_normalized": "1.1.0.0",
"time": "2012-04-18 14:27:40",
"time": "2012-04-17 14:27:40",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
@ -337,7 +337,7 @@
"name": "twig/extensions",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-05-16 21:28:19",
"time": "2012-05-15 21:28:19",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig-extensions",
@ -382,7 +382,7 @@
"name": "kriswallsmith/assetic",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-06-08 01:41:54",
"time": "2012-06-07 01:41:54",
"source": {
"type": "git",
"url": "http://github.com/kriswallsmith/assetic.git",
@ -437,285 +437,12 @@
}
}
},
{
"name": "sensio/generator-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Sensio/Bundle/GeneratorBundle",
"time": "2012-06-15 18:40:45",
"source": {
"type": "git",
"url": "https://github.com/sensio/SensioGeneratorBundle",
"reference": "43ed45c48db18e4a0e48aec0c098f42e56e22d36"
},
"dist": {
"type": "zip",
"url": "https://github.com/sensio/SensioGeneratorBundle/zipball/43ed45c48db18e4a0e48aec0c098f42e56e22d36",
"reference": "43ed45c48db18e4a0e48aec0c098f42e56e22d36",
"shasum": ""
},
"require": {
"symfony/framework-bundle": "2.1.*"
},
"require-dev": {
"symfony/doctrine-bridge": "2.1.*",
"doctrine/orm": ">=2.1,<2.4-dev",
"twig/twig": ">=1.8,<2.0-dev"
},
"suggest": {
"doctrine/doctrine-bundle": "to generate entities and their crud controller"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
}
],
"description": "This bundle generates code for you",
"autoload": {
"psr-0": {
"Sensio\\Bundle\\GeneratorBundle": ""
}
}
},
{
"name": "symfony/assetic-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Symfony/Bundle/AsseticBundle",
"time": "2012-06-16 23:29:31",
"source": {
"type": "git",
"url": "https://github.com/symfony/AsseticBundle",
"reference": "8fe7b898b08103c1d6fce64c3e279a7afd61adfc"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/AsseticBundle/zipball/8fe7b898b08103c1d6fce64c3e279a7afd61adfc",
"reference": "8fe7b898b08103c1d6fce64c3e279a7afd61adfc",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/framework-bundle": "2.1.*",
"kriswallsmith/assetic": "1.1.*"
},
"suggest": {
"symfony/twig-bundle": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Kris Wallsmith",
"email": "kris.wallsmith@gmail.com",
"homepage": "http://kriswallsmith.net/",
"role": null
}
],
"description": "Integrates Assetic into Symfony2",
"homepage": "https://github.com/symfony/AsseticBundle",
"keywords": [
"assets",
"compression",
"minification"
],
"autoload": {
"psr-0": {
"Symfony\\Bundle\\AsseticBundle": ""
}
}
},
{
"name": "symfony/monolog-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Symfony/Bundle/MonologBundle",
"time": "2012-06-28 15:48:07",
"source": {
"type": "git",
"url": "https://github.com/symfony/MonologBundle",
"reference": "7fe7f711bb04b86ad7f45a9e11a7f8cbaf9bc1a5"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/MonologBundle/zipball/7fe7f711bb04b86ad7f45a9e11a7f8cbaf9bc1a5",
"reference": "7fe7f711bb04b86ad7f45a9e11a7f8cbaf9bc1a5",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"monolog/monolog": "1.*",
"symfony/monolog-bridge": "2.1.*",
"symfony/dependency-injection": "2.1.*",
"symfony/config": "2.1.*"
},
"require-dev": {
"symfony/yaml": "2.1.*",
"symfony/config": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Symfony Community",
"email": null,
"homepage": "http://symfony.com/contributors",
"role": null
}
],
"description": "Symfony MonologBundle",
"homepage": "http://symfony.com",
"autoload": {
"psr-0": {
"Symfony\\Bundle\\MonologBundle": ""
}
}
},
{
"name": "sensio/framework-extra-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Sensio/Bundle/FrameworkExtraBundle",
"time": "2012-06-29 05:03:39",
"source": {
"type": "git",
"url": "https://github.com/sensio/SensioFrameworkExtraBundle",
"reference": "4f54e5d5fb3b54fb107892684018f3704934c48d"
},
"dist": {
"type": "zip",
"url": "https://github.com/sensio/SensioFrameworkExtraBundle/zipball/4f54e5d5fb3b54fb107892684018f3704934c48d",
"reference": "4f54e5d5fb3b54fb107892684018f3704934c48d",
"shasum": ""
},
"require": {
"doctrine/common": ">=2.1,<2.4-dev",
"symfony/framework-bundle": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
}
],
"description": "This bundle provides a way to configure your controllers with annotations",
"keywords": [
"annotations",
"controllers"
],
"autoload": {
"psr-0": {
"Sensio\\Bundle\\FrameworkExtraBundle": ""
}
}
},
{
"name": "twig/twig",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-07-03 15:33:25",
"source": {
"type": "git",
"url": "git://github.com/fabpot/Twig.git",
"reference": "26eb0a2653eade50dffdd31b11ca454232dea8cf"
},
"dist": {
"type": "zip",
"url": "https://github.com/fabpot/Twig/zipball/26eb0a2653eade50dffdd31b11ca454232dea8cf",
"reference": "26eb0a2653eade50dffdd31b11ca454232dea8cf",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"installation-source": "source",
"license": [
"BSD-3"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"homepage": null,
"role": null
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
}
},
{
"name": "doctrine/doctrine-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Doctrine/Bundle/DoctrineBundle",
"time": "2012-07-03 10:42:17",
"time": "2012-07-02 10:42:17",
"source": {
"type": "git",
"url": "git://github.com/doctrine/DoctrineBundle.git",
@ -785,70 +512,11 @@
}
}
},
{
"name": "symfony/swiftmailer-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Symfony/Bundle/SwiftmailerBundle",
"time": "2012-07-04 15:33:34",
"source": {
"type": "git",
"url": "https://github.com/symfony/SwiftmailerBundle",
"reference": "e1d413ce27fd1696bdc82ad9525f1b874664530e"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/SwiftmailerBundle/zipball/e1d413ce27fd1696bdc82ad9525f1b874664530e",
"reference": "e1d413ce27fd1696bdc82ad9525f1b874664530e",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"symfony/swiftmailer-bridge": "2.1.*",
"swiftmailer/swiftmailer": ">=4.2.0,<4.3-dev"
},
"require-dev": {
"symfony/dependency-injection": "2.1.*",
"symfony/http-kernel": "2.1.*",
"symfony/config": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Symfony Community",
"email": null,
"homepage": "http://symfony.com/contributors",
"role": null
}
],
"description": "Symfony SwiftmailerBundle",
"homepage": "http://symfony.com",
"autoload": {
"psr-0": {
"Symfony\\Bundle\\SwiftmailerBundle": ""
}
}
},
{
"name": "doctrine/orm",
"version": "2.2.x-dev",
"version_normalized": "2.2.9999999.9999999-dev",
"time": "2012-07-07 07:48:00",
"time": "2012-07-06 07:48:00",
"source": {
"type": "git",
"url": "git://github.com/doctrine/doctrine2.git",
@ -909,66 +577,11 @@
}
}
},
{
"name": "swiftmailer/swiftmailer",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-07-08 09:21:00",
"source": {
"type": "git",
"url": "git://github.com/swiftmailer/swiftmailer.git",
"reference": "f51b5f33c83b48faea75f1285f99a2e8f1c66f75"
},
"dist": {
"type": "zip",
"url": "https://github.com/swiftmailer/swiftmailer/zipball/f51b5f33c83b48faea75f1285f99a2e8f1c66f75",
"reference": "f51b5f33c83b48faea75f1285f99a2e8f1c66f75",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.2-dev"
}
},
"installation-source": "source",
"license": [
"LGPL"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Chris Corbyn",
"email": "",
"homepage": null,
"role": null
}
],
"description": "Swiftmailer, free feature-rich PHP mailer",
"homepage": "http://swiftmailer.org",
"keywords": [
"mail",
"mailer"
],
"autoload": {
"files": [
"lib/swift_required.php"
]
}
},
{
"name": "doctrine/dbal",
"version": "2.2.x-dev",
"version_normalized": "2.2.9999999.9999999-dev",
"time": "2012-07-08 18:30:35",
"time": "2012-07-07 18:30:35",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal",
@ -1030,25 +643,456 @@
}
},
{
"name": "symfony/symfony",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-07-09 05:52:38",
"name": "egeloen/ckeditor-bundle",
"version": "2.0.x-dev",
"version_normalized": "2.0.9999999.9999999-dev",
"target-dir": "Ivory/CKEditorBundle",
"time": "2012-05-16 18:38:27",
"source": {
"type": "git",
"url": "git://github.com/symfony/symfony.git",
"reference": "v2.1.0-BETA2"
"url": "https://github.com/egeloen/IvoryCKEditorBundle",
"reference": "ca9d4a631577d7195c6517dd89ae8a3cc02b85ba"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/symfony/zipball/v2.1.0-BETA2",
"reference": "v2.1.0-BETA2",
"url": "https://github.com/egeloen/IvoryCKEditorBundle/zipball/ca9d4a631577d7195c6517dd89ae8a3cc02b85ba",
"reference": "ca9d4a631577d7195c6517dd89ae8a3cc02b85ba",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/framework-bundle": "2.*"
},
"type": "symfony-bundle",
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Eric GELOEN",
"email": "geloen.eric@gmail.com",
"homepage": null,
"role": null
}
],
"description": "Provides a CKEditor integration for your Symfony2 Project.",
"keywords": [
"CKEditor"
],
"autoload": {
"psr-0": {
"Ivory\\CKEditorBundle": ""
}
}
},
{
"name": "sensio/generator-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Sensio/Bundle/GeneratorBundle",
"time": "2012-07-13 00:04:48",
"source": {
"type": "git",
"url": "https://github.com/sensio/SensioGeneratorBundle",
"reference": "c17dbddefe517e3adfcbeaa1ab7cef3e572d29c6"
},
"dist": {
"type": "zip",
"url": "https://github.com/sensio/SensioGeneratorBundle/zipball/c17dbddefe517e3adfcbeaa1ab7cef3e572d29c6",
"reference": "c17dbddefe517e3adfcbeaa1ab7cef3e572d29c6",
"shasum": ""
},
"require": {
"symfony/framework-bundle": "2.1.*"
},
"require-dev": {
"symfony/doctrine-bridge": "2.1.*",
"doctrine/orm": ">=2.1,<2.4-dev",
"twig/twig": ">=1.8,<2.0-dev"
},
"suggest": {
"doctrine/doctrine-bundle": "to generate entities and their crud controller"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
}
],
"description": "This bundle generates code for you",
"autoload": {
"psr-0": {
"Sensio\\Bundle\\GeneratorBundle": ""
}
}
},
{
"name": "symfony/assetic-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Symfony/Bundle/AsseticBundle",
"time": "2012-07-12 06:51:17",
"source": {
"type": "git",
"url": "https://github.com/symfony/AsseticBundle",
"reference": "ed933dcfa45f00b6bc6d7727007403f3ff429e5a"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/AsseticBundle/zipball/ed933dcfa45f00b6bc6d7727007403f3ff429e5a",
"reference": "ed933dcfa45f00b6bc6d7727007403f3ff429e5a",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/framework-bundle": "2.1.*",
"kriswallsmith/assetic": "1.1.*"
},
"suggest": {
"symfony/twig-bundle": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Kris Wallsmith",
"email": "kris.wallsmith@gmail.com",
"homepage": "http://kriswallsmith.net/",
"role": null
}
],
"description": "Integrates Assetic into Symfony2",
"homepage": "https://github.com/symfony/AsseticBundle",
"keywords": [
"assets",
"compression",
"minification"
],
"autoload": {
"psr-0": {
"Symfony\\Bundle\\AsseticBundle": ""
}
}
},
{
"name": "symfony/monolog-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Symfony/Bundle/MonologBundle",
"time": "2012-06-30 05:48:07",
"source": {
"type": "git",
"url": "https://github.com/symfony/MonologBundle",
"reference": "v2.1.0-BETA3"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/MonologBundle/zipball/v2.1.0-BETA3",
"reference": "v2.1.0-BETA3",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"monolog/monolog": "1.*",
"symfony/monolog-bridge": "2.1.*",
"symfony/dependency-injection": "2.1.*",
"symfony/config": "2.1.*"
},
"require-dev": {
"symfony/yaml": "2.1.*",
"symfony/config": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Symfony Community",
"email": null,
"homepage": "http://symfony.com/contributors",
"role": null
}
],
"description": "Symfony MonologBundle",
"homepage": "http://symfony.com",
"autoload": {
"psr-0": {
"Symfony\\Bundle\\MonologBundle": ""
}
}
},
{
"name": "sensio/framework-extra-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Sensio/Bundle/FrameworkExtraBundle",
"time": "2012-07-11 23:13:52",
"source": {
"type": "git",
"url": "https://github.com/sensio/SensioFrameworkExtraBundle",
"reference": "e9ac8f1a911ed29e30296c7f1549f53d4c94eddf"
},
"dist": {
"type": "zip",
"url": "https://github.com/sensio/SensioFrameworkExtraBundle/zipball/e9ac8f1a911ed29e30296c7f1549f53d4c94eddf",
"reference": "e9ac8f1a911ed29e30296c7f1549f53d4c94eddf",
"shasum": ""
},
"require": {
"doctrine/common": ">=2.1,<2.4-dev",
"symfony/framework-bundle": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
}
],
"description": "This bundle provides a way to configure your controllers with annotations",
"keywords": [
"annotations",
"controllers"
],
"autoload": {
"psr-0": {
"Sensio\\Bundle\\FrameworkExtraBundle": ""
}
}
},
{
"name": "twig/twig",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-07-15 23:42:44",
"source": {
"type": "git",
"url": "git://github.com/fabpot/Twig.git",
"reference": "d45664045194ef62573c153c424508527105041e"
},
"dist": {
"type": "zip",
"url": "https://github.com/fabpot/Twig/zipball/d45664045194ef62573c153c424508527105041e",
"reference": "d45664045194ef62573c153c424508527105041e",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"installation-source": "source",
"license": [
"BSD-3"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"homepage": null,
"role": null
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
}
},
{
"name": "symfony/swiftmailer-bundle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Symfony/Bundle/SwiftmailerBundle",
"time": "2012-07-05 11:33:34",
"source": {
"type": "git",
"url": "https://github.com/symfony/SwiftmailerBundle",
"reference": "v2.1.0-BETA3"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/SwiftmailerBundle/zipball/v2.1.0-BETA3",
"reference": "v2.1.0-BETA3",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"symfony/swiftmailer-bridge": "2.1.*",
"swiftmailer/swiftmailer": ">=4.2.0,<4.3-dev"
},
"require-dev": {
"symfony/dependency-injection": "2.1.*",
"symfony/http-kernel": "2.1.*",
"symfony/config": "2.1.*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
},
"installation-source": "source",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Symfony Community",
"email": null,
"homepage": "http://symfony.com/contributors",
"role": null
}
],
"description": "Symfony SwiftmailerBundle",
"homepage": "http://symfony.com",
"autoload": {
"psr-0": {
"Symfony\\Bundle\\SwiftmailerBundle": ""
}
}
},
{
"name": "swiftmailer/swiftmailer",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-07-13 12:47:17",
"source": {
"type": "git",
"url": "git://github.com/swiftmailer/swiftmailer.git",
"reference": "8b2aa953f87da228ba413e8fb1372e49c1374050"
},
"dist": {
"type": "zip",
"url": "https://github.com/swiftmailer/swiftmailer/zipball/8b2aa953f87da228ba413e8fb1372e49c1374050",
"reference": "8b2aa953f87da228ba413e8fb1372e49c1374050",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.2-dev"
}
},
"installation-source": "source",
"license": [
"LGPL"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": null,
"role": null
},
{
"name": "Chris Corbyn",
"email": "",
"homepage": null,
"role": null
}
],
"description": "Swiftmailer, free feature-rich PHP mailer",
"homepage": "http://swiftmailer.org",
"keywords": [
"mail",
"mailer"
],
"autoload": {
"files": [
"lib/swift_required.php"
]
}
},
{
"name": "symfony/symfony",
"version": "dev-master",
"version_normalized": "9999999-dev",
"time": "2012-07-15 16:27:15",
"source": {
"type": "git",
"url": "git://github.com/symfony/symfony.git",
"reference": "151b79a6ce3f459e2fa8815cd9f2786eef750993"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/symfony/zipball/151b79a6ce3f459e2fa8815cd9f2786eef750993",
"reference": "151b79a6ce3f459e2fa8815cd9f2786eef750993",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"twig/twig": ">=1.8,<2.0-dev",
"doctrine/common": ">2.2,<2.4-dev"
"doctrine/common": ">2.2,<2.4-dev",
"php": ">=5.3.3"
},
"replace": {
"symfony/doctrine-bridge": "self.version",
@ -1135,16 +1179,16 @@
"version": "dev-master",
"version_normalized": "9999999-dev",
"target-dir": "Sensio/Bundle/DistributionBundle",
"time": "2012-07-08 08:02:21",
"time": "2012-07-15 10:24:10",
"source": {
"type": "git",
"url": "https://github.com/sensio/SensioDistributionBundle",
"reference": "9a7dbd867fd5061e4bfd660a175aa66122f53d25"
"reference": "5886adae1613c0a72fbb95259a83ae798e86c0d3"
},
"dist": {
"type": "zip",
"url": "https://github.com/sensio/SensioDistributionBundle/zipball/9a7dbd867fd5061e4bfd660a175aa66122f53d25",
"reference": "9a7dbd867fd5061e4bfd660a175aa66122f53d25",
"url": "https://github.com/sensio/SensioDistributionBundle/zipball/5886adae1613c0a72fbb95259a83ae798e86c0d3",
"reference": "5886adae1613c0a72fbb95259a83ae798e86c0d3",
"shasum": ""
},
"require": {

@ -0,0 +1 @@
Subproject commit ca9d4a631577d7195c6517dd89ae8a3cc02b85ba

View File

@ -31,7 +31,7 @@ class ScriptHandler
return;
}
static::executeBuildBootstrap($appDir);
static::executeBuildBootstrap($appDir, $options['process-timeout']);
}
public static function clearCache($event)
@ -45,7 +45,7 @@ class ScriptHandler
return;
}
static::executeCommand($event, $appDir, 'cache:clear --no-warmup');
static::executeCommand($event, $appDir, 'cache:clear --no-warmup', $options['process-timeout']);
}
public static function installAssets($event)
@ -123,7 +123,7 @@ namespace { return \$loader; }
", substr(file_get_contents($file), 5)));
}
protected static function executeCommand($event, $appDir, $cmd)
protected static function executeCommand($event, $appDir, $cmd, $timeout = 300)
{
$php = escapeshellarg(self::getPhp());
$console = escapeshellarg($appDir.'/console');
@ -131,17 +131,17 @@ namespace { return \$loader; }
$console.= ' --ansi';
}
$process = new Process($php.' '.$console.' '.$cmd, null, null, null, 300);
$process = new Process($php.' '.$console.' '.$cmd, null, null, null, $timeout);
$process->run(function ($type, $buffer) { echo $buffer; });
}
protected static function executeBuildBootstrap($appDir)
protected static function executeBuildBootstrap($appDir, $timeout = 300)
{
$php = escapeshellarg(self::getPhp());
$cmd = escapeshellarg(__DIR__.'/../Resources/bin/build_bootstrap.php');
$appDir = escapeshellarg($appDir);
$process = new Process($php.' '.$cmd.' '.$appDir, null, null, null, 300);
$process = new Process($php.' '.$cmd.' '.$appDir, null, null, null, $timeout);
$process->run(function ($type, $buffer) { echo $buffer; });
}
@ -155,6 +155,8 @@ namespace { return \$loader; }
$options['symfony-assets-install'] = getenv('SYMFONY_ASSETS_INSTALL') ?: $options['symfony-assets-install'];
$options['process-timeout'] = $event->getComposer()->getConfig()->get('process-timeout');
return $options;
}

View File

@ -1,91 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Diff;
/**
* Computes a Diff between files (line by line).
*
* Implements the Longest common subsequence problem algorithm.
*
* @see http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Diff
{
private $diff;
public function __construct($str1, $str2)
{
$lines1 = explode("\n", $str1);
$lines2 = explode("\n", $str2);
$this->diff = $this->computeDiff($this->computeLcs($lines1, $lines2), $lines1, $lines2, count($lines1) - 1, count($lines2) - 1);
}
public function getDiff()
{
$diff = array();
for ($i = 0, $max = count($this->diff); $i < $max; $i++) {
if ('' != $this->diff[$i][0]) {
$diff[] = array('@', sprintf(' Line %s', $this->diff[$i][2]));
do {
$diff[] = $this->diff[$i++];
} while ('' != $this->diff[$i][0]);
}
}
return $diff;
}
public function computeDiff(array $c, array $lines1, array $lines2, $i, $j)
{
$diff = array();
if ($i > -1 && $j > -1 && $lines1[$i] == $lines2[$j]) {
$diff = array_merge($diff, $this->computeDiff($c, $lines1, $lines2, $i - 1, $j - 1));
$diff[] = array('', $lines1[$i], $i, $j);
} else {
if ($j > -1 && ($i == -1 || $c[$i][$j - 1] >= $c[$i - 1][$j])) {
$diff = array_merge($diff, $this->computeDiff($c, $lines1, $lines2, $i, $j - 1));
$diff[] = array('+', $lines2[$j], $i, $j);
} elseif ($i > -1 && ($j == -1 || $c[$i][$j - 1] < $c[$i - 1][$j])) {
$diff = array_merge($diff, $this->computeDiff($c, $lines1, $lines2, $i - 1, $j));
$diff[] = array('-', $lines1[$i], $i, $j);
}
}
return $diff;
}
private function computeLcs(array $lines1, array $lines2)
{
for ($i = -1, $len1 = count($lines1); $i < $len1; $i++) {
for ($j = -1, $len2 = count($lines2); $j < $len2; $j++) {
$c[$i][$j] = 0;
}
}
for ($i = 0; $i < $len1; $i++) {
for ($j = 0; $j < $len2; $j++) {
if ($lines1[$i] == $lines2[$j]) {
$c[$i][$j] = $c[$i - 1][$j - 1] + 1;
} else {
$c[$i][$j] = max($c[$i][$j - 1], $c[$i - 1][$j]);
}
}
}
return $c;
}
}

View File

@ -431,8 +431,8 @@ class SymfonyRequirements extends RequirementCollection
if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) {
$this->addRequirement(
(in_array(date_default_timezone_get(), DateTimeZone::listIdentifiers())),
sprintf('Default timezone is deprecated (%s)', date_default_timezone_get()),
'Fix your <strong>php.ini</strong> file (list of deprecated timezones http://us.php.net/manual/en/timezones.others.php).'
sprintf('Default timezone "%s" is not supported by your installation of PHP', date_default_timezone_get()),
'Fix your <strong>php.ini</strong> file (check for typos and have a look at the list of deprecated timezones http://php.net/manual/en/timezones.others.php).'
);
}
@ -492,6 +492,12 @@ class SymfonyRequirements extends RequirementCollection
/* optional recommendations follow */
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.4', '>='),
sprintf('Your project might not work properly ("Notice: Trying to get property of non-object") due to the PHP bug #52083 before PHP 5.3.4 (%s installed)', $installedPhpVersion),
'Install PHP 5.3.4 or newer'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.8', '>='),
sprintf('Annotations might not work properly due to the PHP bug #55156 before PHP 5.3.8 (%s installed)', $installedPhpVersion),

View File

@ -1,59 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Upgrade;
use Sensio\Bundle\DistributionBundle\Diff\Diff;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Upgrade class.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Upgrade
{
public function outputConsoleDiff(OutputInterface $output, $file1, $file2)
{
if (is_file($file1)) {
$file1 = realpath($file1);
$str1 = file_get_contents($file1);
} else {
$str1 = '';
}
if (!is_file($file2)) {
throw new \RuntimeException(sprintf('The skeleton file "%s" does not exist.', $file2));
}
$file2 = realpath($file2);
$str2 = file_get_contents($file2);
$diff = new Diff($str1, $str2);
$output->writeln(sprintf('--- %s', $file1));
$output->writeln(sprintf('+++ %s', $file2));
foreach ($diff->getDiff() as $line) {
if ('+' == $line[0]) {
$format = '<fg=green>+%s</>';
} elseif ('-' == $line[0]) {
$format = '<fg=red>-%s</>';
} elseif ('@' == $line[0]) {
$format = '<fg=cyan>@%s</>';
} else {
$format = ' %s';
}
$output->writeln(sprintf($format, $line[1]));
}
$output->writeln('');
}
}

View File

@ -0,0 +1,4 @@
coverage
phpunit.xml
vendor
composer.lock

View File

@ -0,0 +1,6 @@
CHANGELOG for 2.1.x
===================
* added the possibility to configure the id name for the Doctrine converter via the id option
* [BC break] The ParamConverterInterface::apply() method now must return a
Boolean value indicating if a conversion was done.

View File

@ -74,6 +74,19 @@ option::
{
}
This also allows you to have multiple converters in one action::
/**
* @Route("/blog/{id}/comments/{comment_id}")
* @ParamConverter("comment", class="SensioBlogBundle:Comment", options={"id" = "comment_id"})
*/
public function showAction(Post $post, Comment $comment)
{
}
In the example above, the post parameter is handled automatically, but the comment is
configured with the annotation since they can not both follow the default convention.
Creating a Converter
--------------------

View File

@ -0,0 +1,18 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Configuration;
class ConfigurationAnnotationTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException RuntimeException
*/
public function testUndefinedSetterThrowsException()
{
$this->getMockForAbstractClass('Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationAnnotation', array(
array(
'doesNotExists' => true,
),
));
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\CacheListener;
class CacheListenerTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->listener = new CacheListener();
$this->response = new Response();
$this->cache = new Cache(array());
$this->request = $this->createRequest($this->cache);
$this->event = $this->createEventMock($this->request, $this->response);
}
public function testWontReassignResponseWhenResponseIsUnsuccessful()
{
$this->event
->expects($this->never())
->method('setResponse')
;
$this->response->setStatusCode(404);
$this->assertInternalType('null', $this->listener->onKernelResponse($this->event));
}
public function testWontReassignResponseWhenNoConfigurationIsPresent()
{
$this->event
->expects($this->never())
->method('setResponse')
;
$this->request->attributes->remove('_cache');
$this->assertInternalType('null', $this->listener->onKernelResponse($this->event));
}
public function testResponseIsPublicIfConfigurationIsPublic()
{
$request = $this->createRequest(new Cache(array(
'public' => true,
)));
$this->listener->onKernelResponse($this->createEventMock($request, $this->response));
$this->assertTrue($this->response->headers->hasCacheControlDirective('public'));
$this->assertFalse($this->response->headers->hasCacheControlDirective('private'));
}
public function testConfigurationAttributesAreSetOnResponse()
{
$this->assertInternalType('null', $this->response->getMaxAge());
$this->assertInternalType('null', $this->response->getExpires());
$this->assertFalse($this->response->headers->hasCacheControlDirective('s-maxage'));
$this->request->attributes->set('_cache', new Cache(array(
'expires' => 'tomorrow',
'smaxage' => '15',
'maxage' => '15',
)));
$this->listener->onKernelResponse($this->event);
$this->assertEquals('15', $this->response->getMaxAge());
$this->assertEquals('15', $this->response->headers->getCacheControlDirective('s-maxage'));
$this->assertInstanceOf('DateTime', $this->response->getExpires());
}
protected function createRequest(Cache $cache = null)
{
return new Request(array(), array(), array(
'_cache' => $cache,
));
}
protected function createEventMock(Request $request, Response $response)
{
$event = $this->getMock('Symfony\Component\HttpKernel\Event\FilterResponseEvent', array(), array(), '', null);
$event
->expects($this->any())
->method('getRequest')
->will($this->returnValue($request))
;
$event
->expects($this->any())
->method('getResponse')
->will($this->returnValue($response))
;
return $event;
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener;
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerCacheAtClass;
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerCacheAtClassAndMethod;
use Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture\FooControllerCacheAtMethod;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class ControllerListenerTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->listener = new ControllerListener(new AnnotationReader());
$this->request = $this->createRequest();
}
public function tearDown()
{
$this->listener = null;
$this->request = null;
}
public function testCacheAnnotationAtMethod()
{
$controller = new FooControllerCacheAtMethod();
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
$this->listener->onKernelController($this->event);
$this->assertNotNull($this->getReadedCache());
$this->assertEquals(FooControllerCacheAtMethod::METHOD_SMAXAGE, $this->getReadedCache()->getSMaxAge());
}
public function testCacheAnnotationAtClass()
{
$controller = new FooControllerCacheAtClass();
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
$this->listener->onKernelController($this->event);
$this->assertNotNull($this->getReadedCache());
$this->assertEquals(FooControllerCacheAtClass::CLASS_SMAXAGE, $this->getReadedCache()->getSMaxAge());
}
public function testCacheAnnotationAtClassAndMethod()
{
$controller = new FooControllerCacheAtClassAndMethod();
$this->event = $this->getFilterControllerEvent(array($controller, 'barAction'), $this->request);
$this->listener->onKernelController($this->event);
$this->assertNotNull($this->getReadedCache());
$this->assertEquals(FooControllerCacheAtClassAndMethod::METHOD_SMAXAGE, $this->getReadedCache()->getSMaxAge());
$this->event = $this->getFilterControllerEvent(array($controller, 'bar2Action'), $this->request);
$this->listener->onKernelController($this->event);
$this->assertNotNull($this->getReadedCache());
$this->assertEquals(FooControllerCacheAtClassAndMethod::CLASS_SMAXAGE, $this->getReadedCache()->getSMaxAge());
}
protected function createRequest(Cache $cache = null)
{
return new Request(array(), array(), array(
'_cache' => $cache,
));
}
protected function getFilterControllerEvent($controller, Request $request)
{
$mockKernel = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\Kernel', array('', ''));
return new FilterControllerEvent($mockKernel, $controller, $request, HttpKernelInterface::MASTER_REQUEST);
}
protected function getReadedCache()
{
return $this->request->attributes->get('_cache');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
/**
* @Cache(smaxage="20")
*/
class FooControllerCacheAtClass
{
const CLASS_SMAXAGE = 20;
public function barAction()
{
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
/**
* @Cache(smaxage="20")
*/
class FooControllerCacheAtClassAndMethod
{
const CLASS_SMAXAGE = 20;
const METHOD_SMAXAGE = 15;
/**
* @Cache(smaxage="15")
*/
public function barAction()
{
}
public function bar2Action()
{
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\EventListener\Fixture;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
class FooControllerCacheAtMethod
{
const METHOD_SMAXAGE = 15;
/**
* @Cache(smaxage="15")
*/
public function barAction()
{
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Request\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter;
class DoctrineParamConverterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Doctrine\Common\Persistence\ManagerRegistry
*/
private $manager;
/**
* @var DoctrineParamConverter
*/
private $converter;
public function setUp()
{
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
$this->markTestSkipped();
}
$this->manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$this->converter = new DoctrineParamConverter($this->manager);
}
public function createConfiguration($class = null, array $options = null)
{
$config = $this->getMock(
'Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface', array(
'getClass', 'getAliasName', 'getOptions'
));
if ($options !== null) {
$config->expects($this->once())
->method('getOptions')
->will($this->returnValue($options));
}
if ($class !== null) {
$config->expects($this->any())
->method('getClass')
->will($this->returnValue($class));
}
return $config;
}
public function testApplyWithNoIdAndData()
{
$request = new Request();
$config = $this->createConfiguration(null, array());
$objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager');
$this->manager->expects($this->never())->method('find');
$this->manager->expects($this->once())
->method('getManager')
->will($this->returnValue($objectManager));
$this->setExpectedException('LogicException');
$this->converter->apply($request, $config);
}
public function testSupports()
{
$config = $this->createConfiguration('stdClass', array());
$metadataFactory = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory');
$metadataFactory->expects($this->once())
->method('isTransient')
->with($this->equalTo('stdClass'))
->will($this->returnValue( false ));
$objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager');
$objectManager->expects($this->once())
->method('getMetadataFactory')
->will($this->returnValue($metadataFactory));
$this->manager->expects($this->once())
->method('getManager')
->will($this->returnValue($objectManager));
$ret = $this->converter->supports($config);
$this->assertTrue($ret, "Should be supported");
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Tests\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager;
use Sensio\Bundle\FrameworkExtraBundle\Configuration;
use Symfony\Component\HttpFoundation\Request;
class ParamConverterManagerTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->manager = new ParamConverterManager();
}
public function testPriorities()
{
$this->assertEquals(array(), $this->manager->all());
$high = $this->createParamConverterMock();
$low = $this->createParamConverterMock();
$this->manager->add($low);
$this->manager->add($high, 10);
$this->assertEquals(array(
$high,
$low,
), $this->manager->all());
}
public function testApply()
{
$supported = $this->createParamConverterMock();
$supported
->expects($this->once())
->method('supports')
->will($this->returnValue(true))
;
$supported
->expects($this->once())
->method('apply')
->will($this->returnValue(false))
;
$invalid = $this->createParamConverterMock();
$invalid
->expects($this->once())
->method('supports')
->will($this->returnValue(false))
;
$invalid
->expects($this->never())
->method('apply')
;
$configurations = array(
new Configuration\ParamConverter(array(
'name' => 'var',
)),
);
$this->manager->add($supported);
$this->manager->add($invalid);
$this->manager->apply(new Request(), $configurations);
}
public function testApplyNotCalledOnAlreadyConvertedObjects()
{
$converter = $this->createParamConverterMock();
$converter
->expects($this->never())
->method('supports')
;
$converter
->expects($this->never())
->method('apply')
;
$this->manager->add($converter);
$request = new Request();
$request->attributes->set('converted', new \stdClass);
$configuration = new Configuration\ParamConverter(array(
'name' => 'converted',
'class' => 'stdClass',
));
$this->manager->apply($request, array($configuration));
}
protected function createParamConverterMock()
{
return $this->getMock('Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface');
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./vendor/autoload.php" colors="true">
<testsuites>
<testsuite name="SensioFrameworkExtraBundle">
<directory suffix="Test.php">./Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,3 @@
phpunit.xml
vendor
composer.lock

View File

@ -0,0 +1,101 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Command;
use Symfony\Component\Console\Tester\CommandTester;
use Sensio\Bundle\GeneratorBundle\Command\GenerateBundleCommand;
class GenerateBundleCommandTest extends GenerateCommandTest
{
/**
* @dataProvider getInteractiveCommandData
*/
public function testInteractiveCommand($options, $input, $expected)
{
list($namespace, $bundle, $dir, $format, $structure) = $expected;
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($namespace, $bundle, $dir, $format, $structure)
;
$tester = new CommandTester($this->getCommand($generator, $input));
$tester->execute($options);
}
public function getInteractiveCommandData()
{
$tmp = sys_get_temp_dir();
return array(
array(array('--dir' => $tmp), "Foo/BarBundle\n", array('Foo\BarBundle', 'FooBarBundle', $tmp.'/', 'annotation', false)),
array(array('--dir' => $tmp), "Foo/BarBundle\nBarBundle\nfoo\nyml\nn", array('Foo\BarBundle', 'BarBundle', 'foo/', 'yml', false)),
array(array('--dir' => $tmp, '--format' => 'yml', '--bundle-name' => 'BarBundle', '--structure' => true), "Foo/BarBundle\n", array('Foo\BarBundle', 'BarBundle', $tmp.'/', 'yml', true)),
);
}
/**
* @dataProvider getNonInteractiveCommandData
*/
public function testNonInteractiveCommand($options, $expected)
{
list($namespace, $bundle, $dir, $format, $structure) = $expected;
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($namespace, $bundle, $dir, $format, $structure)
;
$tester = new CommandTester($this->getCommand($generator, ''));
$tester->execute($options, array('interactive' => false));
}
public function getNonInteractiveCommandData()
{
$tmp = sys_get_temp_dir();
return array(
array(array('--dir' => $tmp, '--namespace' => 'Foo/BarBundle'), array('Foo\BarBundle', 'FooBarBundle', $tmp.'/', 'annotation', false)),
array(array('--dir' => $tmp, '--namespace' => 'Foo/BarBundle', '--format' => 'yml', '--bundle-name' => 'BarBundle', '--structure' => true), array('Foo\BarBundle', 'BarBundle', $tmp.'/', 'yml', true)),
);
}
protected function getCommand($generator, $input)
{
$command = $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Command\GenerateBundleCommand')
->setMethods(array('checkAutoloader', 'updateKernel', 'updateRouting'))
->getMock()
;
$command->setContainer($this->getContainer());
$command->setHelperSet($this->getHelperSet($input));
$command->setGenerator($generator);
return $command;
}
protected function getGenerator()
{
// get a noop generator
return $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator')
->disableOriginalConstructor()
->setMethods(array('generate'))
->getMock()
;
}
}

View File

@ -0,0 +1,74 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Command;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\FormatterHelper;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
use Symfony\Component\DependencyInjection\Container;
abstract class GenerateCommandTest extends \PHPUnit_Framework_TestCase
{
protected function getHelperSet($input)
{
$dialog = new DialogHelper();
$dialog->setInputStream($this->getInputStream($input));
return new HelperSet(array(new FormatterHelper(), $dialog));
}
protected function getBundle()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle
->expects($this->any())
->method('getPath')
->will($this->returnValue(sys_get_temp_dir()))
;
return $bundle;
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fputs($stream, $input.str_repeat("\n", 10));
rewind($stream);
return $stream;
}
protected function getContainer()
{
$kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface');
$kernel
->expects($this->any())
->method('getBundle')
->will($this->returnValue($this->getBundle()))
;
$filesystem = $this->getMock('Symfony\Component\Filesystem\Filesystem');
$filesystem
->expects($this->any())
->method('isAbsolutePath')
->will($this->returnValue(true))
;
$container = new Container();
$container->set('kernel', $kernel);
$container->set('filesystem', $filesystem);
$container->setParameter('kernel.root_dir', sys_get_temp_dir());
return $container;
}
}

View File

@ -0,0 +1,141 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Command;
use Symfony\Component\Console\Tester\CommandTester;
use Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand;
class GenerateDoctrineCrudCommandTest extends GenerateCommandTest
{
/**
* @dataProvider getInteractiveCommandData
*/
public function testInteractiveCommand($options, $input, $expected)
{
list($entity, $format, $prefix, $withWrite) = $expected;
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($this->getBundle(), $entity, $this->getDoctrineMetadata(), $format, $prefix, $withWrite)
;
$tester = new CommandTester($this->getCommand($generator, $input));
$tester->execute($options);
}
public function getInteractiveCommandData()
{
return array(
array(array(), "AcmeBlogBundle:Blog/Post\n", array('Blog\\Post', 'annotation', 'blog_post', false)),
array(array('--entity' => 'AcmeBlogBundle:Blog/Post'), '', array('Blog\\Post', 'annotation', 'blog_post', false)),
array(array(), "AcmeBlogBundle:Blog/Post\ny\nyml\nfoobar\n", array('Blog\\Post', 'yml', 'foobar', true)),
array(array(), "AcmeBlogBundle:Blog/Post\ny\nyml\n/foobar\n", array('Blog\\Post', 'yml', 'foobar', true)),
array(array('--entity' => 'AcmeBlogBundle:Blog/Post', '--format' => 'yml', '--route-prefix' => 'foo', '--with-write' => true), '', array('Blog\\Post', 'yml', 'foo', true)),
);
}
/**
* @dataProvider getNonInteractiveCommandData
*/
public function testNonInteractiveCommand($options, $expected)
{
list($entity, $format, $prefix, $withWrite) = $expected;
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($this->getBundle(), $entity, $this->getDoctrineMetadata(), $format, $prefix, $withWrite)
;
$tester = new CommandTester($this->getCommand($generator, ''));
$tester->execute($options, array('interactive' => false));
}
public function getNonInteractiveCommandData()
{
return array(
array(array('--entity' => 'AcmeBlogBundle:Blog/Post'), array('Blog\\Post', 'annotation', 'blog_post', false)),
array(array('--entity' => 'AcmeBlogBundle:Blog/Post', '--format' => 'yml', '--route-prefix' => 'foo', '--with-write' => true), array('Blog\\Post', 'yml', 'foo', true)),
);
}
protected function getCommand($generator, $input)
{
$command = $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand')
->setMethods(array('getEntityMetadata'))
->getMock()
;
$command
->expects($this->any())
->method('getEntityMetadata')
->will($this->returnValue(array($this->getDoctrineMetadata())))
;
$command->setContainer($this->getContainer());
$command->setHelperSet($this->getHelperSet($input));
$command->setGenerator($generator);
$command->setFormGenerator($this->getFormGenerator());
return $command;
}
protected function getDoctrineMetadata()
{
return $this
->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadataInfo')
->disableOriginalConstructor()
->getMock()
;
}
protected function getGenerator()
{
// get a noop generator
return $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator')
->disableOriginalConstructor()
->setMethods(array('generate'))
->getMock()
;
}
protected function getFormGenerator()
{
return $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator')
->disableOriginalConstructor()
->setMethods(array('generate'))
->getMock()
;
}
protected function getContainer()
{
$container = parent::getContainer();
$registry = $this->getMock('Symfony\Bridge\Doctrine\RegistryInterface');
$registry
->expects($this->any())
->method('getEntityNamespace')
->will($this->returnValue('Foo\\FooBundle\\Entity'))
;
$container->set('doctrine', $registry);
return $container;
}
}

View File

@ -0,0 +1,104 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Command;
use Symfony\Component\Console\Tester\CommandTester;
use Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineEntityCommand;
class GenerateDoctrineEntityCommandTest extends GenerateCommandTest
{
/**
* @dataProvider getInteractiveCommandData
*/
public function testInteractiveCommand($options, $input, $expected)
{
list($entity, $format, $fields) = $expected;
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($this->getBundle(), $entity, $format, $fields)
;
$tester = new CommandTester($this->getCommand($generator, $input));
$tester->execute($options);
}
public function getInteractiveCommandData()
{
return array(
array(array(), "AcmeBlogBundle:Blog/Post\n", array('Blog\\Post', 'annotation', array())),
array(array('--entity' => 'AcmeBlogBundle:Blog/Post'), '', array('Blog\\Post', 'annotation', array())),
array(array(), "AcmeBlogBundle:Blog/Post\nyml\n\n", array('Blog\\Post', 'yml', array())),
array(array(), "AcmeBlogBundle:Blog/Post\nyml\ntitle\n\n255\ndescription\ntext\n\n", array('Blog\\Post', 'yml', array(
array('fieldName' => 'title', 'type' => 'string', 'length' => 255),
array('fieldName' => 'description', 'type' => 'text'),
))),
);
}
/**
* @dataProvider getNonInteractiveCommandData
*/
public function testNonInteractiveCommand($options, $expected)
{
list($entity, $format, $fields) = $expected;
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($this->getBundle(), $entity, $format, $fields)
;
$generator
->expects($this->any())
->method('isReservedKeyword')
->will($this->returnValue(false))
;
$tester = new CommandTester($this->getCommand($generator, ''));
$tester->execute($options, array('interactive' => false));
}
public function getNonInteractiveCommandData()
{
return array(
array(array('--entity' => 'AcmeBlogBundle:Blog/Post'), array('Blog\\Post', 'annotation', array())),
array(array('--entity' => 'AcmeBlogBundle:Blog/Post', '--format' => 'yml', '--fields' => 'title:string(255) description:text'), array('Blog\\Post', 'yml', array(
array('fieldName' => 'title', 'type' => 'string', 'length' => 255),
array('fieldName' => 'description', 'type' => 'text', 'length' => ''),
))),
);
}
protected function getCommand($generator, $input)
{
$command = new GenerateDoctrineEntityCommand();
$command->setContainer($this->getContainer());
$command->setHelperSet($this->getHelperSet($input));
$command->setGenerator($generator);
return $command;
}
protected function getGenerator()
{
// get a noop generator
return $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator')
->disableOriginalConstructor()
->setMethods(array('generate', 'isReservedKeyword'))
->getMock()
;
}
}

View File

@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Generator;
use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator;
class BundleGeneratorTest extends GeneratorTest
{
public function testGenerateYaml()
{
$generator = new BundleGenerator($this->filesystem, __DIR__.'/../../Resources/skeleton/bundle');
$generator->generate('Foo\BarBundle', 'FooBarBundle', $this->tmpDir, 'yml', false);
$files = array(
'FooBarBundle.php',
'Controller/DefaultController.php',
'Resources/views/Default/index.html.twig',
'Resources/config/routing.yml',
'Tests/Controller/DefaultControllerTest.php',
'Resources/config/services.yml',
'DependencyInjection/Configuration.php',
'DependencyInjection/FooBarExtension.php',
);
foreach ($files as $file) {
$this->assertTrue(file_exists($this->tmpDir.'/Foo/BarBundle/'.$file), sprintf('%s has been generated', $file));
}
$content = file_get_contents($this->tmpDir.'/Foo/BarBundle/FooBarBundle.php');
$this->assertContains('namespace Foo\\BarBundle', $content);
$content = file_get_contents($this->tmpDir.'/Foo/BarBundle/Controller/DefaultController.php');
$this->assertContains('public function indexAction', $content);
$this->assertNotContains('@Route("/hello/{name}"', $content);
$content = file_get_contents($this->tmpDir.'/Foo/BarBundle/Resources/views/Default/index.html.twig');
$this->assertContains('Hello {{ name }}!', $content);
}
public function testGenerateAnnotation()
{
$generator = new BundleGenerator($this->filesystem, __DIR__.'/../../Resources/skeleton/bundle');
$generator->generate('Foo\BarBundle', 'FooBarBundle', $this->tmpDir, 'annotation', false);
$this->assertFalse(file_exists($this->tmpDir.'/Foo/BarBundle/Resources/config/routing.yml'));
$this->assertFalse(file_exists($this->tmpDir.'/Foo/BarBundle/Resources/config/routing.xml'));
$content = file_get_contents($this->tmpDir.'/Foo/BarBundle/Controller/DefaultController.php');
$this->assertContains('@Route("/hello/{name}"', $content);
}
}

View File

@ -0,0 +1,206 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Generator;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator;
class DoctrineCrudGeneratorTest extends GeneratorTest
{
public function testGenerateYamlFull()
{
$this->getGenerator()->generate($this->getBundle(), 'Post', $this->getMetadata(), 'yml', '/post', true);
$files = array(
'Controller/PostController.php',
'Tests/Controller/PostControllerTest.php',
'Resources/config/routing/post.yml',
'Resources/views/Post/index.html.twig',
'Resources/views/Post/show.html.twig',
'Resources/views/Post/new.html.twig',
'Resources/views/Post/edit.html.twig',
);
foreach ($files as $file) {
$this->assertTrue(file_exists($this->tmpDir.'/'.$file), sprintf('%s has been generated', $file));
}
$files = array(
'Resources/config/routing/post.xml',
);
foreach ($files as $file) {
$this->assertFalse(file_exists($this->tmpDir.'/'.$file), sprintf('%s has not been generated', $file));
}
$content = file_get_contents($this->tmpDir.'/Controller/PostController.php');
$strings = array(
'namespace Foo\BarBundle\Controller;',
'public function indexAction',
'public function showAction',
'public function newAction',
'public function editAction',
);
foreach ($strings as $string) {
$this->assertContains($string, $content);
}
}
public function testGenerateXml()
{
$this->getGenerator()->generate($this->getBundle(), 'Post', $this->getMetadata(), 'xml', '/post', false);
$files = array(
'Controller/PostController.php',
'Tests/Controller/PostControllerTest.php',
'Resources/config/routing/post.xml',
'Resources/views/Post/index.html.twig',
'Resources/views/Post/show.html.twig',
);
foreach ($files as $file) {
$this->assertTrue(file_exists($this->tmpDir.'/'.$file), sprintf('%s has been generated', $file));
}
$files = array(
'Resources/config/routing/post.yml',
'Resources/views/Post/new.html.twig',
'Resources/views/Post/edit.html.twig',
);
foreach ($files as $file) {
$this->assertFalse(file_exists($this->tmpDir.'/'.$file), sprintf('%s has not been generated', $file));
}
$content = file_get_contents($this->tmpDir.'/Controller/PostController.php');
$strings = array(
'namespace Foo\BarBundle\Controller;',
'public function indexAction',
'public function showAction',
);
foreach ($strings as $string) {
$this->assertContains($string, $content);
}
$content = file_get_contents($this->tmpDir.'/Controller/PostController.php');
$strings = array(
'public function newAction',
'public function editAction',
'@Route',
);
foreach ($strings as $string) {
$this->assertNotContains($string, $content);
}
}
public function testGenerateAnnotationWrite()
{
$this->getGenerator()->generate($this->getBundle(), 'Post', $this->getMetadata(), 'annotation', '/post', true);
$files = array(
'Controller/PostController.php',
'Tests/Controller/PostControllerTest.php',
'Resources/views/Post/index.html.twig',
'Resources/views/Post/show.html.twig',
'Resources/views/Post/new.html.twig',
'Resources/views/Post/edit.html.twig',
);
foreach ($files as $file) {
$this->assertTrue(file_exists($this->tmpDir.'/'.$file), sprintf('%s has been generated', $file));
}
$files = array(
'Resources/config/routing/post.yml',
'Resources/config/routing/post.xml',
);
foreach ($files as $file) {
$this->assertFalse(file_exists($this->tmpDir.'/'.$file), sprintf('%s has not been generated', $file));
}
$content = file_get_contents($this->tmpDir.'/Controller/PostController.php');
$strings = array(
'namespace Foo\BarBundle\Controller;',
'public function indexAction',
'public function showAction',
'public function newAction',
'public function editAction',
'@Route',
);
foreach ($strings as $string) {
$this->assertContains($string, $content);
}
}
public function testGenerateAnnotation()
{
$this->getGenerator()->generate($this->getBundle(), 'Post', $this->getMetadata(), 'annotation', '/post', false);
$files = array(
'Controller/PostController.php',
'Tests/Controller/PostControllerTest.php',
'Resources/views/Post/index.html.twig',
'Resources/views/Post/show.html.twig',
);
foreach ($files as $file) {
$this->assertTrue(file_exists($this->tmpDir.'/'.$file), sprintf('%s has been generated', $file));
}
$files = array(
'Resources/config/routing/post.yml',
'Resources/config/routing/post.xml',
'Resources/views/Post/new.html.twig',
'Resources/views/Post/edit.html.twig',
);
foreach ($files as $file) {
$this->assertFalse(file_exists($this->tmpDir.'/'.$file), sprintf('%s has not been generated', $file));
}
$content = file_get_contents($this->tmpDir.'/Controller/PostController.php');
$strings = array(
'namespace Foo\BarBundle\Controller;',
'public function indexAction',
'public function showAction',
'@Route',
);
foreach ($strings as $string) {
$this->assertContains($string, $content);
}
$content = file_get_contents($this->tmpDir.'/Controller/PostController.php');
$strings = array(
'public function newAction',
'public function editAction',
);
foreach ($strings as $string) {
$this->assertNotContains($string, $content);
}
}
protected function getGenerator()
{
return new DoctrineCrudGenerator($this->filesystem, __DIR__.'/../../Resources/skeleton/crud');
}
protected function getBundle()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle->expects($this->any())->method('getPath')->will($this->returnValue($this->tmpDir));
$bundle->expects($this->any())->method('getName')->will($this->returnValue('FooBarBundle'));
$bundle->expects($this->any())->method('getNamespace')->will($this->returnValue('Foo\BarBundle'));
return $bundle;
}
public function getMetadata()
{
$metadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadataInfo')->disableOriginalConstructor()->getMock();
$metadata->identifier = array('id');
$metadata->fieldMappings = array('title' => array('type' => 'string'));
return $metadata;
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Generator;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator;
class DoctrineFormGeneratorTest extends GeneratorTest
{
public function testGenerate()
{
$generator = new DoctrineFormGenerator($this->filesystem, __DIR__.'/../../Resources/skeleton/form');
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle->expects($this->any())->method('getPath')->will($this->returnValue($this->tmpDir));
$bundle->expects($this->any())->method('getNamespace')->will($this->returnValue('Foo\BarBundle'));
$metadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadataInfo')->disableOriginalConstructor()->getMock();
$metadata->identifier = array('id');
$metadata->associationMappings = array('title' => array('type' => 'string'));
$generator->generate($bundle, 'Post', $metadata);
$this->assertTrue(file_exists($this->tmpDir.'/Form/PostType.php'));
$content = file_get_contents($this->tmpDir.'/Form/PostType.php');
$this->assertContains('->add(\'title\')', $content);
$this->assertContains('class PostType extends AbstractType', $content);
$this->assertContains("'data_class' => 'Foo\BarBundle\Entity\Post'", $content);
$this->assertContains("'foo_barbundle_posttype'", $content);
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Tests\Generator;
use Symfony\Component\Filesystem\Filesystem;
abstract class GeneratorTest extends \PHPUnit_Framework_TestCase
{
protected $filesystem;
protected $tmpDir;
public function setUp()
{
$this->tmpDir = sys_get_temp_dir().'/sf2';
$this->filesystem = new Filesystem();
$this->filesystem->remove($this->tmpDir);
}
public function tearDown()
{
$this->filesystem->remove($this->tmpDir);
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./vendor/autoload.php" colors="true">
<testsuites>
<testsuite name="SensioGeneratorBundle">
<directory suffix="Test.php">./Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,6 @@
.DS_Store
tests/acceptance.conf.php
tests/smoke.conf.php
build/*
.project
.settings/*

100
vendor/swiftmailer/swiftmailer/CHANGES vendored Normal file
View File

@ -0,0 +1,100 @@
Changelog since Version 4.x.x
=============================
4.2.2 (2012-XX-XX)
------------------
* n/a
4.2.1 (2012-07-13)
------------------
* changed the coding standards to PSR-1/2
* fixed issue with autoloading
* added NativeQpContentEncoder to enhance performance (for PHP 5.3+)
4.2.0 (2012-06-29)
------------------
* added documentation about how to use the Japanese support introduced in 4.1.8
* added a way to override the default configuration in a lazy way
* changed the PEAR init script to lazy-load the initialization
* fixed a bug when calling Swift_Preferences before anything else (regression introduced in 4.1.8)
4.1.8 (2012-06-17)
------------------
* added Japanese iso-2022-jp support
* changed the init script to lazy-load the initialization
* fixed docblocks (@id) which caused some problems with libraries parsing the dobclocks
* fixed Swift_Mime_Headers_IdentificationHeader::setId() when passed an array of ids
* fixed encoding of email addresses in headers
* added replacements setter to the Decorator plugin
4.1.7 (2012-04-26)
------------------
* fixed QpEncoder safeMapShareId property
4.1.6 (2012-03-23)
------------------
* reduced the size of serialized Messages
4.1.5 (2012-01-04)
------------------
* enforced Swift_Spool::queueMessage() to return a Boolean
* made an optimization to the memory spool: start the transport only when required
* prevented stream_socket_client() from generating an error and throw a Swift_TransportException instead
* fixed a PHP warning when calling to mail() when safe_mode is off
* many doc tweaks
4.1.4 (2011-12-16)
------------------
* added a memory spool (Swift_MemorySpool)
* fixed too many opened files when sending emails with attachments
4.1.3 (2011-10-27)
------------------
* added STARTTLS support
* added missing @return tags on fluent methods
* added a MessageLogger plugin that logs all sent messages
* added composer.json
4.1.2 (2011-09-13)
------------------
* fixed wrong detection of magic_quotes_runtime
* fixed fatal errors when no To or Subject header has been set
* fixed charset on parameter header continuations
* added documentation about how to install Swiftmailer from the PEAR channel
* fixed various typos and markup problem in the documentation
* fixed warning when cache directory does not exist
* fixed "slashes are escaped" bug
* changed require_once() to require() in autoload
4.1.1 (2011-07-04)
------------------
* added missing file in PEAR package
4.1.0 (2011-06-30)
------------------
* documentation has been converted to ReST
4.1.0 RC1 (2011-06-17)
----------------------
New features:
* changed the Decorator Plugin to allow replacements in all headers
* added Swift_Mime_Grammar and Swift_Validate to validate an email address
* modified the autoloader to lazy-initialize Swiftmailer
* removed Swift_Mailer::batchSend()
* added NullTransport
* added new plugins: RedirectingPlugin and ImpersonatePlugin
* added a way to send messages asynchronously (Spool)

16
vendor/swiftmailer/swiftmailer/README vendored Normal file
View File

@ -0,0 +1,16 @@
Swift Mailer
------------
Swift Mailer is a component based mailing solution for PHP 5.
It is released under the LGPL license.
Homepage: http://swiftmailer.org
Documentation: http://swiftmailer.org/docs
Mailing List: http://groups.google.com/group/swiftmailer
Bugs: https://github.com/swiftmailer/swiftmailer/issues
Repository: https://github.com/swiftmailer/swiftmailer
Swift Mailer is highly object-oriented by design and lends itself
to use in complex web application with a great deal of flexibility.
For full details on usage, see the documentation.

View File

@ -0,0 +1,67 @@
This README applies to anyone who checks out the source from git.
If you're reading this page on github.com, and you don't have git
installed or know about git, you can download this repository by
using the "download" button on github.com, right above the file
list.
PREAMBLE:
---------
The git repository is structured in the expected way where "master" is the
main branch you should use if you want to have bleeding-edge updates. Any
other branch should be ignored since it will likely contain unstable
and/or experimental developments.
Generally speaking you should feel safe using the "master" branch in
production code. Anything likely to break will be committed to another
branch. Only bugfixes and clean non-breaking feature additions will be
performed in master.
All releases (post version 4.0.0) are tagged using the version number of
that release. Earlier versions exist in a subversion repository at the
old sourceforge project page.
WHAT IS SWIFT MAILER?
---------------------
Swift Mailer is a component based mailing solution for PHP 5.
It is released under the LGPL license.
Homepage: http://swiftmailer.org/
Documentation: http://swiftmailer.org/docs
Mailing List: http://groups.google.com/group/swiftmailer
Bugs: https://github.com/swiftmailer/swiftmailer/issues
Repository: https://github.com/swiftmailer/swiftmailer
Swift Mailer is highly object-oriented by design and lends itself
to use in complex web application with a great deal of flexibility.
For full details on usage, see the documentation.
WHY SO MUCH CLUTTER?
--------------------
As you can probably see, there are a lot more files in here than you find in
the pre-packaged versions. That's because I store notes (UML, RFCs etc) in
the repository.
The main library files live in /lib and the tests live in /tests. You can run
the tests by pointing your web browser at /test-suite, or by running the
command "php test-suite/run.php". Some tests will be "skipped" if
tests/smoke.conf.php and tests/acceptance.conf.php are not editted. This is
harmless and normal.
If you want to create a bundled-up package from subversion you can do so if
you have Ant (http://ant.apache.org/) installed. Simply run "ant package"
from this directory and the tar.gz file will be created in the /build
directory.
Running the command "ant" with no arguments will bundle up the package without
compressing it into a tar.gz file.
Tests can also be run using "ant test" provided php is on your PATH
environment variable.
EoM

View File

@ -1 +1 @@
Swift-4.2.1
Swift-4.2.2-DEV

112
vendor/swiftmailer/swiftmailer/build.xml vendored Normal file
View File

@ -0,0 +1,112 @@
<!-- Ant build file for Swift Mailer (Standard Edition) -->
<project name="Swift Mailer SE" default="bundle">
<!-- Config settings -->
<property name="path" value="." />
<property name="builddir" value="${path}/build" />
<property name="src.versionfile" value="${path}/VERSION" />
<!-- Generate version info from VERSION file -->
<loadfile property="bundle.name" srcFile="${src.versionfile}">
<filterchain>
<striplinebreaks />
</filterchain>
</loadfile>
<!-- Generate version number -->
<loadfile property="bundle.version" srcFile="${src.versionfile}">
<filterchain>
<striplinebreaks />
<tokenfilter>
<replacestring from="Swift-" to=""/>
</tokenfilter>
</filterchain>
</loadfile>
<property name="src.readmefile" value="${path}/README" />
<property name="src.changesfile" value="${path}/CHANGES" />
<property name="src.libdir" value="${path}/lib" />
<property name="src.testsuite" value="${path}/test-suite" />
<property name="src.tests" value="${path}/tests" />
<property name="builddir.package" value="${builddir}/${bundle.name}" />
<property name="runtests" value="" />
<filelist id="src.licenses" dir="${path}">
<file name="LICENSE" />
</filelist>
<fileset id="build.simpletest.unneeded"
dir="${builddir.package}/test-suite/lib/simpletest">
<include name="docs/" />
<include name="tutorials/" />
<include name="extensions/" />
<include name="test/" />
<include name="packages/" />
</fileset>
<!-- Bundle all files into a central location -->
<target name="bundle" depends="prepare, clean, copy-classes">
<copy file="${src.versionfile}" todir="${builddir.package}" />
<copy file="${src.changesfile}" todir="${builddir.package}" />
<copy todir="${builddir.package}">
<filelist refid="src.licenses" />
</copy>
<copy file="${src.readmefile}" todir="${builddir.package}" />
<move file="${builddir.package}/tests/acceptance.conf.php.default"
tofile="${builddir.package}/tests/acceptance.conf.php" />
<move file="${builddir.package}/tests/smoke.conf.php.default"
tofile="${builddir.package}/tests/smoke.conf.php" />
</target>
<!-- Copy some *.default files to their required files if needed -->
<target name="prepare">
<copy file="${src.tests}/acceptance.conf.php.default"
tofile="${src.tests}/acceptance.conf.php" />
<copy file="${src.tests}/smoke.conf.php.default"
tofile="${src.tests}/smoke.conf.php" />
</target>
<!-- Clean out the build directory -->
<target name="clean">
<delete includeemptydirs="true">
<fileset dir="${builddir}" includes="**/*" />
</delete>
</target>
<!-- Copy all classes from the class path to the bundle -->
<target name="copy-classes">
<copy todir="${builddir.package}/lib">
<fileset dir="${src.libdir}" />
<filterchain>
<replacetokens>
<token key="SWIFT_VERSION_NUMBER" value="${bundle.version}"/>
</replacetokens>
</filterchain>
</copy>
<copy todir="${builddir.package}/test-suite">
<fileset dir="${src.testsuite}" />
</copy>
<copy todir="${builddir.package}/tests">
<fileset dir="${src.tests}" />
</copy>
<delete includeemptydirs="true">
<fileset refid="build.simpletest.unneeded" />
</delete>
</target>
<!-- Package up the project as a gzip file -->
<target name="package" depends="bundle">
<tar destfile="${builddir.package}.tar.gz" compression="gzip" longfile="gnu">
<fileset dir="${builddir}" />
</tar>
<delete includeemptydirs="true">
<fileset dir="${builddir.package}" includes="**" />
</delete>
</target>
<!-- Run the included unit tests -->
<target name="test" depends="prepare, clean, copy-classes">
<exec executable="php" failonerror="true">
<env key="_" value="php" />
<arg value="${builddir.package}/test-suite/run.php" />
<arg value="${runtests}" />
</exec>
</target>
</project>

View File

@ -0,0 +1,42 @@
<?php
if (!isset($argv[1]))
{
die('You must provide the version (1.0.0)');
}
if (!isset($argv[2]))
{
die('You must provide the stability (alpha, beta, or stable)');
}
$context = array(
'date' => date('Y-m-d'),
'time' => date('H:m:00'),
'version' => $argv[1],
'api_version' => $argv[1],
'stability' => $argv[2],
'api_stability' => $argv[2],
);
$context['files'] = '';
$path = realpath(dirname(__FILE__).'/lib/classes/Swift');
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY) as $file)
{
if (preg_match('/\.php$/', $file))
{
$name = str_replace($path.'/', '', $file);
$context['files'] .= ' <file install-as="Swift/'.$name.'" name="'.$name.'" role="php" />'."\n";
}
}
$template = file_get_contents(dirname(__FILE__).'/package.xml.tpl');
$content = preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', 'replace_parameters', $template);
file_put_contents(dirname(__FILE__).'/package.xml', $content);
function replace_parameters($matches)
{
global $context;
return isset($context[$matches[1]]) ? $context[$matches[1]] : null;
}

View File

@ -20,7 +20,7 @@ class Swift_SmtpTransport extends Swift_Transport_EsmtpTransport
* Create a new SmtpTransport, optionally with $host, $port and $security.
* @param string $host
* @param int $port
* @param int $security
* @param string $security
*/
public function __construct($host = 'localhost', $port = 25, $security = null)
{
@ -39,7 +39,7 @@ class Swift_SmtpTransport extends Swift_Transport_EsmtpTransport
* Create a new SmtpTransport instance.
* @param string $host
* @param int $port
* @param int $security
* @param string $security
* @return Swift_SmtpTransport
*/
public static function newInstance($host = 'localhost', $port = 25, $security = null)

View File

@ -21,10 +21,12 @@ define('SWIFT_REQUIRED_LOADED', true);
//Load Swift utility class
require dirname(__FILE__) . '/classes/Swift.php';
if (!function_exists('_swiftmailer_init')) {
function _swiftmailer_init()
{
require dirname(__FILE__) . '/swift_init.php';
}
}
//Start the autoloader and lazy-load the init script to set up dependency injection
Swift::registerAutoload('_swiftmailer_init');

View File

@ -21,10 +21,12 @@ define('SWIFT_REQUIRED_LOADED', true);
//Load Swift utility class
require dirname(__FILE__) . '/Swift.php';
if (!function_exists('_swiftmailer_init')) {
function _swiftmailer_init()
{
require dirname(__FILE__) . '/swift_init.php';
}
}
//Start the autoloader and lazy-load the init script to set up dependency injection
Swift::registerAutoload('_swiftmailer_init');

View File

@ -0,0 +1,15 @@
Applications I need to test with
---------------------------------
Standalone (can read .eml files):
---------------------------------
Microsoft Entourage (can assume same as outlook)
Mozilla Thunderbird
Apple Mail
Web-based:
----------
Hotmail
Gmail
Yahoo!
Mail2Web

View File

@ -0,0 +1,46 @@
Following is a list of character sets along with their widths:
--------------------------------------------------------------
1 Octet 8bit:
-------------
Windows 125* (CP125*)
CP*
ANSI
ISO-8859-* (IEC-8859-*)
Macintosh (Mac OS Roman)
KOI8-U (potentially KOI*8-*)
KOI8-R
MIK
Cork (T1)
ISCII
VISCII
1 Octet 7bit:
-------------
US-ASCII
K0I7
2 octets 16 bit:
----------------
UCS-2
UTF-16* (UTF-16BE etc)
4-octets 32 bit:
----------------
UCS-4
UTF-32
Variable-width:
----------------------------
Big5 - http://en.wikipedia.org/wiki/Big5 (1-2 bytes: 00-7f=1, 81-fe=2)
HKSCS - http://en.wikipedia.org/wiki/HKSCS (a big5 variant, but some variants use 10646)
ISO-10646 (IEC-10646) - http://en.wikipedia.org/wiki/ISO_10646 (unicode)
UTF-8 (1-5 bytes)
ISO-2022 (IEC-2022) - http://en.wikipedia.org/wiki/ISO_2022
Shift-JIS - http://en.wikipedia.org/wiki/Shift-JIS
A good resource:
----------------
http://en.wikipedia.org/wiki/Character_encoding#Simple_character_sets

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<message>
<subject>Some test message</subject>
<to name="Chris Corbyn">chris@w3style.co.uk</to>
<to name="Mark Corbyn">mark@swiftmailer.org</to>
<from>chris.corbyn@sitepoint.com</from>
<content-type>text/plain</content-type>
<body>
Here's a recipe for beef stifado
</body>
<part>
<content-type>text/html</content-type>
<body>
This is the other part
</body>
</part>
<attachment>
<content-type>application/pdf</content-type>
<filename>stifado.pdf</filename>
<body type="file">/path/to/stifado.pdf</body>
</attachment>
</message>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,395 @@
Network Working Group N. Freed
Request For Comments: 1854 Innosoft International, Inc.
Category: Standards Track A. Cargille, WG Chair
October 1995
SMTP Service Extension
for Command Pipelining
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Abstract
This memo defines an extension to the SMTP service whereby a server
can indicate the extent of its ability to accept multiple commands in
a single TCP send operation. Using a single TCP send operation for
multiple commands can improve SMTP performance significantly.
Introduction
Although SMTP is widely and robustly deployed, certain extensions may
nevertheless prove useful. In particular, many parts of the Internet
make use of high latency network links.
SMTP's intrinsic one command-one response structure is significantly
penalized by high latency links, often to the point where the factors
contributing to overall connection time are dominated by the time
spent waiting for responses to individual commands (turnaround time).
In the best of all worlds it would be possible to simply deploy SMTP
client software that makes use of command pipelining: batching up
multiple commands into single TCP send operations. Unfortunately, the
original SMTP specification [1] did not explicitly state that SMTP
servers must support this. As a result a non-trivial number of
Internet SMTP servers cannot adequately handle command pipelining.
Flaws known to exist in deployed servers include:
(1) Connection handoff and buffer flushes in the middle of
the SMTP dialogue. Creation of server processes for
incoming SMTP connections is a useful, obvious, and
harmless implementation technique. However, some SMTP
servers defer process forking and connection handoff
Freed & Cargille Standards Track [Page 1]
RFC 1854 SMTP Pipelining October 1995
until some intermediate point in the SMTP dialogue.
When this is done material read from the TCP connection
and kept in process buffers can be lost.
(2) Flushing the TCP input buffer when an SMTP command
fails. SMTP commands often fail but there is no reason
to flush the TCP input buffer when this happens.
Nevertheless, some SMTP servers do this.
(3) Improper processing and promulgation of SMTP command
failures. For example, some SMTP servers will refuse to
accept a DATA command if the last RCPT TO command
fails, paying no attention to the success or failure of
prior RCPT TO command results. Other servers will
accept a DATA command even when all previous RCPT TO
commands have failed. Although it is possible to
accommodate this sort of behavior in a client that
employs command pipelining, it does complicate the
construction of the client unnecessarily.
This memo uses the mechanism described in [2] to define an extension
to the SMTP service whereby an SMTP server can declare that it is
capable of handling pipelined commands. The SMTP client can then
check for this declaration and use pipelining only when the server
declares itself capable of handling it.
1. Framework for the Command Pipelining Extension
The Command Pipelining extension is defined as follows:
(1) the name of the SMTP service extension is Pipelining;
(2) the EHLO keyword value associated with the extension is
PIPELINING;
(3) no parameter is used with the PIPELINING EHLO keyword;
(4) no additional parameters are added to either the MAIL
FROM or RCPT TO commands.
(5) no additional SMTP verbs are defined by this extension;
and,
(6) the next section specifies how support for the
extension affects the behavior of a server and client
SMTP.
Freed & Cargille Standards Track [Page 2]
RFC 1854 SMTP Pipelining October 1995
2. The Pipelining Service Extension
When a client SMTP wishes to employ command pipelining, it first
issues the EHLO command to the server SMTP. If the server SMTP
responds with code 250 to the EHLO command, and the response includes
the EHLO keyword value PIPELINING, then the server SMTP has indicated
that it can accommodate SMTP command pipelining.
2.1. Client use of pipelining
Once the client SMTP has confirmed that support exists for the
pipelining extension, the client SMTP may then elect to transmit
groups of SMTP commands in batches without waiting for a response to
each individual command. In particular, the commands RSET, MAIL FROM,
SEND FROM, SOML FROM, SAML FROM, and RCPT TO can all appear anywhere
in a pipelined command group. The EHLO, DATA, VRFY, EXPN, TURN,
QUIT, and NOOP commands can only appear as the last command in a
group since their success or failure produces a change of state which
the client SMTP must accommodate. (NOOP is included in this group so
it can be used as a synchronization point.)
Additional commands added by other SMTP extensions may only appear as
the last command in a group unless otherwise specified by the
extensions that define the commands.
The actual transfer of message content is explicitly allowed to be
the first "command" in a group. That is, the RSET/MAIL FROM sequence
necessary to initiate a new message transaction can be placed in the
same group as the final transfer of the headers and body of the
previous message.
Client SMTP implementations that employ pipelining MUST check ALL
statuses associated with each command in a group. For example, if
none of the RCPT TO recipient addresses were accepted the client must
then check the response to the DATA command -- the client cannot
assume that the DATA command will be rejected just because none of
the RCPT TO commands worked. If the DATA command was properly
rejected the client SMTP can just issue RSET, but if the DATA command
was accepted the client SMTP should send a single dot.
Command statuses MUST be coordinated with responses by counting each
separate response and correlating that count with the number of
commands known to have been issued. Multiline responses MUST be
supported. Matching on the basis of either the error code value or
associated text is expressly forbidden.
Client SMTP implementations MAY elect to operate in a nonblocking
fashion, processing server responses immediately upon receipt, even
Freed & Cargille Standards Track [Page 3]
RFC 1854 SMTP Pipelining October 1995
if there is still data pending transmission from the client's
previous TCP send operation. If nonblocking operation is not
supported, however, client SMTP implementations MUST also check the
TCP window size and make sure that each group of commands fits
entirely within the window. The window size is usually, but not
always, 4K octets. Failure to perform this check can lead to
deadlock conditions.
Clients MUST NOT confuse responses to multiple commands with
multiline responses. Each command requires one or more lines of
response, the last line not containing a dash between the response
code and the response string.
2.2. Server support of pipelining
A server SMTP implementation that offers the pipelining extension:
(1) MUST NOT flush or otherwise lose the contents of the
TCP input buffer under any circumstances whatsoever.
(2) SHOULD issue a positive response to the DATA command if
and only if one or more valid RCPT TO addresses have
been previously received.
(3) MUST NOT, after issuing a positive response to a DATA
command with no valid recipients and subsequently
receiving an empty message, send any message whatsoever
to anybody.
(4) SHOULD elect to store responses to grouped RSET, MAIL
FROM, SEND FROM, SOML FROM, SAML FROM, and RCPT TO
commands in an internal buffer so they can sent as a
unit.
(5) MUST NOT buffer responses to EHLO, DATA, VRFY, EXPN,
TURN, QUIT, and NOOP.
(6) MUST NOT buffer responses to unrecognized commands.
(7) MUST send all pending responses immediately whenever
the local TCP input buffer is emptied.
(8) MUST NOT make assumptions about commands that are yet
to be received.
(9) SHOULD issue response text that indicates, either
implicitly or explicitly, what command the response
matches.
Freed & Cargille Standards Track [Page 4]
RFC 1854 SMTP Pipelining October 1995
The overriding intent of these server requirements is to make it as
easy as possible for servers to conform to these pipelining
extensions.
3. Examples
Consider the following SMTP dialogue that does not use pipelining:
S: <wait for open connection>
C: <open connection to server>
S: 220 innosoft.com SMTP service ready
C: HELO dbc.mtview.ca.us
S: 250 innosoft.com
C: MAIL FROM:<mrose@dbc.mtview.ca.us>
S: 250 sender <mrose@dbc.mtview.ca.us> OK
C: RCPT TO:<ned@innosoft.com>
S: 250 recipient <ned@innosoft.com> OK
C: RCPT TO:<dan@innosoft.com>
S: 250 recipient <dan@innosoft.com> OK
C: RCPT TO:<kvc@innosoft.com>
S: 250 recipient <kvc@innosoft.com> OK
C: DATA
S: 354 enter mail, end with line containing only "."
...
C: .
S: 250 message sent
C: QUIT
S: 221 goodbye
The client waits for a server response a total of 9 times in this
simple example. But if pipelining is employed the following dialogue
is possible:
S: <wait for open connection>
C: <open connection to server>
S: 220 innosoft.com SMTP service ready
C: EHLO dbc.mtview.ca.us
S: 250-innosoft.com
S: 250 PIPELINING
C: MAIL FROM:<mrose@dbc.mtview.ca.us>
C: RCPT TO:<ned@innosoft.com>
C: RCPT TO:<dan@innosoft.com>
C: RCPT TO:<kvc@innosoft.com>
C: DATA
S: 250 sender <mrose@dbc.mtview.ca.us> OK
S: 250 recipient <ned@innosoft.com> OK
S: 250 recipient <dan@innosoft.com> OK
S: 250 recipient <kvc@innosoft.com> OK
Freed & Cargille Standards Track [Page 5]
RFC 1854 SMTP Pipelining October 1995
S: 354 enter mail, end with line containing only "."
...
C: .
C: QUIT
S: 250 message sent
S: 221 goodbye
The total number of turnarounds has been reduced from 9 to 4.
The next example illustrates one possible form of behavior when
pipelining is used and all recipients are rejected:
S: <wait for open connection>
C: <open connection to server>
S: 220 innosoft.com SMTP service ready
C: EHLO dbc.mtview.ca.us
S: 250-innosoft.com
S: 250 PIPELINING
C: MAIL FROM:<mrose@dbc.mtview.ca.us>
C: RCPT TO:<nsb@thumper.bellcore.com>
C: RCPT TO:<galvin@tis.com>
C: DATA
S: 250 sender <mrose@dbc.mtview.ca.us> OK
S: 550 remote mail to <nsb@thumper.bellore.com> not allowed
S: 550 remote mail to <galvin@tis.com> not allowed
S: 554 no valid recipients given
C: QUIT
S: 221 goodbye
The client SMTP waits for the server 4 times here as well. If the
server SMTP does not check for at least one valid recipient prior to
accepting the DATA command, the following dialogue would result:
S: <wait for open connection>
C: <open connection to server>
S: 220 innosoft.com SMTP service ready
C: EHLO dbc.mtview.ca.us
S: 250-innosoft.com
S: 250 PIPELINING
C: MAIL FROM:<mrose@dbc.mtview.ca.us>
C: RCPT TO:<nsb@thumper.bellcore.com>
C: RCPT TO:<galvin@tis.com>
C: DATA
S: 250 sender <mrose@dbc.mtview.ca.us> OK
S: 550 remote mail to <nsb@thumper.bellore.com> not allowed
S: 550 remote mail to <galvin@tis.com> not allowed
S: 354 enter mail, end with line containing only "."
C: .
Freed & Cargille Standards Track [Page 6]
RFC 1854 SMTP Pipelining October 1995
C: QUIT
S: 554 no valid recipients
S: 221 goodbye
4. Security Considerations
This RFC does not discuss security issues and is not believed to
raise any security issues not endemic in electronic mail and present
in fully conforming implementations of [1].
5. Acknowledgements
This document is based on the SMTP service extension model presented
in RFC 1425. Marshall Rose's description of SMTP command pipelining
in his book "The Internet Message" also served as a source of
inspiration for this extension.
6. References
[1] Postel, J., "Simple Mail Transfer Protocol", STD 10
RFC 821, USC/Information Sciences Institute, August
1982.
[2] Klensin, J., Freed, N., Rose, M., Stefferud, E.,
and D. Crocker, "SMTP Service Extensions", RFC 1651,
MCI, Innosoft, Dover Beach Consulting, Inc.,
Network Management Associates, Inc., Silicon Graphics,
Inc., July 1994.
7. Author's Address
Ned Freed
Innosoft International, Inc.
1050 East Garvey Avenue South
West Covina, CA 91790
USA
Phone: +1 818 919 3600
Fax: +1 818 919 3614
EMail: ned@innosoft.com
Freed & Cargille Standards Track [Page 7]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,843 @@
Network Working Group K. Moore
Request for Comments: 2047 University of Tennessee
Obsoletes: 1521, 1522, 1590 November 1996
Category: Standards Track
MIME (Multipurpose Internet Mail Extensions) Part Three:
Message Header Extensions for Non-ASCII Text
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Abstract
STD 11, RFC 822, defines a message representation protocol specifying
considerable detail about US-ASCII message headers, and leaves the
message content, or message body, as flat US-ASCII text. This set of
documents, collectively called the Multipurpose Internet Mail
Extensions, or MIME, redefines the format of messages to allow for
(1) textual message bodies in character sets other than US-ASCII,
(2) an extensible set of different formats for non-textual message
bodies,
(3) multi-part message bodies, and
(4) textual header information in character sets other than US-ASCII.
These documents are based on earlier work documented in RFC 934, STD
11, and RFC 1049, but extends and revises them. Because RFC 822 said
so little about message bodies, these documents are largely
orthogonal to (rather than a revision of) RFC 822.
This particular document is the third document in the series. It
describes extensions to RFC 822 to allow non-US-ASCII text data in
Internet mail header fields.
Moore Standards Track [Page 1]
RFC 2047 Message Header Extensions November 1996
Other documents in this series include:
+ RFC 2045, which specifies the various headers used to describe
the structure of MIME messages.
+ RFC 2046, which defines the general structure of the MIME media
typing system and defines an initial set of media types,
+ RFC 2048, which specifies various IANA registration procedures
for MIME-related facilities, and
+ RFC 2049, which describes MIME conformance criteria and
provides some illustrative examples of MIME message formats,
acknowledgements, and the bibliography.
These documents are revisions of RFCs 1521, 1522, and 1590, which
themselves were revisions of RFCs 1341 and 1342. An appendix in RFC
2049 describes differences and changes from previous versions.
1. Introduction
RFC 2045 describes a mechanism for denoting textual body parts which
are coded in various character sets, as well as methods for encoding
such body parts as sequences of printable US-ASCII characters. This
memo describes similar techniques to allow the encoding of non-ASCII
text in various portions of a RFC 822 [2] message header, in a manner
which is unlikely to confuse existing message handling software.
Like the encoding techniques described in RFC 2045, the techniques
outlined here were designed to allow the use of non-ASCII characters
in message headers in a way which is unlikely to be disturbed by the
quirks of existing Internet mail handling programs. In particular,
some mail relaying programs are known to (a) delete some message
header fields while retaining others, (b) rearrange the order of
addresses in To or Cc fields, (c) rearrange the (vertical) order of
header fields, and/or (d) "wrap" message headers at different places
than those in the original message. In addition, some mail reading
programs are known to have difficulty correctly parsing message
headers which, while legal according to RFC 822, make use of
backslash-quoting to "hide" special characters such as "<", ",", or
":", or which exploit other infrequently-used features of that
specification.
While it is unfortunate that these programs do not correctly
interpret RFC 822 headers, to "break" these programs would cause
severe operational problems for the Internet mail system. The
extensions described in this memo therefore do not rely on little-
used features of RFC 822.
Moore Standards Track [Page 2]
RFC 2047 Message Header Extensions November 1996
Instead, certain sequences of "ordinary" printable ASCII characters
(known as "encoded-words") are reserved for use as encoded data. The
syntax of encoded-words is such that they are unlikely to
"accidentally" appear as normal text in message headers.
Furthermore, the characters used in encoded-words are restricted to
those which do not have special meanings in the context in which the
encoded-word appears.
Generally, an "encoded-word" is a sequence of printable ASCII
characters that begins with "=?", ends with "?=", and has two "?"s in
between. It specifies a character set and an encoding method, and
also includes the original text encoded as graphic ASCII characters,
according to the rules for that encoding method.
A mail composer that implements this specification will provide a
means of inputting non-ASCII text in header fields, but will
translate these fields (or appropriate portions of these fields) into
encoded-words before inserting them into the message header.
A mail reader that implements this specification will recognize
encoded-words when they appear in certain portions of the message
header. Instead of displaying the encoded-word "as is", it will
reverse the encoding and display the original text in the designated
character set.
NOTES
This memo relies heavily on notation and terms defined RFC 822 and
RFC 2045. In particular, the syntax for the ABNF used in this memo
is defined in RFC 822, as well as many of the terminal or nonterminal
symbols from RFC 822 are used in the grammar for the header
extensions defined here. Among the symbols defined in RFC 822 and
referenced in this memo are: 'addr-spec', 'atom', 'CHAR', 'comment',
'CTLs', 'ctext', 'linear-white-space', 'phrase', 'quoted-pair'.
'quoted-string', 'SPACE', and 'word'. Successful implementation of
this protocol extension requires careful attention to the RFC 822
definitions of these terms.
When the term "ASCII" appears in this memo, it refers to the "7-Bit
American Standard Code for Information Interchange", ANSI X3.4-1986.
The MIME charset name for this character set is "US-ASCII". When not
specifically referring to the MIME charset name, this document uses
the term "ASCII", both for brevity and for consistency with RFC 822.
However, implementors are warned that the character set name must be
spelled "US-ASCII" in MIME message and body part headers.
Moore Standards Track [Page 3]
RFC 2047 Message Header Extensions November 1996
This memo specifies a protocol for the representation of non-ASCII
text in message headers. It specifically DOES NOT define any
translation between "8-bit headers" and pure ASCII headers, nor is
any such translation assumed to be possible.
2. Syntax of encoded-words
An 'encoded-word' is defined by the following ABNF grammar. The
notation of RFC 822 is used, with the exception that white space
characters MUST NOT appear between components of an 'encoded-word'.
encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
charset = token ; see section 3
encoding = token ; see section 4
token = 1*<Any CHAR except SPACE, CTLs, and especials>
especials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "
<"> / "/" / "[" / "]" / "?" / "." / "="
encoded-text = 1*<Any printable ASCII character other than "?"
or SPACE>
; (but see "Use of encoded-words in message
; headers", section 5)
Both 'encoding' and 'charset' names are case-independent. Thus the
charset name "ISO-8859-1" is equivalent to "iso-8859-1", and the
encoding named "Q" may be spelled either "Q" or "q".
An 'encoded-word' may not be more than 75 characters long, including
'charset', 'encoding', 'encoded-text', and delimiters. If it is
desirable to encode more text than will fit in an 'encoded-word' of
75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may
be used.
While there is no limit to the length of a multiple-line header
field, each line of a header field that contains one or more
'encoded-word's is limited to 76 characters.
The length restrictions are included both to ease interoperability
through internetwork mail gateways, and to impose a limit on the
amount of lookahead a header parser must employ (while looking for a
final ?= delimiter) before it can decide whether a token is an
"encoded-word" or something else.
Moore Standards Track [Page 4]
RFC 2047 Message Header Extensions November 1996
IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's
by an RFC 822 parser. As a consequence, unencoded white space
characters (such as SPACE and HTAB) are FORBIDDEN within an
'encoded-word'. For example, the character sequence
=?iso-8859-1?q?this is some text?=
would be parsed as four 'atom's, rather than as a single 'atom' (by
an RFC 822 parser) or 'encoded-word' (by a parser which understands
'encoded-words'). The correct way to encode the string "this is some
text" is to encode the SPACE characters as well, e.g.
=?iso-8859-1?q?this=20is=20some=20text?=
The characters which may appear in 'encoded-text' are further
restricted by the rules in section 5.
3. Character sets
The 'charset' portion of an 'encoded-word' specifies the character
set associated with the unencoded text. A 'charset' can be any of
the character set names allowed in an MIME "charset" parameter of a
"text/plain" body part, or any character set name registered with
IANA for use with the MIME text/plain content-type.
Some character sets use code-switching techniques to switch between
"ASCII mode" and other modes. If unencoded text in an 'encoded-word'
contains a sequence which causes the charset interpreter to switch
out of ASCII mode, it MUST contain additional control codes such that
ASCII mode is again selected at the end of the 'encoded-word'. (This
rule applies separately to each 'encoded-word', including adjacent
'encoded-word's within a single header field.)
When there is a possibility of using more than one character set to
represent the text in an 'encoded-word', and in the absence of
private agreements between sender and recipients of a message, it is
recommended that members of the ISO-8859-* series be used in
preference to other character sets.
4. Encodings
Initially, the legal values for "encoding" are "Q" and "B". These
encodings are described below. The "Q" encoding is recommended for
use when most of the characters to be encoded are in the ASCII
character set; otherwise, the "B" encoding should be used.
Nevertheless, a mail reader which claims to recognize 'encoded-word's
MUST be able to accept either encoding for any character set which it
supports.
Moore Standards Track [Page 5]
RFC 2047 Message Header Extensions November 1996
Only a subset of the printable ASCII characters may be used in
'encoded-text'. Space and tab characters are not allowed, so that
the beginning and end of an 'encoded-word' are obvious. The "?"
character is used within an 'encoded-word' to separate the various
portions of the 'encoded-word' from one another, and thus cannot
appear in the 'encoded-text' portion. Other characters are also
illegal in certain contexts. For example, an 'encoded-word' in a
'phrase' preceding an address in a From header field may not contain
any of the "specials" defined in RFC 822. Finally, certain other
characters are disallowed in some contexts, to ensure reliability for
messages that pass through internetwork mail gateways.
The "B" encoding automatically meets these requirements. The "Q"
encoding allows a wide range of printable characters to be used in
non-critical locations in the message header (e.g., Subject), with
fewer characters available for use in other locations.
4.1. The "B" encoding
The "B" encoding is identical to the "BASE64" encoding defined by RFC
2045.
4.2. The "Q" encoding
The "Q" encoding is similar to the "Quoted-Printable" content-
transfer-encoding defined in RFC 2045. It is designed to allow text
containing mostly ASCII characters to be decipherable on an ASCII
terminal without decoding.
(1) Any 8-bit value may be represented by a "=" followed by two
hexadecimal digits. For example, if the character set in use
were ISO-8859-1, the "=" character would thus be encoded as
"=3D", and a SPACE by "=20". (Upper case should be used for
hexadecimal digits "A" through "F".)
(2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be
represented as "_" (underscore, ASCII 95.). (This character may
not pass through some internetwork mail gateways, but its use
will greatly enhance readability of "Q" encoded data with mail
readers that do not support this encoding.) Note that the "_"
always represents hexadecimal 20, even if the SPACE character
occupies a different code position in the character set in use.
(3) 8-bit values which correspond to printable ASCII characters other
than "=", "?", and "_" (underscore), MAY be represented as those
characters. (But see section 5 for restrictions.) In
particular, SPACE and TAB MUST NOT be represented as themselves
within encoded words.
Moore Standards Track [Page 6]
RFC 2047 Message Header Extensions November 1996
5. Use of encoded-words in message headers
An 'encoded-word' may appear in a message header or body part header
according to the following rules:
(1) An 'encoded-word' may replace a 'text' token (as defined by RFC 822)
in any Subject or Comments header field, any extension message
header field, or any MIME body part field for which the field body
is defined as '*text'. An 'encoded-word' may also appear in any
user-defined ("X-") message or body part header field.
Ordinary ASCII text and 'encoded-word's may appear together in the
same header field. However, an 'encoded-word' that appears in a
header field defined as '*text' MUST be separated from any adjacent
'encoded-word' or 'text' by 'linear-white-space'.
(2) An 'encoded-word' may appear within a 'comment' delimited by "(" and
")", i.e., wherever a 'ctext' is allowed. More precisely, the RFC
822 ABNF definition for 'comment' is amended as follows:
comment = "(" *(ctext / quoted-pair / comment / encoded-word) ")"
A "Q"-encoded 'encoded-word' which appears in a 'comment' MUST NOT
contain the characters "(", ")" or "
'encoded-word' that appears in a 'comment' MUST be separated from
any adjacent 'encoded-word' or 'ctext' by 'linear-white-space'.
It is important to note that 'comment's are only recognized inside
"structured" field bodies. In fields whose bodies are defined as
'*text', "(" and ")" are treated as ordinary characters rather than
comment delimiters, and rule (1) of this section applies. (See RFC
822, sections 3.1.2 and 3.1.3)
(3) As a replacement for a 'word' entity within a 'phrase', for example,
one that precedes an address in a From, To, or Cc header. The ABNF
definition for 'phrase' from RFC 822 thus becomes:
phrase = 1*( encoded-word / word )
In this case the set of characters that may be used in a "Q"-encoded
'encoded-word' is restricted to: <upper and lower case ASCII
letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_"
(underscore, ASCII 95.)>. An 'encoded-word' that appears within a
'phrase' MUST be separated from any adjacent 'word', 'text' or
'special' by 'linear-white-space'.
Moore Standards Track [Page 7]
RFC 2047 Message Header Extensions November 1996
These are the ONLY locations where an 'encoded-word' may appear. In
particular:
+ An 'encoded-word' MUST NOT appear in any portion of an 'addr-spec'.
+ An 'encoded-word' MUST NOT appear within a 'quoted-string'.
+ An 'encoded-word' MUST NOT be used in a Received header field.
+ An 'encoded-word' MUST NOT be used in parameter of a MIME
Content-Type or Content-Disposition field, or in any structured
field body except within a 'comment' or 'phrase'.
The 'encoded-text' in an 'encoded-word' must be self-contained;
'encoded-text' MUST NOT be continued from one 'encoded-word' to
another. This implies that the 'encoded-text' portion of a "B"
'encoded-word' will be a multiple of 4 characters long; for a "Q"
'encoded-word', any "=" character that appears in the 'encoded-text'
portion will be followed by two hexadecimal characters.
Each 'encoded-word' MUST encode an integral number of octets. The
'encoded-text' in each 'encoded-word' must be well-formed according
to the encoding specified; the 'encoded-text' may not be continued in
the next 'encoded-word'. (For example, "=?charset?Q?=?=
=?charset?Q?AB?=" would be illegal, because the two hex digits "AB"
must follow the "=" in the same 'encoded-word'.)
Each 'encoded-word' MUST represent an integral number of characters.
A multi-octet character may not be split across adjacent 'encoded-
word's.
Only printable and white space character data should be encoded using
this scheme. However, since these encoding schemes allow the
encoding of arbitrary octet values, mail readers that implement this
decoding should also ensure that display of the decoded data on the
recipient's terminal will not cause unwanted side-effects.
Use of these methods to encode non-textual data (e.g., pictures or
sounds) is not defined by this memo. Use of 'encoded-word's to
represent strings of purely ASCII characters is allowed, but
discouraged. In rare cases it may be necessary to encode ordinary
text that looks like an 'encoded-word'.
Moore Standards Track [Page 8]
RFC 2047 Message Header Extensions November 1996
6. Support of 'encoded-word's by mail readers
6.1. Recognition of 'encoded-word's in message headers
A mail reader must parse the message and body part headers according
to the rules in RFC 822 to correctly recognize 'encoded-word's.
'encoded-word's are to be recognized as follows:
(1) Any message or body part header field defined as '*text', or any
user-defined header field, should be parsed as follows: Beginning
at the start of the field-body and immediately following each
occurrence of 'linear-white-space', each sequence of up to 75
printable characters (not containing any 'linear-white-space')
should be examined to see if it is an 'encoded-word' according to
the syntax rules in section 2. Any other sequence of printable
characters should be treated as ordinary ASCII text.
(2) Any header field not defined as '*text' should be parsed
according to the syntax rules for that header field. However,
any 'word' that appears within a 'phrase' should be treated as an
'encoded-word' if it meets the syntax rules in section 2.
Otherwise it should be treated as an ordinary 'word'.
(3) Within a 'comment', any sequence of up to 75 printable characters
(not containing 'linear-white-space'), that meets the syntax
rules in section 2, should be treated as an 'encoded-word'.
Otherwise it should be treated as normal comment text.
(4) A MIME-Version header field is NOT required to be present for
'encoded-word's to be interpreted according to this
specification. One reason for this is that the mail reader is
not expected to parse the entire message header before displaying
lines that may contain 'encoded-word's.
6.2. Display of 'encoded-word's
Any 'encoded-word's so recognized are decoded, and if possible, the
resulting unencoded text is displayed in the original character set.
NOTE: Decoding and display of encoded-words occurs *after* a
structured field body is parsed into tokens. It is therefore
possible to hide 'special' characters in encoded-words which, when
displayed, will be indistinguishable from 'special' characters in the
surrounding text. For this and other reasons, it is NOT generally
possible to translate a message header containing 'encoded-word's to
an unencoded form which can be parsed by an RFC 822 mail reader.
Moore Standards Track [Page 9]
RFC 2047 Message Header Extensions November 1996
When displaying a particular header field that contains multiple
'encoded-word's, any 'linear-white-space' that separates a pair of
adjacent 'encoded-word's is ignored. (This is to allow the use of
multiple 'encoded-word's to represent long strings of unencoded text,
without having to separate 'encoded-word's where spaces occur in the
unencoded text.)
In the event other encodings are defined in the future, and the mail
reader does not support the encoding used, it may either (a) display
the 'encoded-word' as ordinary text, or (b) substitute an appropriate
message indicating that the text could not be decoded.
If the mail reader does not support the character set used, it may
(a) display the 'encoded-word' as ordinary text (i.e., as it appears
in the header), (b) make a "best effort" to display using such
characters as are available, or (c) substitute an appropriate message
indicating that the decoded text could not be displayed.
If the character set being used employs code-switching techniques,
display of the encoded text implicitly begins in "ASCII mode". In
addition, the mail reader must ensure that the output device is once
again in "ASCII mode" after the 'encoded-word' is displayed.
6.3. Mail reader handling of incorrectly formed 'encoded-word's
It is possible that an 'encoded-word' that is legal according to the
syntax defined in section 2, is incorrectly formed according to the
rules for the encoding being used. For example:
(1) An 'encoded-word' which contains characters which are not legal
for a particular encoding (for example, a "-" in the "B"
encoding, or a SPACE or HTAB in either the "B" or "Q" encoding),
is incorrectly formed.
(2) Any 'encoded-word' which encodes a non-integral number of
characters or octets is incorrectly formed.
A mail reader need not attempt to display the text associated with an
'encoded-word' that is incorrectly formed. However, a mail reader
MUST NOT prevent the display or handling of a message because an
'encoded-word' is incorrectly formed.
7. Conformance
A mail composing program claiming compliance with this specification
MUST ensure that any string of non-white-space printable ASCII
characters within a '*text' or '*ctext' that begins with "=?" and
ends with "?=" be a valid 'encoded-word'. ("begins" means: at the
Moore Standards Track [Page 10]
RFC 2047 Message Header Extensions November 1996
start of the field-body, immediately following 'linear-white-space',
or immediately following a "(" for an 'encoded-word' within '*ctext';
"ends" means: at the end of the field-body, immediately preceding
'linear-white-space', or immediately preceding a ")" for an
'encoded-word' within '*ctext'.) In addition, any 'word' within a
'phrase' that begins with "=?" and ends with "?=" must be a valid
'encoded-word'.
A mail reading program claiming compliance with this specification
must be able to distinguish 'encoded-word's from 'text', 'ctext', or
'word's, according to the rules in section 6, anytime they appear in
appropriate places in message headers. It must support both the "B"
and "Q" encodings for any character set which it supports. The
program must be able to display the unencoded text if the character
set is "US-ASCII". For the ISO-8859-* character sets, the mail
reading program must at least be able to display the characters which
are also in the ASCII set.
8. Examples
The following are examples of message headers containing 'encoded-
word's:
From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
Note: In the first 'encoded-word' of the Subject field above, the
last "=" at the end of the 'encoded-text' is necessary because each
'encoded-word' must be self-contained (the "=" character completes a
group of 4 base64 characters representing 2 octets). An additional
octet could have been encoded in the first 'encoded-word' (so that
the encoded-word would contain an exact multiple of 3 encoded
octets), except that the second 'encoded-word' uses a different
'charset' than the first one.
From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
Subject: Time for ISO 10646?
To: Dave Crocker <dcrocker@mordor.stanford.edu>
Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
Subject: Re: RFC-HDR care and feeding
Moore Standards Track [Page 11]
RFC 2047 Message Header Extensions November 1996
From: Nathaniel Borenstein <nsb@thumper.bellcore.com>
(=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed
<ned@innosoft.com>, Keith Moore <moore@cs.utk.edu>
Subject: Test of new header generator
MIME-Version: 1.0
Content-type: text/plain; charset=ISO-8859-1
The following examples illustrate how text containing 'encoded-word's
which appear in a structured field body. The rules are slightly
different for fields defined as '*text' because "(" and ")" are not
recognized as 'comment' delimiters. [Section 5, paragraph (1)].
In each of the following examples, if the same sequence were to occur
in a '*text' field, the "displayed as" form would NOT be treated as
encoded words, but be identical to the "encoded form". This is
because each of the encoded-words in the following examples is
adjacent to a "(" or ")" character.
encoded form displayed as
---------------------------------------------------------------------
(=?ISO-8859-1?Q?a?=) (a)
(=?ISO-8859-1?Q?a?= b) (a b)
Within a 'comment', white space MUST appear between an
'encoded-word' and surrounding text. [Section 5,
paragraph (2)]. However, white space is not needed between
the initial "(" that begins the 'comment', and the
'encoded-word'.
(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) (ab)
White space between adjacent 'encoded-word's is not
displayed.
(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) (ab)
Even multiple SPACEs between 'encoded-word's are ignored
for the purpose of display.
(=?ISO-8859-1?Q?a?= (ab)
=?ISO-8859-1?Q?b?=)
Any amount of linear-space-white between 'encoded-word's,
even if it includes a CRLF followed by one or more SPACEs,
is ignored for the purposes of display.
Moore Standards Track [Page 12]
RFC 2047 Message Header Extensions November 1996
(=?ISO-8859-1?Q?a_b?=) (a b)
In order to cause a SPACE to be displayed within a portion
of encoded text, the SPACE MUST be encoded as part of the
'encoded-word'.
(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=) (a b)
In order to cause a SPACE to be displayed between two strings
of encoded text, the SPACE MAY be encoded as part of one of
the 'encoded-word's.
9. References
[RFC 822] Crocker, D., "Standard for the Format of ARPA Internet Text
Messages", STD 11, RFC 822, UDEL, August 1982.
[RFC 2049] Borenstein, N., and N. Freed, "Multipurpose Internet Mail
Extensions (MIME) Part Five: Conformance Criteria and Examples",
RFC 2049, November 1996.
[RFC 2045] Borenstein, N., and N. Freed, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message Bodies",
RFC 2045, November 1996.
[RFC 2046] Borenstein N., and N. Freed, "Multipurpose Internet Mail
Extensions (MIME) Part Two: Media Types", RFC 2046,
November 1996.
[RFC 2048] Freed, N., Klensin, J., and J. Postel, "Multipurpose
Internet Mail Extensions (MIME) Part Four: Registration
Procedures", RFC 2048, November 1996.
Moore Standards Track [Page 13]
RFC 2047 Message Header Extensions November 1996
10. Security Considerations
Security issues are not discussed in this memo.
11. Acknowledgements
The author wishes to thank Nathaniel Borenstein, Issac Chan, Lutz
Donnerhacke, Paul Eggert, Ned Freed, Andreas M. Kirchwitz, Olle
Jarnefors, Mike Rosin, Yutaka Sato, Bart Schaefer, and Kazuhiko
Yamamoto, for their helpful advice, insightful comments, and
illuminating questions in response to earlier versions of this
specification.
12. Author's Address
Keith Moore
University of Tennessee
107 Ayres Hall
Knoxville TN 37996-1301
EMail: moore@cs.utk.edu
Moore Standards Track [Page 14]
RFC 2047 Message Header Extensions November 1996
Appendix - changes since RFC 1522 (in no particular order)
+ explicitly state that the MIME-Version is not requried to use
'encoded-word's.
+ add explicit note that SPACEs and TABs are not allowed within
'encoded-word's, explaining that an 'encoded-word' must look like an
'atom' to an RFC822 parser.values, to be precise).
+ add examples from Olle Jarnefors (thanks!) which illustrate how
encoded-words with adjacent linear-white-space are displayed.
+ explicitly list terms defined in RFC822 and referenced in this memo
+ fix transcription typos that caused one or two lines and a couple of
characters to disappear in the resulting text, due to nroff quirks.
+ clarify that encoded-words are allowed in '*text' fields in both
RFC822 headers and MIME body part headers, but NOT as parameter
values.
+ clarify the requirement to switch back to ASCII within the encoded
portion of an 'encoded-word', for any charset that uses code switching
sequences.
+ add a note about 'encoded-word's being delimited by "(" and ")"
within a comment, but not in a *text (how bizarre!).
+ fix the Andre Pirard example to get rid of the trailing "_" after
the =E9. (no longer needed post-1342).
+ clarification: an 'encoded-word' may appear immediately following
the initial "(" or immediately before the final ")" that delimits a
comment, not just adjacent to "(" and ")" *within* *ctext.
+ add a note to explain that a "B" 'encoded-word' will always have a
multiple of 4 characters in the 'encoded-text' portion.
+ add note about the "=" in the examples
+ note that processing of 'encoded-word's occurs *after* parsing, and
some of the implications thereof.
+ explicitly state that you can't expect to translate between
1522 and either vanilla 822 or so-called "8-bit headers".
+ explicitly state that 'encoded-word's are not valid within a
'quoted-string'.
Moore Standards Track [Page 15]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,675 @@
Network Working Group R. Troost
Request for Comments: 2183 New Century Systems
Updates: 1806 S. Dorner
Category: Standards Track QUALCOMM Incorporated
K. Moore, Editor
University of Tennessee
August 1997
Communicating Presentation Information in
Internet Messages:
The Content-Disposition Header Field
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Abstract
This memo provides a mechanism whereby messages conforming to the
MIME specifications [RFC 2045, RFC 2046, RFC 2047, RFC 2048, RFC
2049] can convey presentational information. It specifies the
"Content-Disposition" header field, which is optional and valid for
any MIME entity ("message" or "body part"). Two values for this
header field are described in this memo; one for the ordinary linear
presentation of the body part, and another to facilitate the use of
mail to transfer files. It is expected that more values will be
defined in the future, and procedures are defined for extending this
set of values.
This document is intended as an extension to MIME. As such, the
reader is assumed to be familiar with the MIME specifications, and
[RFC 822]. The information presented herein supplements but does not
replace that found in those documents.
This document is a revision to the Experimental protocol defined in
RFC 1806. As compared to RFC 1806, this document contains minor
editorial updates, adds new parameters needed to support the File
Transfer Body Part, and references a separate specification for the
handling of non-ASCII and/or very long parameter values.
Troost, et. al. Standards Track [Page 1]
RFC 2183 Content-Disposition August 1997
1. Introduction
MIME specifies a standard format for encapsulating multiple pieces of
data into a single Internet message. That document does not address
the issue of presentation styles; it provides a framework for the
interchange of message content, but leaves presentation issues solely
in the hands of mail user agent (MUA) implementors.
Two common ways of presenting multipart electronic messages are as a
main document with a list of separate attachments, and as a single
document with the various parts expanded (displayed) inline. The
display of an attachment is generally construed to require positive
action on the part of the recipient, while inline message components
are displayed automatically when the message is viewed. A mechanism
is needed to allow the sender to transmit this sort of presentational
information to the recipient; the Content-Disposition header provides
this mechanism, allowing each component of a message to be tagged
with an indication of its desired presentation semantics.
Tagging messages in this manner will often be sufficient for basic
message formatting. However, in many cases a more powerful and
flexible approach will be necessary. The definition of such
approaches is beyond the scope of this memo; however, such approaches
can benefit from additional Content-Disposition values and
parameters, to be defined at a later date.
In addition to allowing the sender to specify the presentational
disposition of a message component, it is desirable to allow her to
indicate a default archival disposition; a filename. The optional
"filename" parameter provides for this. Further, the creation-date,
modification-date, and read-date parameters allow preservation of
those file attributes when the file is transmitted over MIME email.
NB: The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD,
SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when they appear in this
document, are to be interpreted as described in [RFC 2119].
2. The Content-Disposition Header Field
Content-Disposition is an optional header field. In its absence, the
MUA may use whatever presentation method it deems suitable.
It is desirable to keep the set of possible disposition types small
and well defined, to avoid needless complexity. Even so, evolving
usage will likely require the definition of additional disposition
types or parameters, so the set of disposition values is extensible;
see below.
Troost, et. al. Standards Track [Page 2]
RFC 2183 Content-Disposition August 1997
In the extended BNF notation of [RFC 822], the Content-Disposition
header field is defined as follows:
disposition := "Content-Disposition" ":"
disposition-type
*(";" disposition-parm)
disposition-type := "inline"
/ "attachment"
/ extension-token
; values are not case-sensitive
disposition-parm := filename-parm
/ creation-date-parm
/ modification-date-parm
/ read-date-parm
/ size-parm
/ parameter
filename-parm := "filename" "=" value
creation-date-parm := "creation-date" "=" quoted-date-time
modification-date-parm := "modification-date" "=" quoted-date-time
read-date-parm := "read-date" "=" quoted-date-time
size-parm := "size" "=" 1*DIGIT
quoted-date-time := quoted-string
; contents MUST be an RFC 822 `date-time'
; numeric timezones (+HHMM or -HHMM) MUST be used
NOTE ON PARAMETER VALUE LENGHTS: A short (length <= 78 characters)
parameter value containing only non-`tspecials' characters SHOULD be
represented as a single `token'. A short parameter value containing
only ASCII characters, but including `tspecials' characters, SHOULD
be represented as `quoted-string'. Parameter values longer than 78
characters, or which contain non-ASCII characters, MUST be encoded as
specified in [RFC 2184].
`Extension-token', `parameter', `tspecials' and `value' are defined
according to [RFC 2045] (which references [RFC 822] in the definition
of some of these tokens). `quoted-string' and `DIGIT' are defined in
[RFC 822].
Troost, et. al. Standards Track [Page 3]
RFC 2183 Content-Disposition August 1997
2.1 The Inline Disposition Type
A bodypart should be marked `inline' if it is intended to be
displayed automatically upon display of the message. Inline
bodyparts should be presented in the order in which they occur,
subject to the normal semantics of multipart messages.
2.2 The Attachment Disposition Type
Bodyparts can be designated `attachment' to indicate that they are
separate from the main body of the mail message, and that their
display should not be automatic, but contingent upon some further
action of the user. The MUA might instead present the user of a
bitmap terminal with an iconic representation of the attachments, or,
on character terminals, with a list of attachments from which the
user could select for viewing or storage.
2.3 The Filename Parameter
The sender may want to suggest a filename to be used if the entity is
detached and stored in a separate file. If the receiving MUA writes
the entity to a file, the suggested filename should be used as a
basis for the actual filename, where possible.
It is important that the receiving MUA not blindly use the suggested
filename. The suggested filename SHOULD be checked (and possibly
changed) to see that it conforms to local filesystem conventions,
does not overwrite an existing file, and does not present a security
problem (see Security Considerations below).
The receiving MUA SHOULD NOT respect any directory path information
that may seem to be present in the filename parameter. The filename
should be treated as a terminal component only. Portable
specification of directory paths might possibly be done in the future
via a separate Content-Disposition parameter, but no provision is
made for it in this draft.
Current [RFC 2045] grammar restricts parameter values (and hence
Content-Disposition filenames) to US-ASCII. We recognize the great
desirability of allowing arbitrary character sets in filenames, but
it is beyond the scope of this document to define the necessary
mechanisms. We expect that the basic [RFC 1521] `value'
specification will someday be amended to allow use of non-US-ASCII
characters, at which time the same mechanism should be used in the
Content-Disposition filename parameter.
Troost, et. al. Standards Track [Page 4]
RFC 2183 Content-Disposition August 1997
Beyond the limitation to US-ASCII, the sending MUA may wish to bear
in mind the limitations of common filesystems. Many have severe
length and character set restrictions. Short alphanumeric filenames
are least likely to require modification by the receiving system.
The presence of the filename parameter does not force an
implementation to write the entity to a separate file. It is
perfectly acceptable for implementations to leave the entity as part
of the normal mail stream unless the user requests otherwise. As a
consequence, the parameter may be used on any MIME entity, even
`inline' ones. These will not normally be written to files, but the
parameter could be used to provide a filename if the receiving user
should choose to write the part to a file.
2.4 The Creation-Date parameter
The creation-date parameter MAY be used to indicate the date at which
the file was created. If this parameter is included, the paramter
value MUST be a quoted-string which contains a representation of the
creation date of the file in [RFC 822] `date-time' format.
UNIX and POSIX implementors are cautioned that the `st_ctime' file
attribute of the `stat' structure is not the creation time of the
file; it is thus not appropriate as a source for the creation-date
parameter value.
2.5 The Modification-Date parameter
The modification-date parameter MAY be used to indicate the date at
which the file was last modified. If the modification-date parameter
is included, the paramter value MUST be a quoted-string which
contains a representation of the last modification date of the file
in [RFC 822] `date-time' format.
2.6 The Read-Date parameter
The read-date parameter MAY be used to indicate the date at which the
file was last read. If the read-date parameter is included, the
parameter value MUST be a quoted-string which contains a
representation of the last-read date of the file in [RFC 822] `date-
time' format.
2.7 The Size parameter
The size parameter indicates an approximate size of the file in
octets. It can be used, for example, to pre-allocate space before
attempting to store the file, or to determine whether enough space
exists.
Troost, et. al. Standards Track [Page 5]
RFC 2183 Content-Disposition August 1997
2.8 Future Extensions and Unrecognized Disposition Types
In the likely event that new parameters or disposition types are
needed, they should be registered with the Internet Assigned Numbers
Authority (IANA), in the manner specified in Section 9 of this memo.
Once new disposition types and parameters are defined, there is of
course the likelihood that implementations will see disposition types
and parameters they do not understand. Furthermore, since x-tokens
are allowed, implementations may also see entirely unregistered
disposition types and parameters.
Unrecognized parameters should be ignored. Unrecognized disposition
types should be treated as `attachment'. The choice of `attachment'
for unrecognized types is made because a sender who goes to the
trouble of producing a Content-Disposition header with a new
disposition type is more likely aiming for something more elaborate
than inline presentation.
Unless noted otherwise in the definition of a parameter, Content-
Disposition parameters are valid for all dispositions. (In contrast
to MIME content-type parameters, which are defined on a per-content-
type basis.) Thus, for example, the `filename' parameter still means
the name of the file to which the part should be written, even if the
disposition itself is unrecognized.
2.9 Content-Disposition and Multipart
If a Content-Disposition header is used on a multipart body part, it
applies to the multipart as a whole, not the individual subparts.
The disposition types of the subparts do not need to be consulted
until the multipart itself is presented. When the multipart is
displayed, then the dispositions of the subparts should be respected.
If the `inline' disposition is used, the multipart should be
displayed as normal; however, an `attachment' subpart should require
action from the user to display.
If the `attachment' disposition is used, presentation of the
multipart should not proceed without explicit user action. Once the
user has chosen to display the multipart, the individual subpart
dispositions should be consulted to determine how to present the
subparts.
Troost, et. al. Standards Track [Page 6]
RFC 2183 Content-Disposition August 1997
2.10 Content-Disposition and the Main Message
It is permissible to use Content-Disposition on the main body of an
[RFC 822] message.
3. Examples
Here is a an example of a body part containing a JPEG image that is
intended to be viewed by the user immediately:
Content-Type: image/jpeg
Content-Disposition: inline
Content-Description: just a small picture of me
<jpeg data>
The following body part contains a JPEG image that should be
displayed to the user only if the user requests it. If the JPEG is
written to a file, the file should be named "genome.jpg". The
recipient's user might also choose to set the last-modified date of
the stored file to date in the modification-date parameter:
Content-Type: image/jpeg
Content-Disposition: attachment; filename=genome.jpeg;
modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
Content-Description: a complete map of the human genome
<jpeg data>
The following is an example of the use of the `attachment'
disposition with a multipart body part. The user should see text-
part-1 immediately, then take some action to view multipart-2. After
taking action to view multipart-2, the user will see text-part-2
right away, and be required to take action to view jpeg-1. Subparts
are indented for clarity; they would not be so indented in a real
message.
Troost, et. al. Standards Track [Page 7]
RFC 2183 Content-Disposition August 1997
Content-Type: multipart/mixed; boundary=outer
Content-Description: multipart-1
--outer
Content-Type: text/plain
Content-Disposition: inline
Content-Description: text-part-1
Some text goes here
--outer
Content-Type: multipart/mixed; boundary=inner
Content-Disposition: attachment
Content-Description: multipart-2
--inner
Content-Type: text/plain
Content-Disposition: inline
Content-Description: text-part-2
Some more text here.
--inner
Content-Type: image/jpeg
Content-Disposition: attachment
Content-Description: jpeg-1
<jpeg data>
--inner--
--outer--
4. Summary
Content-Disposition takes one of two values, `inline' and
`attachment'. `Inline' indicates that the entity should be
immediately displayed to the user, whereas `attachment' means that
the user should take additional action to view the entity.
The `filename' parameter can be used to suggest a filename for
storing the bodypart, if the user wishes to store it in an external
file.
Troost, et. al. Standards Track [Page 8]
RFC 2183 Content-Disposition August 1997
5. Security Considerations
There are security issues involved any time users exchange data.
While these are not to be minimized, neither does this memo change
the status quo in that regard, except in one instance.
Since this memo provides a way for the sender to suggest a filename,
a receiving MUA must take care that the sender's suggested filename
does not represent a hazard. Using UNIX as an example, some hazards
would be:
+ Creating startup files (e.g., ".login").
+ Creating or overwriting system files (e.g., "/etc/passwd").
+ Overwriting any existing file.
+ Placing executable files into any command search path
(e.g., "~/bin/more").
+ Sending the file to a pipe (e.g., "| sh").
In general, the receiving MUA should not name or place the file such
that it will get interpreted or executed without the user explicitly
initiating the action.
It is very important to note that this is not an exhaustive list; it
is intended as a small set of examples only. Implementors must be
alert to the potential hazards on their target systems.
6. References
[RFC 2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement
Levels", RFC 2119, March 1997.
[RFC 2184]
Freed, N. and K. Moore, "MIME Parameter value and Encoded Words:
Character Sets, Lanaguage, and Continuations", RFC 2184, August
1997.
[RFC 2045]
Freed, N. and N. Borenstein, "MIME (Multipurpose Internet Mail
Extensions) Part One: Format of Internet Message Bodies", RFC
2045, December 1996.
Troost, et. al. Standards Track [Page 9]
RFC 2183 Content-Disposition August 1997
[RFC 2046]
Freed, N. and N. Borenstein, "MIME (Multipurpose Internet Mail
Extensions) Part Two: Media Types", RFC 2046, December 1996.
[RFC 2047]
Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part
Three: Message Header Extensions for non-ASCII Text", RFC 2047,
December 1996.
[RFC 2048]
Freed, N., Klensin, J. and J. Postel, "MIME (Multipurpose
Internet Mail Extensions) Part Four: Registration Procedures",
RFC 2048, December 1996.
[RFC 2049]
Freed, N. and N. Borenstein, "MIME (Multipurpose Internet Mail
Extensions) Part Five: Conformance Criteria and Examples", RFC
2049, December 1996.
[RFC 822]
Crocker, D., "Standard for the Format of ARPA Internet Text
Messages", STD 11, RFC 822, UDEL, August 1982.
7. Acknowledgements
We gratefully acknowledge the help these people provided during the
preparation of this draft:
Nathaniel Borenstein
Ned Freed
Keith Moore
Dave Crocker
Dan Pritchett
Troost, et. al. Standards Track [Page 10]
RFC 2183 Content-Disposition August 1997
8. Authors' Addresses
You should blame the editor of this version of the document for any
changes since RFC 1806:
Keith Moore
Department of Computer Science
University of Tennessee, Knoxville
107 Ayres Hall
Knoxville TN 37996-1301
USA
Phone: +1 (423) 974-5067
Fax: +1 (423) 974-8296
Email: moore@cs.utk.edu
The authors of RFC 1806 are:
Rens Troost
New Century Systems
324 East 41st Street #804
New York, NY, 10017 USA
Phone: +1 (212) 557-2050
Fax: +1 (212) 557-2049
EMail: rens@century.com
Steve Dorner
QUALCOMM Incorporated
6455 Lusk Boulevard
San Diego, CA 92121
USA
EMail: sdorner@qualcomm.com
9. Registration of New Content-Disposition Values and Parameters
New Content-Disposition values (besides "inline" and "attachment")
may be defined only by Internet standards-track documents, or in
Experimental documents approved by the Internet Engineering Steering
Group.
Troost, et. al. Standards Track [Page 11]
RFC 2183 Content-Disposition August 1997
New content-disposition parameters may be registered by supplying the
information in the following template and sending it via electronic
mail to IANA@IANA.ORG:
To: IANA@IANA.ORG
Subject: Registration of new Content-Disposition parameter
Content-Disposition parameter name:
Allowable values for this parameter:
(If the parameter can only assume a small number of values,
list each of those values. Otherwise, describe the values
that the parameter can assume.)
Description:
(What is the purpose of this parameter and how is it used?)
10. Changes since RFC 1806
The following changes have been made since the earlier version of
this document, published in RFC 1806 as an Experimental protocol:
+ Updated references to MIME documents. In some cases this
involved substituting a reference to one of the current MIME
RFCs for a reference to RFC 1521; in other cases, a reference to
RFC 1521 was simply replaced with the word "MIME".
+ Added a section on registration procedures, since none of the
procedures in RFC 2048 seemed to be appropriate.
+ Added new parameter types: creation-date, modification-date,
read-date, and size.
+ Incorporated a reference to draft-freed-pvcsc-* for encoding
long or non-ASCII parameter values.
+ Added reference to RFC 2119 to define MUST, SHOULD, etc.
keywords.
Troost, et. al. Standards Track [Page 12]

View File

@ -0,0 +1,899 @@
Network Working Group J. Myers
Request for Comments: 2222 Netscape Communications
Category: Standards Track October 1997
Simple Authentication and Security Layer (SASL)
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (1997). All Rights Reserved.
Table of Contents
1. Abstract .............................................. 2
2. Organization of this Document ......................... 2
2.1. How to Read This Document ............................. 2
2.2. Conventions Used in this Document ..................... 2
2.3. Examples .............................................. 3
3. Introduction and Overview ............................. 3
4. Profiling requirements ................................ 4
5. Specific issues ....................................... 5
5.1. Client sends data first ............................... 5
5.2. Server returns success with additional data ........... 5
5.3. Multiple authentications .............................. 5
6. Registration procedures ............................... 6
6.1. Comments on SASL mechanism registrations .............. 6
6.2. Location of Registered SASL Mechanism List ............ 6
6.3. Change Control ........................................ 7
6.4. Registration Template ................................. 7
7. Mechanism definitions ................................. 8
7.1. Kerberos version 4 mechanism .......................... 8
7.2. GSSAPI mechanism ...................................... 9
7.2.1 Client side of authentication protocol exchange ....... 9
7.2.2 Server side of authentication protocol exchange ....... 10
7.2.3 Security layer ........................................ 11
7.3. S/Key mechanism ....................................... 11
7.4. External mechanism .................................... 12
8. References ............................................ 13
9. Security Considerations ............................... 13
10. Author's Address ...................................... 14
Myers Standards Track [Page 1]
RFC 2222 SASL October 1997
Appendix A. Relation of SASL to Transport Security .......... 15
Full Copyright Statement .................................... 16
1. Abstract
This document describes a method for adding authentication support to
connection-based protocols. To use this specification, a protocol
includes a command for identifying and authenticating a user to a
server and for optionally negotiating protection of subsequent
protocol interactions. If its use is negotiated, a security layer is
inserted between the protocol and the connection. This document
describes how a protocol specifies such a command, defines several
mechanisms for use by the command, and defines the protocol used for
carrying a negotiated security layer over the connection.
2. Organization of this Document
2.1. How to Read This Document
This document is written to serve two different audiences, protocol
designers using this specification to support authentication in their
protocol, and implementors of clients or servers for those protocols
using this specification.
The sections "Introduction and Overview", "Profiling requirements",
and "Security Considerations" cover issues that protocol designers
need to understand and address in profiling this specification for
use in a specific protocol.
Implementors of a protocol using this specification need the
protocol-specific profiling information in addition to the
information in this document.
2.2. Conventions Used in this Document
In examples, "C:" and "S:" indicate lines sent by the client and
server respectively.
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
in this document are to be interpreted as defined in "Key words for
use in RFCs to Indicate Requirement Levels" [RFC 2119].
Myers Standards Track [Page 2]
RFC 2222 SASL October 1997
2.3. Examples
Examples in this document are for the IMAP profile [RFC 2060] of this
specification. The base64 encoding of challenges and responses, as
well as the "+ " preceding the responses are part of the IMAP4
profile, not part of the SASL specification itself.
3. Introduction and Overview
The Simple Authentication and Security Layer (SASL) is a method for
adding authentication support to connection-based protocols. To use
this specification, a protocol includes a command for identifying and
authenticating a user to a server and for optionally negotiating a
security layer for subsequent protocol interactions.
The command has a required argument identifying a SASL mechanism.
SASL mechanisms are named by strings, from 1 to 20 characters in
length, consisting of upper-case letters, digits, hyphens, and/or
underscores. SASL mechanism names must be registered with the IANA.
Procedures for registering new SASL mechanisms are given in the
section "Registration procedures"
If a server supports the requested mechanism, it initiates an
authentication protocol exchange. This consists of a series of
server challenges and client responses that are specific to the
requested mechanism. The challenges and responses are defined by the
mechanisms as binary tokens of arbitrary length. The protocol's
profile then specifies how these binary tokens are then encoded for
transfer over the connection.
After receiving the authentication command or any client response, a
server may issue a challenge, indicate failure, or indicate
completion. The protocol's profile specifies how the server
indicates which of the above it is doing.
After receiving a challenge, a client may issue a response or abort
the exchange. The protocol's profile specifies how the client
indicates which of the above it is doing.
During the authentication protocol exchange, the mechanism performs
authentication, transmits an authorization identity (frequently known
as a userid) from the client to server, and negotiates the use of a
mechanism-specific security layer. If the use of a security layer is
agreed upon, then the mechanism must also define or negotiate the
maximum cipher-text buffer size that each side is able to receive.
Myers Standards Track [Page 3]
RFC 2222 SASL October 1997
The transmitted authorization identity may be different than the
identity in the client's authentication credentials. This permits
agents such as proxy servers to authenticate using their own
credentials, yet request the access privileges of the identity for
which they are proxying. With any mechanism, transmitting an
authorization identity of the empty string directs the server to
derive an authorization identity from the client's authentication
credentials.
If use of a security layer is negotiated, it is applied to all
subsequent data sent over the connection. The security layer takes
effect immediately following the last response of the authentication
exchange for data sent by the client and the completion indication
for data sent by the server. Once the security layer is in effect,
the protocol stream is processed by the security layer into buffers
of cipher-text. Each buffer is transferred over the connection as a
stream of octets prepended with a four octet field in network byte
order that represents the length of the following buffer. The length
of the cipher-text buffer must be no larger than the maximum size
that was defined or negotiated by the other side.
4. Profiling requirements
In order to use this specification, a protocol definition must supply
the following information:
1. A service name, to be selected from the IANA registry of "service"
elements for the GSSAPI host-based service name form [RFC 2078].
2. A definition of the command to initiate the authentication
protocol exchange. This command must have as a parameter the
mechanism name being selected by the client.
The command SHOULD have an optional parameter giving an initial
response. This optional parameter allows the client to avoid a
round trip when using a mechanism which is defined to have the
client send data first. When this initial response is sent by the
client and the selected mechanism is defined to have the server
start with an initial challenge, the command fails. See section
5.1 of this document for further information.
3. A definition of the method by which the authentication protocol
exchange is carried out, including how the challenges and
responses are encoded, how the server indicates completion or
failure of the exchange, how the client aborts an exchange, and
how the exchange method interacts with any line length limits in
the protocol.
Myers Standards Track [Page 4]
RFC 2222 SASL October 1997
4. Identification of the octet where any negotiated security layer
starts to take effect, in both directions.
5. A specification of how the authorization identity passed from the
client to the server is to be interpreted.
5. Specific issues
5.1. Client sends data first
Some mechanisms specify that the first data sent in the
authentication protocol exchange is from the client to the server.
If a protocol's profile permits the command which initiates an
authentication protocol exchange to contain an initial client
response, this parameter SHOULD be used with such mechanisms.
If the initial client response parameter is not given, or if a
protocol's profile does not permit the command which initiates an
authentication protocol exchange to contain an initial client
response, then the server issues a challenge with no data. The
client's response to this challenge is then used as the initial
client response. (The server then proceeds to send the next
challenge, indicates completion, or indicates failure.)
5.2. Server returns success with additional data
Some mechanisms may specify that server challenge data be sent to the
client along with an indication of successful completion of the
exchange. This data would, for example, authenticate the server to
the client.
If a protocol's profile does not permit this server challenge to be
returned with a success indication, then the server issues the server
challenge without an indication of successful completion. The client
then responds with no data. After receiving this empty response, the
server then indicates successful completion.
5.3. Multiple authentications
Unless otherwise stated by the protocol's profile, only one
successful SASL negotiation may occur in a protocol session. In this
case, once an authentication protocol exchange has successfully
completed, further attempts to initiate an authentication protocol
exchange fail.
Myers Standards Track [Page 5]
RFC 2222 SASL October 1997
In the case that a profile explicitly permits multiple successful
SASL negotiations to occur, then in no case may multiple security
layers be simultaneously in effect. If a security layer is in effect
and a subsequent SASL negotiation selects no security layer, the
original security layer remains in effect. If a security layer is in
effect and a subsequent SASL negotiation selects a second security
layer, then the second security layer replaces the first.
6. Registration procedures
Registration of a SASL mechanism is done by filling in the template
in section 6.4 and sending it in to iana@isi.edu. IANA has the right
to reject obviously bogus registrations, but will perform no review
of clams made in the registration form.
There is no naming convention for SASL mechanisms; any name that
conforms to the syntax of a SASL mechanism name can be registered.
While the registration procedures do not require it, authors of SASL
mechanisms are encouraged to seek community review and comment
whenever that is feasible. Authors may seek community review by
posting a specification of their proposed mechanism as an internet-
draft. SASL mechanisms intended for widespread use should be
standardized through the normal IETF process, when appropriate.
6.1. Comments on SASL mechanism registrations
Comments on registered SASL mechanisms should first be sent to the
"owner" of the mechanism. Submitters of comments may, after a
reasonable attempt to contact the owner, request IANA to attach their
comment to the SASL mechanism registration itself. If IANA approves
of this the comment will be made accessible in conjunction with the
SASL mechanism registration itself.
6.2. Location of Registered SASL Mechanism List
SASL mechanism registrations will be posted in the anonymous FTP
directory "ftp://ftp.isi.edu/in-notes/iana/assignments/sasl-
mechanisms/" and all registered SASL mechanisms will be listed in the
periodically issued "Assigned Numbers" RFC [currently STD 2, RFC
1700]. The SASL mechanism description and other supporting material
may also be published as an Informational RFC by sending it to "rfc-
editor@isi.edu" (please follow the instructions to RFC authors [RFC
2223]).
Myers Standards Track [Page 6]
RFC 2222 SASL October 1997
6.3. Change Control
Once a SASL mechanism registration has been published by IANA, the
author may request a change to its definition. The change request
follows the same procedure as the registration request.
The owner of a SASL mechanism may pass responsibility for the SASL
mechanism to another person or agency by informing IANA; this can be
done without discussion or review.
The IESG may reassign responsibility for a SASL mechanism. The most
common case of this will be to enable changes to be made to
mechanisms where the author of the registration has died, moved out
of contact or is otherwise unable to make changes that are important
to the community.
SASL mechanism registrations may not be deleted; mechanisms which are
no longer believed appropriate for use can be declared OBSOLETE by a
change to their "intended use" field; such SASL mechanisms will be
clearly marked in the lists published by IANA.
The IESG is considered to be the owner of all SASL mechanisms which
are on the IETF standards track.
6.4. Registration Template
To: iana@iana.org
Subject: Registration of SASL mechanism X
SASL mechanism name:
Security considerations:
Published specification (optional, recommended):
Person & email address to contact for further information:
Intended usage:
(One of COMMON, LIMITED USE or OBSOLETE)
Author/Change controller:
(Any other information that the author deems interesting may be
added below this line.)
Myers Standards Track [Page 7]
RFC 2222 SASL October 1997
7. Mechanism definitions
The following mechanisms are hereby defined.
7.1. Kerberos version 4 mechanism
The mechanism name associated with Kerberos version 4 is
"KERBEROS_V4".
The first challenge consists of a random 32-bit number in network
byte order. The client responds with a Kerberos ticket and an
authenticator for the principal "service.hostname@realm", where
"service" is the service name specified in the protocol's profile,
"hostname" is the first component of the host name of the server with
all letters in lower case, and where "realm" is the Kerberos realm of
the server. The encrypted checksum field included within the
Kerberos authenticator contains the server provided challenge in
network byte order.
Upon decrypting and verifying the ticket and authenticator, the
server verifies that the contained checksum field equals the original
server provided random 32-bit number. Should the verification be
successful, the server must add one to the checksum and construct 8
octets of data, with the first four octets containing the incremented
checksum in network byte order, the fifth octet containing a bit-mask
specifying the security layers supported by the server, and the sixth
through eighth octets containing, in network byte order, the maximum
cipher-text buffer size the server is able to receive. The server
must encrypt using DES ECB mode the 8 octets of data in the session
key and issue that encrypted data in a second challenge. The client
considers the server authenticated if the first four octets of the
un-encrypted data is equal to one plus the checksum it previously
sent.
The client must construct data with the first four octets containing
the original server-issued checksum in network byte order, the fifth
octet containing the bit-mask specifying the selected security layer,
the sixth through eighth octets containing in network byte order the
maximum cipher-text buffer size the client is able to receive, and
the following octets containing the authorization identity. The
client must then append from one to eight zero-valued octets so that
the length of the data is a multiple of eight octets. The client must
then encrypt using DES PCBC mode the data with the session key and
respond with the encrypted data. The server decrypts the data and
verifies the contained checksum. The server must verify that the
principal identified in the Kerberos ticket is authorized to connect
as that authorization identity. After this verification, the
authentication process is complete.
Myers Standards Track [Page 8]
RFC 2222 SASL October 1997
The security layers and their corresponding bit-masks are as follows:
1 No security layer
2 Integrity (krb_mk_safe) protection
4 Privacy (krb_mk_priv) protection
Other bit-masks may be defined in the future; bits which are not
understood must be negotiated off.
EXAMPLE: The following are two Kerberos version 4 login scenarios to
the IMAP4 protocol (note that the line breaks in the sample
authenticators are for editorial clarity and are not in real
authenticators)
S: * OK IMAP4 Server
C: A001 AUTHENTICATE KERBEROS_V4
S: + AmFYig==
C: BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT
+nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oebe/ydHJUwYFd
WwuQ1MWiy6IesKvjL5rL9WjXUb9MwT9bpObYLGOKi1Qh
S: + or//EoAADZI=
C: DiAF5A4gA+oOIALuBkAAmw==
S: A001 OK Kerberos V4 authentication successful
S: * OK IMAP4 Server
C: A001 AUTHENTICATE KERBEROS_V4
S: + gcfgCA==
C: BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT
+nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oebe/ydHJUwYFd
WwuQ1MWiy6IesKvjL5rL9WjXUb9MwT9bpObYLGOKi1Qh
S: A001 NO Kerberos V4 authentication failed
7.2. GSSAPI mechanism
The mechanism name associated with all mechanisms employing the
GSSAPI [RFC 2078] is "GSSAPI".
7.2.1 Client side of authentication protocol exchange
The client calls GSS_Init_sec_context, passing in 0 for
input_context_handle (initially) and a targ_name equal to output_name
from GSS_Import_Name called with input_name_type of
GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
"service@hostname" where "service" is the service name specified in
the protocol's profile, and "hostname" is the fully qualified host
name of the server. The client then responds with the resulting
output_token. If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED,
Myers Standards Track [Page 9]
RFC 2222 SASL October 1997
then the client should expect the server to issue a token in a
subsequent challenge. The client must pass the token to another call
to GSS_Init_sec_context, repeating the actions in this paragraph.
When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes
the following actions: If the last call to GSS_Init_sec_context
returned an output_token, then the client responds with the
output_token, otherwise the client responds with no data. The client
should then expect the server to issue a token in a subsequent
challenge. The client passes this token to GSS_Unwrap and interprets
the first octet of resulting cleartext as a bit-mask specifying the
security layers supported by the server and the second through fourth
octets as the maximum size output_message to send to the server. The
client then constructs data, with the first octet containing the
bit-mask specifying the selected security layer, the second through
fourth octets containing in network byte order the maximum size
output_message the client is able to receive, and the remaining
octets containing the authorization identity. The client passes the
data to GSS_Wrap with conf_flag set to FALSE, and responds with the
generated output_message. The client can then consider the server
authenticated.
7.2.2 Server side of authentication protocol exchange
The server passes the initial client response to
GSS_Accept_sec_context as input_token, setting input_context_handle
to 0 (initially). If GSS_Accept_sec_context returns
GSS_S_CONTINUE_NEEDED, the server returns the generated output_token
to the client in challenge and passes the resulting response to
another call to GSS_Accept_sec_context, repeating the actions in this
paragraph.
When GSS_Accept_sec_context returns GSS_S_COMPLETE, the client takes
the following actions: If the last call to GSS_Accept_sec_context
returned an output_token, the server returns it to the client in a
challenge and expects a reply from the client with no data. Whether
or not an output_token was returned (and after receipt of any
response from the client to such an output_token), the server then
constructs 4 octets of data, with the first octet containing a bit-
mask specifying the security layers supported by the server and the
second through fourth octets containing in network byte order the
maximum size output_token the server is able to receive. The server
must then pass the plaintext to GSS_Wrap with conf_flag set to FALSE
and issue the generated output_message to the client in a challenge.
The server must then pass the resulting response to GSS_Unwrap and
interpret the first octet of resulting cleartext as the bit-mask for
the selected security layer, the second through fourth octets as the
maximum size output_message to send to the client, and the remaining
Myers Standards Track [Page 10]
RFC 2222 SASL October 1997
octets as the authorization identity. The server must verify that
the src_name is authorized to authenticate as the authorization
identity. After these verifications, the authentication process is
complete.
7.2.3 Security layer
The security layers and their corresponding bit-masks are as follows:
1 No security layer
2 Integrity protection.
Sender calls GSS_Wrap with conf_flag set to FALSE
4 Privacy protection.
Sender calls GSS_Wrap with conf_flag set to TRUE
Other bit-masks may be defined in the future; bits which are not
understood must be negotiated off.
7.3. S/Key mechanism
The mechanism name associated with S/Key [RFC 1760] using the MD4
digest algorithm is "SKEY".
The client sends an initial response with the authorization identity.
The server then issues a challenge which contains the decimal
sequence number followed by a single space and the seed string for
the indicated authorization identity. The client responds with the
one-time-password, as either a 64-bit value in network byte order or
encoded in the "six English words" format.
The server must verify the one-time-password. After this
verification, the authentication process is complete.
S/Key authentication does not provide for any security layers.
EXAMPLE: The following are two S/Key login scenarios in the IMAP4
protocol.
S: * OK IMAP4 Server
C: A001 AUTHENTICATE SKEY
S: +
C: bW9yZ2Fu
S: + OTUgUWE1ODMwOA==
C: Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
S: A001 OK S/Key authentication successful
Myers Standards Track [Page 11]
RFC 2222 SASL October 1997
S: * OK IMAP4 Server
C: A001 AUTHENTICATE SKEY
S: +
C: c21pdGg=
S: + OTUgUWE1ODMwOA==
C: BsAY3g4gBNo=
S: A001 NO S/Key authentication failed
The following is an S/Key login scenario in an IMAP4-like protocol
which has an optional "initial response" argument to the AUTHENTICATE
command.
S: * OK IMAP4-Like Server
C: A001 AUTHENTICATE SKEY bW9yZ2Fu
S: + OTUgUWE1ODMwOA==
C: Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
S: A001 OK S/Key authentication successful
7.4. External mechanism
The mechanism name associated with external authentication is
"EXTERNAL".
The client sends an initial response with the authorization identity.
The server uses information, external to SASL, to determine whether
the client is authorized to authenticate as the authorization
identity. If the client is so authorized, the server indicates
successful completion of the authentication exchange; otherwise the
server indicates failure.
The system providing this external information may be, for example,
IPsec or TLS.
If the client sends the empty string as the authorization identity
(thus requesting the authorization identity be derived from the
client's authentication credentials), the authorization identity is
to be derived from authentication credentials which exist in the
system which is providing the external authentication.
Myers Standards Track [Page 12]
RFC 2222 SASL October 1997
8. References
[RFC 2060] Crispin, M., "Internet Message Access Protocol - Version
4rev1", RFC 2060, December 1996.
[RFC 2078] Linn, J., "Generic Security Service Application Program
Interface, Version 2", RFC 2078, January 1997.
[RFC 2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", RFC 2119, March 1997.
[RFC 2223] Postel, J., and J. Reynolds, "Instructions to RFC
Authors", RFC 2223, October 1997.
[RFC 1760] Haller, N., "The S/Key One-Time Password System", RFC
1760, February 1995.
[RFC 1700] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2,
RFC 1700, October 1994.
9. Security Considerations
Security issues are discussed throughout this memo.
The mechanisms that support integrity protection are designed such
that the negotiation of the security layer and authorization identity
is integrity protected. When the client selects a security layer
with at least integrity protection, this protects against an active
attacker hijacking the connection and modifying the authentication
exchange to negotiate a plaintext connection.
When a server or client supports multiple authentication mechanisms,
each of which has a different security strength, it is possible for
an active attacker to cause a party to use the least secure mechanism
supported. To protect against this sort of attack, a client or
server which supports mechanisms of different strengths should have a
configurable minimum strength that it will use. It is not sufficient
for this minimum strength check to only be on the server, since an
active attacker can change which mechanisms the client sees as being
supported, causing the client to send authentication credentials for
its weakest supported mechanism.
Myers Standards Track [Page 13]
RFC 2222 SASL October 1997
The client's selection of a SASL mechanism is done in the clear and
may be modified by an active attacker. It is important for any new
SASL mechanisms to be designed such that an active attacker cannot
obtain an authentication with weaker security properties by modifying
the SASL mechanism name and/or the challenges and responses.
Any protocol interactions prior to authentication are performed in
the clear and may be modified by an active attacker. In the case
where a client selects integrity protection, it is important that any
security-sensitive protocol negotiations be performed after
authentication is complete. Protocols should be designed such that
negotiations performed prior to authentication should be either
ignored or revalidated once authentication is complete.
10. Author's Address
John G. Myers
Netscape Communications
501 E. Middlefield Road
Mail Stop MV-029
Mountain View, CA 94043-4042
EMail: jgmyers@netscape.com
Myers Standards Track [Page 14]
RFC 2222 SASL October 1997
Appendix A. Relation of SASL to Transport Security
Questions have been raised about the relationship between SASL and
various services (such as IPsec and TLS) which provide a secured
connection.
Two of the key features of SASL are:
1. The separation of the authorization identity from the identity in
the client's credentials. This permits agents such as proxy
servers to authenticate using their own credentials, yet request
the access privileges of the identity for which they are proxying.
2. Upon successful completion of an authentication exchange, the
server knows the authorization identity the client wishes to use.
This allows servers to move to a "user is authenticated" state in
the protocol.
These features are extremely important to some application protocols,
yet Transport Security services do not always provide them. To
define SASL mechanisms based on these services would be a very messy
task, as the framing of these services would be redundant with the
framing of SASL and some method of providing these important SASL
features would have to be devised.
Sometimes it is desired to enable within an existing connection the
use of a security service which does not fit the SASL model. (TLS is
an example of such a service.) This can be done by adding a command,
for example "STARTTLS", to the protocol. Such a command is outside
the scope of SASL, and should be different from the command which
starts a SASL authentication protocol exchange.
In certain situations, it is reasonable to use SASL underneath one of
these Transport Security services. The transport service would
secure the connection, either service would authenticate the client,
and SASL would negotiate the authorization identity. The SASL
negotiation would be what moves the protocol from "unauthenticated"
to "authenticated" state. The "EXTERNAL" SASL mechanism is
explicitly intended to handle the case where the transport service
secures the connection and authenticates the client and SASL
negotiates the authorization identity.
When using SASL underneath a sufficiently strong Transport Security
service, a SASL security layer would most likely be redundant. The
client and server would thus probably want to negotiate off the use
of a SASL security layer.
Myers Standards Track [Page 15]
RFC 2222 SASL October 1997
Full Copyright Statement
Copyright (C) The Internet Society (1997). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implmentation may be prepared, copied, published
andand distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Myers Standards Track [Page 16]

View File

@ -0,0 +1,563 @@
Network Working Group N. Freed
Request for Comments: 2231 Innosoft
Updates: 2045, 2047, 2183 K. Moore
Obsoletes: 2184 University of Tennessee
Category: Standards Track November 1997
MIME Parameter Value and Encoded Word Extensions:
Character Sets, Languages, and Continuations
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (1997). All Rights Reserved.
1. Abstract
This memo defines extensions to the RFC 2045 media type and RFC 2183
disposition parameter value mechanisms to provide
(1) a means to specify parameter values in character sets
other than US-ASCII,
(2) to specify the language to be used should the value be
displayed, and
(3) a continuation mechanism for long parameter values to
avoid problems with header line wrapping.
This memo also defines an extension to the encoded words defined in
RFC 2047 to allow the specification of the language to be used for
display as well as the character set.
2. Introduction
The Multipurpose Internet Mail Extensions, or MIME [RFC-2045, RFC-
2046, RFC-2047, RFC-2048, RFC-2049], define a message format that
allows for:
Freed & Moore Standards Track [Page 1]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
(1) textual message bodies in character sets other than
US-ASCII,
(2) non-textual message bodies,
(3) multi-part message bodies, and
(4) textual header information in character sets other than
US-ASCII.
MIME is now widely deployed and is used by a variety of Internet
protocols, including, of course, Internet email. However, MIME's
success has resulted in the need for additional mechanisms that were
not provided in the original protocol specification.
In particular, existing MIME mechanisms provide for named media type
(content-type field) parameters as well as named disposition
(content-disposition field). A MIME media type may specify any
number of parameters associated with all of its subtypes, and any
specific subtype may specify additional parameters for its own use. A
MIME disposition value may specify any number of associated
parameters, the most important of which is probably the attachment
disposition's filename parameter.
These parameter names and values end up appearing in the content-type
and content-disposition header fields in Internet email. This
inherently imposes three crucial limitations:
(1) Lines in Internet email header fields are folded
according to RFC 822 folding rules. This makes long
parameter values problematic.
(2) MIME headers, like the RFC 822 headers they often
appear in, are limited to 7bit US-ASCII, and the
encoded-word mechanisms of RFC 2047 are not available
to parameter values. This makes it impossible to have
parameter values in character sets other than US-ASCII
without specifying some sort of private per-parameter
encoding.
(3) It has recently become clear that character set
information is not sufficient to properly display some
sorts of information -- language information is also
needed [RFC-2130]. For example, support for
handicapped users may require reading text string
Freed & Moore Standards Track [Page 2]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
aloud. The language the text is written in is needed
for this to be done correctly. Some parameter values
may need to be displayed, hence there is a need to
allow for the inclusion of language information.
The last problem on this list is also an issue for the encoded words
defined by RFC 2047, as encoded words are intended primarily for
display purposes.
This document defines extensions that address all of these
limitations. All of these extensions are implemented in a fashion
that is completely compatible at a syntactic level with existing MIME
implementations. In addition, the extensions are designed to have as
little impact as possible on existing uses of MIME.
IMPORTANT NOTE: These mechanisms end up being somewhat gibbous when
they actually are used. As such, these mechanisms should not be used
lightly; they should be reserved for situations where a real need for
them exists.
2.1. Requirements notation
This document occasionally uses terms that appear in capital letters.
When the terms "MUST", "SHOULD", "MUST NOT", "SHOULD NOT", and "MAY"
appear capitalized, they are being used to indicate particular
requirements of this specification. A discussion of the meanings of
these terms appears in [RFC- 2119].
3. Parameter Value Continuations
Long MIME media type or disposition parameter values do not interact
well with header line wrapping conventions. In particular, proper
header line wrapping depends on there being places where linear
whitespace (LWSP) is allowed, which may or may not be present in a
parameter value, and even if present may not be recognizable as such
since specific knowledge of parameter value syntax may not be
available to the agent doing the line wrapping. The result is that
long parameter values may end up getting truncated or otherwise
damaged by incorrect line wrapping implementations.
A mechanism is therefore needed to break up parameter values into
smaller units that are amenable to line wrapping. Any such mechanism
MUST be compatible with existing MIME processors. This means that
(1) the mechanism MUST NOT change the syntax of MIME media
type and disposition lines, and
Freed & Moore Standards Track [Page 3]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
(2) the mechanism MUST NOT depend on parameter ordering
since MIME states that parameters are not order
sensitive. Note that while MIME does prohibit
modification of MIME headers during transport, it is
still possible that parameters will be reordered when
user agent level processing is done.
The obvious solution, then, is to use multiple parameters to contain
a single parameter value and to use some kind of distinguished name
to indicate when this is being done. And this obvious solution is
exactly what is specified here: The asterisk character ("*") followed
by a decimal count is employed to indicate that multiple parameters
are being used to encapsulate a single parameter value. The count
starts at 0 and increments by 1 for each subsequent section of the
parameter value. Decimal values are used and neither leading zeroes
nor gaps in the sequence are allowed.
The original parameter value is recovered by concatenating the
various sections of the parameter, in order. For example, the
content-type field
Content-Type: message/external-body; access-type=URL;
URL*0="ftp://";
URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
is semantically identical to
Content-Type: message/external-body; access-type=URL;
URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
Note that quotes around parameter values are part of the value
syntax; they are NOT part of the value itself. Furthermore, it is
explicitly permitted to have a mixture of quoted and unquoted
continuation fields.
4. Parameter Value Character Set and Language Information
Some parameter values may need to be qualified with character set or
language information. It is clear that a distinguished parameter
name is needed to identify when this information is present along
with a specific syntax for the information in the value itself. In
addition, a lightweight encoding mechanism is needed to accommodate 8
bit information in parameter values.
Freed & Moore Standards Track [Page 4]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
Asterisks ("*") are reused to provide the indicator that language and
character set information is present and encoding is being used. A
single quote ("'") is used to delimit the character set and language
information at the beginning of the parameter value. Percent signs
("%") are used as the encoding flag, which agrees with RFC 2047.
Specifically, an asterisk at the end of a parameter name acts as an
indicator that character set and language information may appear at
the beginning of the parameter value. A single quote is used to
separate the character set, language, and actual value information in
the parameter value string, and an percent sign is used to flag
octets encoded in hexadecimal. For example:
Content-Type: application/x-stuff;
title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A
Note that it is perfectly permissible to leave either the character
set or language field blank. Note also that the single quote
delimiters MUST be present even when one of the field values is
omitted. This is done when either character set, language, or both
are not relevant to the parameter value at hand. This MUST NOT be
done in order to indicate a default character set or language --
parameter field definitions MUST NOT assign a default character set
or language.
4.1. Combining Character Set, Language, and Parameter Continuations
Character set and language information may be combined with the
parameter continuation mechanism. For example:
Content-Type: application/x-stuff
title*0*=us-ascii'en'This%20is%20even%20more%20
title*1*=%2A%2A%2Afun%2A%2A%2A%20
title*2="isn't it!"
Note that:
(1) Language and character set information only appear at
the beginning of a given parameter value.
(2) Continuations do not provide a facility for using more
than one character set or language in the same
parameter value.
(3) A value presented using multiple continuations may
contain a mixture of encoded and unencoded segments.
Freed & Moore Standards Track [Page 5]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
(4) The first segment of a continuation MUST be encoded if
language and character set information are given.
(5) If the first segment of a continued parameter value is
encoded the language and character set field delimiters
MUST be present even when the fields are left blank.
5. Language specification in Encoded Words
RFC 2047 provides support for non-US-ASCII character sets in RFC 822
message header comments, phrases, and any unstructured text field.
This is done by defining an encoded word construct which can appear
in any of these places. Given that these are fields intended for
display, it is sometimes necessary to associate language information
with encoded words as well as just the character set. This
specification extends the definition of an encoded word to allow the
inclusion of such information. This is simply done by suffixing the
character set specification with an asterisk followed by the language
tag. For example:
From: =?US-ASCII*EN?Q?Keith_Moore?= <moore@cs.utk.edu>
6. IMAP4 Handling of Parameter Values
IMAP4 [RFC-2060] servers SHOULD decode parameter value continuations
when generating the BODY and BODYSTRUCTURE fetch attributes.
7. Modifications to MIME ABNF
The ABNF for MIME parameter values given in RFC 2045 is:
parameter := attribute "=" value
attribute := token
; Matching of attributes
; is ALWAYS case-insensitive.
This specification changes this ABNF to:
parameter := regular-parameter / extended-parameter
regular-parameter := regular-parameter-name "=" value
regular-parameter-name := attribute [section]
attribute := 1*attribute-char
Freed & Moore Standards Track [Page 6]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
attribute-char := <any (US-ASCII) CHAR except SPACE, CTLs,
"*", "'", "%", or tspecials>
section := initial-section / other-sections
initial-section := "*0"
other-sections := "*" ("1" / "2" / "3" / "4" / "5" /
"6" / "7" / "8" / "9") *DIGIT)
extended-parameter := (extended-initial-name "="
extended-value) /
(extended-other-names "="
extended-other-values)
extended-initial-name := attribute [initial-section] "*"
extended-other-names := attribute other-sections "*"
extended-initial-value := [charset] "'" [language] "'"
extended-other-values
extended-other-values := *(ext-octet / attribute-char)
ext-octet := "%" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F")
charset := <registered character set name>
language := <registered language tag [RFC-1766]>
The ABNF given in RFC 2047 for encoded-words is:
encoded-word := "=?" charset "?" encoding "?" encoded-text "?="
This specification changes this ABNF to:
encoded-word := "=?" charset ["*" language] "?" encoded-text "?="
8. Character sets which allow specification of language
In the future it is likely that some character sets will provide
facilities for inline language labeling. Such facilities are
inherently more flexible than those defined here as they allow for
language switching in the middle of a string.
Freed & Moore Standards Track [Page 7]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
If and when such facilities are developed they SHOULD be used in
preference to the language labeling facilities specified here. Note
that all the mechanisms defined here allow for the omission of
language labels so as to be able to accommodate this possible future
usage.
9. Security Considerations
This RFC does not discuss security issues and is not believed to
raise any security issues not already endemic in electronic mail and
present in fully conforming implementations of MIME.
10. References
[RFC-822]
Crocker, D., "Standard for the Format of ARPA Internet
Text Messages", STD 11, RFC 822 August 1982.
[RFC-1766]
Alvestrand, H., "Tags for the Identification of
Languages", RFC 1766, March 1995.
[RFC-2045]
Freed, N., and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message
Bodies", RFC 2045, December 1996.
[RFC-2046]
Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part Two: Media Types", RFC 2046,
December 1996.
[RFC-2047]
Moore, K., "Multipurpose Internet Mail Extensions (MIME)
Part Three: Representation of Non-ASCII Text in Internet
Message Headers", RFC 2047, December 1996.
[RFC-2048]
Freed, N., Klensin, J. and J. Postel, "Multipurpose
Internet Mail Extensions (MIME) Part Four: MIME
Registration Procedures", RFC 2048, December 1996.
[RFC-2049]
Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part Five: Conformance Criteria and
Examples", RFC 2049, December 1996.
Freed & Moore Standards Track [Page 8]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
[RFC-2060]
Crispin, M., "Internet Message Access Protocol - Version
4rev1", RFC 2060, December 1996.
[RFC-2119]
Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", RFC 2119, March 1997.
[RFC-2130]
Weider, C., Preston, C., Simonsen, K., Alvestrand, H.,
Atkinson, R., Crispin, M., and P. Svanberg, "Report from the
IAB Character Set Workshop", RFC 2130, April 1997.
[RFC-2183]
Troost, R., Dorner, S. and K. Moore, "Communicating
Presentation Information in Internet Messages: The
Content-Disposition Header", RFC 2183, August 1997.
11. Authors' Addresses
Ned Freed
Innosoft International, Inc.
1050 Lakes Drive
West Covina, CA 91790
USA
Phone: +1 626 919 3600
Fax: +1 626 919 3614
EMail: ned.freed@innosoft.com
Keith Moore
Computer Science Dept.
University of Tennessee
107 Ayres Hall
Knoxville, TN 37996-1301
USA
EMail: moore@cs.utk.edu
Freed & Moore Standards Track [Page 9]
RFC 2231 MIME Value and Encoded Word Extensions November 1997
12. Full Copyright Statement
Copyright (C) The Internet Society (1997). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Freed & Moore Standards Track [Page 10]

View File

@ -0,0 +1,787 @@
Network Working Group D. Crocker, Ed.
Request for Comments: 2234 Internet Mail Consortium
Category: Standards Track P. Overell
Demon Internet Ltd.
November 1997
Augmented BNF for Syntax Specifications: ABNF
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (1997). All Rights Reserved.
TABLE OF CONTENTS
1. INTRODUCTION .................................................. 2
2. RULE DEFINITION ............................................... 2
2.1 RULE NAMING .................................................. 2
2.2 RULE FORM .................................................... 3
2.3 TERMINAL VALUES .............................................. 3
2.4 EXTERNAL ENCODINGS ........................................... 5
3. OPERATORS ..................................................... 5
3.1 CONCATENATION RULE1 RULE2 ............................. 5
3.2 ALTERNATIVES RULE1 / RULE2 ................................... 6
3.3 INCREMENTAL ALTERNATIVES RULE1 =/ RULE2 .................... 6
3.4 VALUE RANGE ALTERNATIVES %C##-## ........................... 7
3.5 SEQUENCE GROUP (RULE1 RULE2) ................................. 7
3.6 VARIABLE REPETITION *RULE .................................... 8
3.7 SPECIFIC REPETITION NRULE .................................... 8
3.8 OPTIONAL SEQUENCE [RULE] ..................................... 8
3.9 ; COMMENT .................................................... 8
3.10 OPERATOR PRECEDENCE ......................................... 9
4. ABNF DEFINITION OF ABNF ....................................... 9
5. SECURITY CONSIDERATIONS ....................................... 10
Crocker & Overell Standards Track [Page 1]
RFC 2234 ABNF for Syntax Specifications November 1997
6. APPENDIX A - CORE ............................................. 11
6.1 CORE RULES ................................................... 11
6.2 COMMON ENCODING .............................................. 12
7. ACKNOWLEDGMENTS ............................................... 12
8. REFERENCES .................................................... 13
9. CONTACT ....................................................... 13
10. FULL COPYRIGHT STATEMENT ..................................... 14
1. INTRODUCTION
Internet technical specifications often need to define a format
syntax and are free to employ whatever notation their authors deem
useful. Over the years, a modified version of Backus-Naur Form
(BNF), called Augmented BNF (ABNF), has been popular among many
Internet specifications. It balances compactness and simplicity,
with reasonable representational power. In the early days of the
Arpanet, each specification contained its own definition of ABNF.
This included the email specifications, RFC733 and then RFC822 which
have come to be the common citations for defining ABNF. The current
document separates out that definition, to permit selective
reference. Predictably, it also provides some modifications and
enhancements.
The differences between standard BNF and ABNF involve naming rules,
repetition, alternatives, order-independence, and value ranges.
Appendix A (Core) supplies rule definitions and encoding for a core
lexical analyzer of the type common to several Internet
specifications. It is provided as a convenience and is otherwise
separate from the meta language defined in the body of this document,
and separate from its formal status.
2. RULE DEFINITION
2.1 Rule Naming
The name of a rule is simply the name itself; that is, a sequence of
characters, beginning with an alphabetic character, and followed by
a combination of alphabetics, digits and hyphens (dashes).
NOTE: Rule names are case-insensitive
The names <rulename>, <Rulename>, <RULENAME> and <rUlENamE> all refer
to the same rule.
Crocker & Overell Standards Track [Page 2]
RFC 2234 ABNF for Syntax Specifications November 1997
Unlike original BNF, angle brackets ("<", ">") are not required.
However, angle brackets may be used around a rule name whenever their
presence will facilitate discerning the use of a rule name. This is
typically restricted to rule name references in free-form prose, or
to distinguish partial rules that combine into a string not separated
by white space, such as shown in the discussion about repetition,
below.
2.2 Rule Form
A rule is defined by the following sequence:
name = elements crlf
where <name> is the name of the rule, <elements> is one or more rule
names or terminal specifications and <crlf> is the end-of- line
indicator, carriage return followed by line feed. The equal sign
separates the name from the definition of the rule. The elements
form a sequence of one or more rule names and/or value definitions,
combined according to the various operators, defined in this
document, such as alternative and repetition.
For visual ease, rule definitions are left aligned. When a rule
requires multiple lines, the continuation lines are indented. The
left alignment and indentation are relative to the first lines of the
ABNF rules and need not match the left margin of the document.
2.3 Terminal Values
Rules resolve into a string of terminal values, sometimes called
characters. In ABNF a character is merely a non-negative integer.
In certain contexts a specific mapping (encoding) of values into a
character set (such as ASCII) will be specified.
Terminals are specified by one or more numeric characters with the
base interpretation of those characters indicated explicitly. The
following bases are currently defined:
b = binary
d = decimal
x = hexadecimal
Crocker & Overell Standards Track [Page 3]
RFC 2234 ABNF for Syntax Specifications November 1997
Hence:
CR = %d13
CR = %x0D
respectively specify the decimal and hexadecimal representation of
[US-ASCII] for carriage return.
A concatenated string of such values is specified compactly, using a
period (".") to indicate separation of characters within that value.
Hence:
CRLF = %d13.10
ABNF permits specifying literal text string directly, enclosed in
quotation-marks. Hence:
command = "command string"
Literal text strings are interpreted as a concatenated set of
printable characters.
NOTE: ABNF strings are case-insensitive and
the character set for these strings is us-ascii.
Hence:
rulename = "abc"
and:
rulename = "aBc"
will match "abc", "Abc", "aBc", "abC", "ABc", "aBC", "AbC" and "ABC".
To specify a rule which IS case SENSITIVE,
specify the characters individually.
For example:
rulename = %d97 %d98 %d99
or
rulename = %d97.98.99
Crocker & Overell Standards Track [Page 4]
RFC 2234 ABNF for Syntax Specifications November 1997
will match only the string which comprises only lowercased
characters, abc.
2.4 External Encodings
External representations of terminal value characters will vary
according to constraints in the storage or transmission environment.
Hence, the same ABNF-based grammar may have multiple external
encodings, such as one for a 7-bit US-ASCII environment, another for
a binary octet environment and still a different one when 16-bit
Unicode is used. Encoding details are beyond the scope of ABNF,
although Appendix A (Core) provides definitions for a 7-bit US-ASCII
environment as has been common to much of the Internet.
By separating external encoding from the syntax, it is intended that
alternate encoding environments can be used for the same syntax.
3. OPERATORS
3.1 Concatenation Rule1 Rule2
A rule can define a simple, ordered string of values -- i.e., a
concatenation of contiguous characters -- by listing a sequence of
rule names. For example:
foo = %x61 ; a
bar = %x62 ; b
mumble = foo bar foo
So that the rule <mumble> matches the lowercase string "aba".
LINEAR WHITE SPACE: Concatenation is at the core of the ABNF
parsing model. A string of contiguous characters (values) is
parsed according to the rules defined in ABNF. For Internet
specifications, there is some history of permitting linear white
space (space and horizontal tab) to be freelyPand
implicitlyPinterspersed around major constructs, such as
delimiting special characters or atomic strings.
NOTE: This specification for ABNF does not
provide for implicit specification of linear white
space.
Any grammar which wishes to permit linear white space around
delimiters or string segments must specify it explicitly. It is
often useful to provide for such white space in "core" rules that are
Crocker & Overell Standards Track [Page 5]
RFC 2234 ABNF for Syntax Specifications November 1997
then used variously among higher-level rules. The "core" rules might
be formed into a lexical analyzer or simply be part of the main
ruleset.
3.2 Alternatives Rule1 / Rule2
Elements separated by forward slash ("/") are alternatives.
Therefore,
foo / bar
will accept <foo> or <bar>.
NOTE: A quoted string containing alphabetic
characters is special form for specifying alternative
characters and is interpreted as a non-terminal
representing the set of combinatorial strings with the
contained characters, in the specified order but with
any mixture of upper and lower case..
3.3 Incremental Alternatives Rule1 =/ Rule2
It is sometimes convenient to specify a list of alternatives in
fragments. That is, an initial rule may match one or more
alternatives, with later rule definitions adding to the set of
alternatives. This is particularly useful for otherwise- independent
specifications which derive from the same parent rule set, such as
often occurs with parameter lists. ABNF permits this incremental
definition through the construct:
oldrule =/ additional-alternatives
So that the rule set
ruleset = alt1 / alt2
ruleset =/ alt3
ruleset =/ alt4 / alt5
is the same as specifying
ruleset = alt1 / alt2 / alt3 / alt4 / alt5
Crocker & Overell Standards Track [Page 6]
RFC 2234 ABNF for Syntax Specifications November 1997
3.4 Value Range Alternatives %c##-##
A range of alternative numeric values can be specified compactly,
using dash ("-") to indicate the range of alternative values. Hence:
DIGIT = %x30-39
is equivalent to:
DIGIT = "0" / "1" / "2" / "3" / "4" / "5" / "6" /
"7" / "8" / "9"
Concatenated numeric values and numeric value ranges can not be
specified in the same string. A numeric value may use the dotted
notation for concatenation or it may use the dash notation to specify
one value range. Hence, to specify one printable character, between
end of line sequences, the specification could be:
char-line = %x0D.0A %x20-7E %x0D.0A
3.5 Sequence Group (Rule1 Rule2)
Elements enclosed in parentheses are treated as a single element,
whose contents are STRICTLY ORDERED. Thus,
elem (foo / bar) blat
which matches (elem foo blat) or (elem bar blat).
elem foo / bar blat
matches (elem foo) or (bar blat).
NOTE: It is strongly advised to use grouping
notation, rather than to rely on proper reading of
"bare" alternations, when alternatives consist of
multiple rule names or literals.
Hence it is recommended that instead of the above form, the form:
(elem foo) / (bar blat)
be used. It will avoid misinterpretation by casual readers.
The sequence group notation is also used within free text to set off
an element sequence from the prose.
Crocker & Overell Standards Track [Page 7]
RFC 2234 ABNF for Syntax Specifications November 1997
3.6 Variable Repetition *Rule
The operator "*" preceding an element indicates repetition. The full
form is:
<a>*<b>element
where <a> and <b> are optional decimal values, indicating at least
<a> and at most <b> occurrences of element.
Default values are 0 and infinity so that *<element> allows any
number, including zero; 1*<element> requires at least one;
3*3<element> allows exactly 3 and 1*2<element> allows one or two.
3.7 Specific Repetition nRule
A rule of the form:
<n>element
is equivalent to
<n>*<n>element
That is, exactly <N> occurrences of <element>. Thus 2DIGIT is a
2-digit number, and 3ALPHA is a string of three alphabetic
characters.
3.8 Optional Sequence [RULE]
Square brackets enclose an optional element sequence:
[foo bar]
is equivalent to
*1(foo bar).
3.9 ; Comment
A semi-colon starts a comment that continues to the end of line.
This is a simple way of including useful notes in parallel with the
specifications.
Crocker & Overell Standards Track [Page 8]
RFC 2234 ABNF for Syntax Specifications November 1997
3.10 Operator Precedence
The various mechanisms described above have the following precedence,
from highest (binding tightest) at the top, to lowest and loosest at
the bottom:
Strings, Names formation
Comment
Value range
Repetition
Grouping, Optional
Concatenation
Alternative
Use of the alternative operator, freely mixed with concatenations can
be confusing.
Again, it is recommended that the grouping operator be used to
make explicit concatenation groups.
4. ABNF DEFINITION OF ABNF
This syntax uses the rules provided in Appendix A (Core).
rulelist = 1*( rule / (*c-wsp c-nl) )
rule = rulename defined-as elements c-nl
; continues if next line starts
; with white space
rulename = ALPHA *(ALPHA / DIGIT / "-")
defined-as = *c-wsp ("=" / "=/") *c-wsp
; basic rules definition and
; incremental alternatives
elements = alternation *c-wsp
c-wsp = WSP / (c-nl WSP)
c-nl = comment / CRLF
; comment or newline
comment = ";" *(WSP / VCHAR) CRLF
alternation = concatenation
*(*c-wsp "/" *c-wsp concatenation)
Crocker & Overell Standards Track [Page 9]
RFC 2234 ABNF for Syntax Specifications November 1997
concatenation = repetition *(1*c-wsp repetition)
repetition = [repeat] element
repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)
element = rulename / group / option /
char-val / num-val / prose-val
group = "(" *c-wsp alternation *c-wsp ")"
option = "[" *c-wsp alternation *c-wsp "]"
char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE
; quoted string of SP and VCHAR
without DQUOTE
num-val = "%" (bin-val / dec-val / hex-val)
bin-val = "b" 1*BIT
[ 1*("." 1*BIT) / ("-" 1*BIT) ]
; series of concatenated bit values
; or single ONEOF range
dec-val = "d" 1*DIGIT
[ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]
hex-val = "x" 1*HEXDIG
[ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]
prose-val = "<" *(%x20-3D / %x3F-7E) ">"
; bracketed string of SP and VCHAR
without angles
; prose description, to be used as
last resort
5. SECURITY CONSIDERATIONS
Security is truly believed to be irrelevant to this document.
Crocker & Overell Standards Track [Page 10]
RFC 2234 ABNF for Syntax Specifications November 1997
6. APPENDIX A - CORE
This Appendix is provided as a convenient core for specific grammars.
The definitions may be used as a core set of rules.
6.1 Core Rules
Certain basic rules are in uppercase, such as SP, HTAB, CRLF,
DIGIT, ALPHA, etc.
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
BIT = "0" / "1"
CHAR = %x01-7F
; any 7-bit US-ASCII character,
excluding NUL
CR = %x0D
; carriage return
CRLF = CR LF
; Internet standard newline
CTL = %x00-1F / %x7F
; controls
DIGIT = %x30-39
; 0-9
DQUOTE = %x22
; " (Double Quote)
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
HTAB = %x09
; horizontal tab
LF = %x0A
; linefeed
LWSP = *(WSP / CRLF WSP)
; linear white space (past newline)
OCTET = %x00-FF
; 8 bits of data
SP = %x20
Crocker & Overell Standards Track [Page 11]
RFC 2234 ABNF for Syntax Specifications November 1997
; space
VCHAR = %x21-7E
; visible (printing) characters
WSP = SP / HTAB
; white space
6.2 Common Encoding
Externally, data are represented as "network virtual ASCII", namely
7-bit US-ASCII in an 8-bit field, with the high (8th) bit set to
zero. A string of values is in "network byte order" with the
higher-valued bytes represented on the left-hand side and being sent
over the network first.
7. ACKNOWLEDGMENTS
The syntax for ABNF was originally specified in RFC 733. Ken L.
Harrenstien, of SRI International, was responsible for re-coding the
BNF into an augmented BNF that makes the representation smaller and
easier to understand.
This recent project began as a simple effort to cull out the portion
of RFC 822 which has been repeatedly cited by non-email specification
writers, namely the description of augmented BNF. Rather than simply
and blindly converting the existing text into a separate document,
the working group chose to give careful consideration to the
deficiencies, as well as benefits, of the existing specification and
related specifications available over the last 15 years and therefore
to pursue enhancement. This turned the project into something rather
more ambitious than first intended. Interestingly the result is not
massively different from that original, although decisions such as
removing the list notation came as a surprise.
The current round of specification was part of the DRUMS working
group, with significant contributions from Jerome Abela , Harald
Alvestrand, Robert Elz, Roger Fajman, Aviva Garrett, Tom Harsch, Dan
Kohn, Bill McQuillan, Keith Moore, Chris Newman , Pete Resnick and
Henning Schulzrinne.
Crocker & Overell Standards Track [Page 12]
RFC 2234 ABNF for Syntax Specifications November 1997
8. REFERENCES
[US-ASCII] Coded Character Set--7-Bit American Standard Code for
Information Interchange, ANSI X3.4-1986.
[RFC733] Crocker, D., Vittal, J., Pogran, K., and D. Henderson,
"Standard for the Format of ARPA Network Text Message," RFC 733,
November 1977.
[RFC822] Crocker, D., "Standard for the Format of ARPA Internet Text
Messages", STD 11, RFC 822, August 1982.
9. CONTACT
David H. Crocker Paul Overell
Internet Mail Consortium Demon Internet Ltd
675 Spruce Dr. Dorking Business Park
Sunnyvale, CA 94086 USA Dorking
Surrey, RH4 1HN
UK
Phone: +1 408 246 8253
Fax: +1 408 249 6205
EMail: dcrocker@imc.org paulo@turnpike.com
Crocker & Overell Standards Track [Page 13]
RFC 2234 ABNF for Syntax Specifications November 1997
10. Full Copyright Statement
Copyright (C) The Internet Society (1997). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Crocker & Overell Standards Track [Page 14]

View File

@ -0,0 +1,451 @@
Network Working Group P. Hoffman
Request for Comments: 2487 Internet Mail Consortium
Category: Standards Track January 1999
SMTP Service Extension for Secure SMTP over TLS
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (1999). All Rights Reserved.
1. Abstract
This document describes an extension to the SMTP service that allows
an SMTP server and client to use transport-layer security to provide
private, authenticated communication over the Internet. This gives
SMTP agents the ability to protect some or all of their
communications from eavesdroppers and attackers.
2. Introduction
SMTP [RFC-821] servers and clients normally communicate in the clear
over the Internet. In many cases, this communication goes through one
or more router that is not controlled or trusted by either entity.
Such an untrusted router might allow a third party to monitor or
alter the communications between the server and client.
Further, there is often a desire for two SMTP agents to be able to
authenticate each others' identities. For example, a secure SMTP
server might only allow communications from other SMTP agents it
knows, or it might act differently for messages received from an
agent it knows than from one it doesn't know.
TLS [TLS], more commonly known as SSL, is a popular mechanism for
enhancing TCP communications with privacy and authentication. TLS is
in wide use with the HTTP protocol, and is also being used for adding
security to many other common protocols that run over TCP.
Hoffman Standards Track [Page 1]
RFC 2487 SMTP Service Extension January 1999
2.1 Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC-2119].
3. STARTTLS Extension
The STARTTLS extension to SMTP is laid out as follows:
(1) the name of the SMTP service defined here is STARTTLS;
(2) the EHLO keyword value associated with the extension is STARTTLS;
(3) the STARTTLS keyword has no parameters;
(4) a new SMTP verb, "STARTTLS", is defined;
(5) no additional parameters are added to any SMTP command.
4. The STARTTLS Keyword
The STARTTLS keyword is used to tell the SMTP client that the SMTP
server allows use of TLS. It takes no parameters.
5. The STARTTLS Command
The format for the STARTTLS command is:
STARTTLS
with no parameters.
After the client gives the STARTTLS command, the server responds with
one of the following reply codes:
220 Ready to start TLS
501 Syntax error (no parameters allowed)
454 TLS not available due to temporary reason
A publicly-referenced SMTP server MUST NOT require use of the
STARTTLS extension in order to deliver mail locally. This rule
prevents the STARTTLS extension from damaging the interoperability of
the Internet's SMTP infrastructure. A publicly-referenced SMTP server
is an SMTP server which runs on port 25 of an Internet host listed in
the MX record (or A record if an MX record is not present) for the
domain name on the right hand side of an Internet mail address.
Hoffman Standards Track [Page 2]
RFC 2487 SMTP Service Extension January 1999
Any SMTP server may refuse to accept messages for relay based on
authentication supplied during the TLS negotiation. An SMTP server
that is not publicly referenced may refuse to accept any messages for
relay or local delivery based on authentication supplied during the
TLS negotiation.
A SMTP server that is not publicly referenced may choose to require
that the client perform a TLS negotiation before accepting any
commands. In this case, the server SHOULD return the reply code:
530 Must issue a STARTTLS command first
to every command other than NOOP, EHLO, STARTTLS, or QUIT. If the
client and server are using the ENHANCEDSTATUSCODES ESMTP extension
[RFC-2034], the status code to be returned SHOULD be 5.7.0.
After receiving a 220 response to a STARTTLS command, the client
SHOULD start the TLS negotiation before giving any other SMTP
commands.
If the SMTP client is using pipelining as defined in RFC 1854, the
STARTTLS command must be the last command in a group.
5.1 Processing After the STARTTLS Command
After the TLS handshake has been completed, both parties MUST
immediately decide whether or not to continue based on the
authentication and privacy achieved. The SMTP client and server may
decide to move ahead even if the TLS negotiation ended with no
authentication and/or no privacy because most SMTP services are
performed with no authentication and no privacy, but some SMTP
clients or servers may want to continue only if a particular level of
authentication and/or privacy was achieved.
If the SMTP client decides that the level of authentication or
privacy is not high enough for it to continue, it SHOULD issue an
SMTP QUIT command immediately after the TLS negotiation is complete.
If the SMTP server decides that the level of authentication or
privacy is not high enough for it to continue, it SHOULD reply to
every SMTP command from the client (other than a QUIT command) with
the 554 reply code (with a possible text string such as "Command
refused due to lack of security").
The decision of whether or not to believe the authenticity of the
other party in a TLS negotiation is a local matter. However, some
general rules for the decisions are:
Hoffman Standards Track [Page 3]
RFC 2487 SMTP Service Extension January 1999
- A SMTP client would probably only want to authenticate an SMTP
server whose server certificate has a domain name that is the
domain name that the client thought it was connecting to.
- A publicly-referenced SMTP server would probably want to accept
any certificate from an SMTP client, and would possibly want to
put distinguishing information about the certificate in the
Received header of messages that were relayed or submitted from
the client.
5.2 Result of the STARTTLS Command
Upon completion of the TLS handshake, the SMTP protocol is reset to
the initial state (the state in SMTP after a server issues a 220
service ready greeting). The server MUST discard any knowledge
obtained from the client, such as the argument to the EHLO command,
which was not obtained from the TLS negotiation itself. The client
MUST discard any knowledge obtained from the server, such as the list
of SMTP service extensions, which was not obtained from the TLS
negotiation itself. The client SHOULD send an EHLO command as the
first command after a successful TLS negotiation.
The list of SMTP service extensions returned in response to an EHLO
command received after the TLS handshake MAY be different than the
list returned before the TLS handshake. For example, an SMTP server
might not want to advertise support for a particular SASL mechanism
[SASL] unless a client has sent an appropriate client certificate
during a TLS handshake.
Both the client and the server MUST know if there is a TLS session
active. A client MUST NOT attempt to start a TLS session if a TLS
session is already active. A server MUST NOT return the TLS extension
in response to an EHLO command received after a TLS handshake has
completed.
6. Usage Example
The following dialog illustrates how a client and server can start a
TLS session:
S: <waits for connection on TCP port 25>
C: <opens connection>
S: 220 mail.imc.org SMTP service ready
C: EHLO mail.ietf.org
S: 250-mail.imc.org offers a warm hug of welcome
S: 250 STARTTLS
C: STARTTLS
S: 220 Go ahead
C: <starts TLS negotiation>
Hoffman Standards Track [Page 4]
RFC 2487 SMTP Service Extension January 1999
C & S: <negotiate a TLS session>
C & S: <check result of negotiation>
C: <continues by sending an SMTP command>
. . .
7. Security Considerations
It should be noted that SMTP is not an end-to-end mechanism. Thus, if
an SMTP client/server pair decide to add TLS privacy, they are not
securing the transport from the originating mail user agent to the
recipient. Further, because delivery of a single piece of mail may
go between more than two SMTP servers, adding TLS privacy to one pair
of servers does not mean that the entire SMTP chain has been made
private. Further, just because an SMTP server can authenticate an
SMTP client, it does not mean that the mail from the SMTP client was
authenticated by the SMTP client when the client received it.
Both the STMP client and server must check the result of the TLS
negotiation to see whether acceptable authentication or privacy was
achieved. Ignoring this step completely invalidates using TLS for
security. The decision about whether acceptable authentication or
privacy was achieved is made locally, is implementation-dependant,
and is beyond the scope of this document.
The SMTP client and server should note carefully the result of the
TLS negotiation. If the negotiation results in no privacy, or if it
results in privacy using algorithms or key lengths that are deemed
not strong enough, or if the authentication is not good enough for
either party, the client may choose to end the SMTP session with an
immediate QUIT command, or the server may choose to not accept any
more SMTP commands.
A server announcing in an EHLO response that it uses a particular TLS
protocol should not pose any security issues, since any use of TLS
will be at least as secure as no use of TLS.
A man-in-the-middle attack can be launched by deleting the "250
STARTTLS" response from the server. This would cause the client not
to try to start a TLS session. An SMTP client can protect against
this attack by recording the fact that a particular SMTP server
offers TLS during one session and generating an alarm if it does not
appear in the EHLO response for a later session. The lack of TLS
during a session SHOULD NOT result in the bouncing of email, although
it could result in delayed processing.
Hoffman Standards Track [Page 5]
RFC 2487 SMTP Service Extension January 1999
Before the TLS handshake has begun, any protocol interactions are
performed in the clear and may be modified by an active attacker. For
this reason, clients and servers MUST discard any knowledge obtained
prior to the start of the TLS handshake upon completion of the TLS
handshake.
The STARTTLS extension is not suitable for authenticating the author
of an email message unless every hop in the delivery chain, including
the submission to the first SMTP server, is authenticated. Another
proposal [SMTP-AUTH] can be used to authenticate delivery and MIME
security multiparts [MIME-SEC] can be used to authenticate the author
of an email message. In addition, the [SMTP-AUTH] proposal offers
simpler and more flexible options to authenticate an SMTP client and
the SASL EXTERNAL mechanism [SASL] MAY be used in conjunction with
the STARTTLS command to provide an authorization identity.
Hoffman Standards Track [Page 6]
RFC 2487 SMTP Service Extension January 1999
A. References
[RFC-821] Postel, J., "Simple Mail Transfer Protocol", RFC 821,
August 1982.
[RFC-1869] Klensin, J., Freed, N, Rose, M, Stefferud, E. and D.
Crocker, "SMTP Service Extensions", STD 10, RFC 1869,
November 1995.
[RFC-2034] Freed, N., "SMTP Service Extension for Returning Enhanced
Error Codes", RFC 2034, October 1996.
[RFC-2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[SASL] Myers, J., "Simple Authentication and Security Layer
(SASL)", RFC 2222, October 1997.
[SMTP-AUTH] "SMTP Service Extension for Authentication", Work in
Progress.
[TLS] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0",
RFC 2246, January 1999.
B. Author's Address
Paul Hoffman
Internet Mail Consortium
127 Segre Place
Santa Cruz, CA 95060
Phone: (831) 426-9827
EMail: phoffman@imc.org
Hoffman Standards Track [Page 7]
RFC 2487 SMTP Service Extension January 1999
C. Full Copyright Statement
Copyright (C) The Internet Society (1999). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Hoffman Standards Track [Page 8]

View File

@ -0,0 +1,619 @@
Network Working Group J. Myers
Request for Comments: 2554 Netscape Communications
Category: Standards Track March 1999
SMTP Service Extension
for Authentication
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (1999). All Rights Reserved.
1. Introduction
This document defines an SMTP service extension [ESMTP] whereby an
SMTP client may indicate an authentication mechanism to the server,
perform an authentication protocol exchange, and optionally negotiate
a security layer for subsequent protocol interactions. This
extension is a profile of the Simple Authentication and Security
Layer [SASL].
2. Conventions Used in this Document
In examples, "C:" and "S:" indicate lines sent by the client and
server respectively.
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
in this document are to be interpreted as defined in "Key words for
use in RFCs to Indicate Requirement Levels" [KEYWORDS].
3. The Authentication service extension
(1) the name of the SMTP service extension is "Authentication"
(2) the EHLO keyword value associated with this extension is "AUTH"
Myers Standards Track [Page 1]
RFC 2554 SMTP Authentication March 1999
(3) The AUTH EHLO keyword contains as a parameter a space separated
list of the names of supported SASL mechanisms.
(4) a new SMTP verb "AUTH" is defined
(5) an optional parameter using the keyword "AUTH" is added to the
MAIL FROM command, and extends the maximum line length of the
MAIL FROM command by 500 characters.
(6) this extension is appropriate for the submission protocol
[SUBMIT].
4. The AUTH command
AUTH mechanism [initial-response]
Arguments:
a string identifying a SASL authentication mechanism.
an optional base64-encoded response
Restrictions:
After an AUTH command has successfully completed, no more AUTH
commands may be issued in the same session. After a successful
AUTH command completes, a server MUST reject any further AUTH
commands with a 503 reply.
The AUTH command is not permitted during a mail transaction.
Discussion:
The AUTH command indicates an authentication mechanism to the
server. If the server supports the requested authentication
mechanism, it performs an authentication protocol exchange to
authenticate and identify the user. Optionally, it also
negotiates a security layer for subsequent protocol
interactions. If the requested authentication mechanism is not
supported, the server rejects the AUTH command with a 504
reply.
The authentication protocol exchange consists of a series of
server challenges and client answers that are specific to the
authentication mechanism. A server challenge, otherwise known
as a ready response, is a 334 reply with the text part
containing a BASE64 encoded string. The client answer consists
of a line containing a BASE64 encoded string. If the client
wishes to cancel an authentication exchange, it issues a line
with a single "*". If the server receives such an answer, it
MUST reject the AUTH command by sending a 501 reply.
Myers Standards Track [Page 2]
RFC 2554 SMTP Authentication March 1999
The optional initial-response argument to the AUTH command is
used to save a round trip when using authentication mechanisms
that are defined to send no data in the initial challenge.
When the initial-response argument is used with such a
mechanism, the initial empty challenge is not sent to the
client and the server uses the data in the initial-response
argument as if it were sent in response to the empty challenge.
Unlike a zero-length client answer to a 334 reply, a zero-
length initial response is sent as a single equals sign ("=").
If the client uses an initial-response argument to the AUTH
command with a mechanism that sends data in the initial
challenge, the server rejects the AUTH command with a 535
reply.
If the server cannot BASE64 decode the argument, it rejects the
AUTH command with a 501 reply. If the server rejects the
authentication data, it SHOULD reject the AUTH command with a
535 reply unless a more specific error code, such as one listed
in section 6, is appropriate. Should the client successfully
complete the authentication exchange, the SMTP server issues a
235 reply.
The service name specified by this protocol's profile of SASL
is "smtp".
If a security layer is negotiated through the SASL
authentication exchange, it takes effect immediately following
the CRLF that concludes the authentication exchange for the
client, and the CRLF of the success reply for the server. Upon
a security layer's taking effect, the SMTP protocol is reset to
the initial state (the state in SMTP after a server issues a
220 service ready greeting). The server MUST discard any
knowledge obtained from the client, such as the argument to the
EHLO command, which was not obtained from the SASL negotiation
itself. The client MUST discard any knowledge obtained from
the server, such as the list of SMTP service extensions, which
was not obtained from the SASL negotiation itself (with the
exception that a client MAY compare the list of advertised SASL
mechanisms before and after authentication in order to detect
an active down-negotiation attack). The client SHOULD send an
EHLO command as the first command after a successful SASL
negotiation which results in the enabling of a security layer.
The server is not required to support any particular
authentication mechanism, nor are authentication mechanisms
required to support any security layers. If an AUTH command
fails, the client may try another authentication mechanism by
issuing another AUTH command.
Myers Standards Track [Page 3]
RFC 2554 SMTP Authentication March 1999
If an AUTH command fails, the server MUST behave the same as if
the client had not issued the AUTH command.
The BASE64 string may in general be arbitrarily long. Clients
and servers MUST be able to support challenges and responses
that are as long as are generated by the authentication
mechanisms they support, independent of any line length
limitations the client or server may have in other parts of its
protocol implementation.
Examples:
S: 220 smtp.example.com ESMTP server ready
C: EHLO jgm.example.com
S: 250-smtp.example.com
S: 250 AUTH CRAM-MD5 DIGEST-MD5
C: AUTH FOOBAR
S: 504 Unrecognized authentication type.
C: AUTH CRAM-MD5
S: 334
PENCeUxFREJoU0NnbmhNWitOMjNGNndAZWx3b29kLmlubm9zb2Z0LmNvbT4=
C: ZnJlZCA5ZTk1YWVlMDljNDBhZjJiODRhMGMyYjNiYmFlNzg2ZQ==
S: 235 Authentication successful.
5. The AUTH parameter to the MAIL FROM command
AUTH=addr-spec
Arguments:
An addr-spec containing the identity which submitted the message
to the delivery system, or the two character sequence "<>"
indicating such an identity is unknown or insufficiently
authenticated. To comply with the restrictions imposed on ESMTP
parameters, the addr-spec is encoded inside an xtext. The syntax
of an xtext is described in section 5 of [ESMTP-DSN].
Discussion:
The optional AUTH parameter to the MAIL FROM command allows
cooperating agents in a trusted environment to communicate the
authentication of individual messages.
If the server trusts the authenticated identity of the client to
assert that the message was originally submitted by the supplied
addr-spec, then the server SHOULD supply the same addr-spec in an
AUTH parameter when relaying the message to any server which
supports the AUTH extension.
Myers Standards Track [Page 4]
RFC 2554 SMTP Authentication March 1999
A MAIL FROM parameter of AUTH=<> indicates that the original
submitter of the message is not known. The server MUST NOT treat
the message as having been originally submitted by the client.
If the AUTH parameter to the MAIL FROM is not supplied, the
client has authenticated, and the server believes the message is
an original submission by the client, the server MAY supply the
client's identity in the addr-spec in an AUTH parameter when
relaying the message to any server which supports the AUTH
extension.
If the server does not sufficiently trust the authenticated
identity of the client, or if the client is not authenticated,
then the server MUST behave as if the AUTH=<> parameter was
supplied. The server MAY, however, write the value of the AUTH
parameter to a log file.
If an AUTH=<> parameter was supplied, either explicitly or due to
the requirement in the previous paragraph, then the server MUST
supply the AUTH=<> parameter when relaying the message to any
server which it has authenticated to using the AUTH extension.
A server MAY treat expansion of a mailing list as a new
submission, setting the AUTH parameter to the mailing list
address or mailing list administration address when relaying the
message to list subscribers.
It is conforming for an implementation to be hard-coded to treat
all clients as being insufficiently trusted. In that case, the
implementation does nothing more than parse and discard
syntactically valid AUTH parameters to the MAIL FROM command and
supply AUTH=<> parameters to any servers to which it
authenticates using the AUTH extension.
Examples:
C: MAIL FROM:<e=mc2@example.com> AUTH=e+3Dmc2@example.com
S: 250 OK
Myers Standards Track [Page 5]
RFC 2554 SMTP Authentication March 1999
6. Error Codes
The following error codes may be used to indicate various conditions
as described.
432 A password transition is needed
This response to the AUTH command indicates that the user needs to
transition to the selected authentication mechanism. This typically
done by authenticating once using the PLAIN authentication mechanism.
534 Authentication mechanism is too weak
This response to the AUTH command indicates that the selected
authentication mechanism is weaker than server policy permits for
that user.
538 Encryption required for requested authentication mechanism
This response to the AUTH command indicates that the selected
authentication mechanism may only be used when the underlying SMTP
connection is encrypted.
454 Temporary authentication failure
This response to the AUTH command indicates that the authentication
failed due to a temporary server failure.
530 Authentication required
This response may be returned by any command other than AUTH, EHLO,
HELO, NOOP, RSET, or QUIT. It indicates that server policy requires
authentication in order to perform the requested action.
Myers Standards Track [Page 6]
RFC 2554 SMTP Authentication March 1999
7. Formal Syntax
The following syntax specification uses the augmented Backus-Naur
Form (BNF) notation as specified in [ABNF].
Except as noted otherwise, all alphabetic characters are case-
insensitive. The use of upper or lower case characters to define
token strings is for editorial clarity only. Implementations MUST
accept these strings in a case-insensitive fashion.
UPALPHA = %x41-5A ;; Uppercase: A-Z
LOALPHA = %x61-7A ;; Lowercase: a-z
ALPHA = UPALPHA / LOALPHA ;; case insensitive
DIGIT = %x30-39 ;; Digits 0-9
HEXDIGIT = %x41-46 / DIGIT ;; hexidecimal digit (uppercase)
hexchar = "+" HEXDIGIT HEXDIGIT
xchar = %x21-2A / %x2C-3C / %x3E-7E
;; US-ASCII except for "+", "=", SPACE and CTL
xtext = *(xchar / hexchar)
AUTH_CHAR = ALPHA / DIGIT / "-" / "_"
auth_type = 1*20AUTH_CHAR
auth_command = "AUTH" SPACE auth_type [SPACE (base64 / "=")]
*(CRLF [base64]) CRLF
auth_param = "AUTH=" xtext
;; The decoded form of the xtext MUST be either
;; an addr-spec or the two characters "<>"
base64 = base64_terminal /
( 1*(4base64_CHAR) [base64_terminal] )
base64_char = UPALPHA / LOALPHA / DIGIT / "+" / "/"
;; Case-sensitive
base64_terminal = (2base64_char "==") / (3base64_char "=")
continue_req = "334" SPACE [base64] CRLF
Myers Standards Track [Page 7]
RFC 2554 SMTP Authentication March 1999
CR = %x0C ;; ASCII CR, carriage return
CRLF = CR LF
CTL = %x00-1F / %x7F ;; any ASCII control character and DEL
LF = %x0A ;; ASCII LF, line feed
SPACE = %x20 ;; ASCII SP, space
8. References
[ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", RFC 2234, November 1997.
[CRAM-MD5] Klensin, J., Catoe, R. and P. Krumviede, "IMAP/POP
AUTHorize Extension for Simple Challenge/Response", RFC
2195, September 1997.
[ESMTP] Klensin, J., Freed, N., Rose, M., Stefferud, E. and D.
Crocker, "SMTP Service Extensions", RFC 1869, November
1995.
[ESMTP-DSN] Moore, K, "SMTP Service Extension for Delivery Status
Notifications", RFC 1891, January 1996.
[KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[SASL] Myers, J., "Simple Authentication and Security Layer
(SASL)", RFC 2222, October 1997.
[SUBMIT] Gellens, R. and J. Klensin, "Message Submission", RFC
2476, December 1998.
[RFC821] Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC
821, August 1982.
[RFC822] Crocker, D., "Standard for the Format of ARPA Internet
Text Messages", STD 11, RFC 822, August 1982.
Myers Standards Track [Page 8]
RFC 2554 SMTP Authentication March 1999
9. Security Considerations
Security issues are discussed throughout this memo.
If a client uses this extension to get an encrypted tunnel through an
insecure network to a cooperating server, it needs to be configured
to never send mail to that server when the connection is not mutually
authenticated and encrypted. Otherwise, an attacker could steal the
client's mail by hijacking the SMTP connection and either pretending
the server does not support the Authentication extension or causing
all AUTH commands to fail.
Before the SASL negotiation has begun, any protocol interactions are
performed in the clear and may be modified by an active attacker.
For this reason, clients and servers MUST discard any knowledge
obtained prior to the start of the SASL negotiation upon completion
of a SASL negotiation which results in a security layer.
This mechanism does not protect the TCP port, so an active attacker
may redirect a relay connection attempt to the submission port
[SUBMIT]. The AUTH=<> parameter prevents such an attack from causing
an relayed message without an envelope authentication to pick up the
authentication of the relay client.
A message submission client may require the user to authenticate
whenever a suitable SASL mechanism is advertised. Therefore, it may
not be desirable for a submission server [SUBMIT] to advertise a SASL
mechanism when use of that mechanism grants the client no benefits
over anonymous submission.
This extension is not intended to replace or be used instead of end-
to-end message signature and encryption systems such as S/MIME or
PGP. This extension addresses a different problem than end-to-end
systems; it has the following key differences:
(1) it is generally useful only within a trusted enclave
(2) it protects the entire envelope of a message, not just the
message's body.
(3) it authenticates the message submission, not authorship of the
message content
(4) it can give the sender some assurance the message was
delivered to the next hop in the case where the sender
mutually authenticates with the next hop and negotiates an
appropriate security layer.
Myers Standards Track [Page 9]
RFC 2554 SMTP Authentication March 1999
Additional security considerations are mentioned in the SASL
specification [SASL].
10. Author's Address
John Gardiner Myers
Netscape Communications
501 East Middlefield Road
Mail Stop MV-029
Mountain View, CA 94043
EMail: jgmyers@netscape.com
Myers Standards Track [Page 10]
RFC 2554 SMTP Authentication March 1999
11. Full Copyright Statement
Copyright (C) The Internet Society (1999). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Myers Standards Track [Page 11]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,507 @@
Network Working Group K. Zeilenga, Ed.
Request for Comments: 4505 OpenLDAP Foundation
Obsoletes: 2245 June 2006
Category: Standards Track
Anonymous Simple Authentication and Security Layer (SASL) Mechanism
Status of This Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (2006).
Abstract
On the Internet, it is common practice to permit anonymous access to
various services. Traditionally, this has been done with a plain-
text password mechanism using "anonymous" as the user name and using
optional trace information, such as an email address, as the
password. As plain-text login commands are not permitted in new IETF
protocols, a new way to provide anonymous login is needed within the
context of the Simple Authentication and Security Layer (SASL)
framework.
1. Introduction
This document defines an anonymous mechanism for the Simple
Authentication and Security Layer ([SASL]) framework. The name
associated with this mechanism is "ANONYMOUS".
Unlike many other SASL mechanisms, whose purpose is to authenticate
and identify the user to a server, the purpose of this SASL mechanism
is to allow the user to gain access to services or resources without
requiring the user to establish or otherwise disclose their identity
to the server. That is, this mechanism provides an anonymous login
method.
This mechanism does not provide a security layer.
This document replaces RFC 2245. Changes since RFC 2245 are detailed
in Appendix A.
Zeilenga Standards Track [Page 1]
RFC 4505 Anonymous SASL Mechanism June 2006
2. The Anonymous Mechanism
The mechanism consists of a single message from the client to the
server. The client may include in this message trace information in
the form of a string of [UTF-8]-encoded [Unicode] characters prepared
in accordance with [StringPrep] and the "trace" stringprep profile
defined in Section 3 of this document. The trace information, which
has no semantical value, should take one of two forms: an Internet
email address, or an opaque string that does not contain the '@'
(U+0040) character and that can be interpreted by the system
administrator of the client's domain. For privacy reasons, an
Internet email address or other information identifying the user
should only be used with permission from the user.
A server that permits anonymous access will announce support for the
ANONYMOUS mechanism and allow anyone to log in using that mechanism,
usually with restricted access.
A formal grammar for the client message using Augmented BNF [ABNF] is
provided below as a tool for understanding this technical
specification.
message = [ email / token ]
;; to be prepared in accordance with Section 3
UTF1 = %x00-3F / %x41-7F ;; less '@' (U+0040)
UTF2 = %xC2-DF UTF0
UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
%xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
%xF4 %x80-8F 2(UTF0)
UTF0 = %x80-BF
TCHAR = UTF1 / UTF2 / UTF3 / UTF4
;; any UTF-8 encoded Unicode character
;; except '@' (U+0040)
email = addr-spec
;; as defined in [IMAIL]
token = 1*255TCHAR
Note to implementors:
The <token> production is restricted to 255 UTF-8-encoded Unicode
characters. As the encoding of a characters uses a sequence of 1
to 4 octets, a token may be as long as 1020 octets.
Zeilenga Standards Track [Page 2]
RFC 4505 Anonymous SASL Mechanism June 2006
3. The "trace" Profile of "Stringprep"
This section defines the "trace" profile of [StringPrep]. This
profile is designed for use with the SASL ANONYMOUS Mechanism.
Specifically, the client is to prepare the <message> production in
accordance with this profile.
The character repertoire of this profile is Unicode 3.2 [Unicode].
No mapping is required by this profile.
No Unicode normalization is required by this profile.
The list of unassigned code points for this profile is that provided
in Appendix A of [StringPrep]. Unassigned code points are not
prohibited.
Characters from the following tables of [StringPrep] are prohibited:
- C.2.1 (ASCII control characters)
- C.2.2 (Non-ASCII control characters)
- C.3 (Private use characters)
- C.4 (Non-character code points)
- C.5 (Surrogate codes)
- C.6 (Inappropriate for plain text)
- C.8 (Change display properties are deprecated)
- C.9 (Tagging characters)
No additional characters are prohibited.
This profile requires bidirectional character checking per Section 6
of [StringPrep].
4. Example
Here is a sample ANONYMOUS login between an IMAP client and server.
In this example, "C:" and "S:" indicate lines sent by the client and
server, respectively. If such lines are wrapped without a new "C:"
or "S:" label, then the wrapping is for editorial clarity and is not
part of the command.
Note that this example uses the IMAP profile [IMAP4] of SASL. The
base64 encoding of challenges and responses as well as the "+ "
preceding the responses are part of the IMAP4 profile, not part of
SASL itself. Additionally, protocols with SASL profiles permitting
an initial client response will be able to avoid the extra round trip
below (the server response with an empty "+ ").
Zeilenga Standards Track [Page 3]
RFC 4505 Anonymous SASL Mechanism June 2006
In this example, the trace information is "sirhc".
S: * OK IMAP4 server ready
C: A001 CAPABILITY
S: * CAPABILITY IMAP4 IMAP4rev1 AUTH=DIGEST-MD5 AUTH=ANONYMOUS
S: A001 OK done
C: A002 AUTHENTICATE ANONYMOUS
S: +
C: c2lyaGM=
S: A003 OK Welcome, trace information has been logged.
5. Security Considerations
The ANONYMOUS mechanism grants access to services and/or resources by
anyone. For this reason, it should be disabled by default so that
the administrator can make an explicit decision to enable it.
If the anonymous user has any write privileges, a denial-of-service
attack is possible by filling up all available space. This can be
prevented by disabling all write access by anonymous users.
If anonymous users have read and write access to the same area, the
server can be used as a communication mechanism to exchange
information anonymously. Servers that accept anonymous submissions
should implement the common "drop box" model, which forbids anonymous
read access to the area where anonymous submissions are accepted.
If the anonymous user can run many expensive operations (e.g., an
IMAP SEARCH BODY command), this could enable a denial-of-service
attack. Servers are encouraged to reduce the priority of anonymous
users or limit their resource usage.
While servers may impose a limit on the number of anonymous users,
note that such limits enable denial-of-service attacks and should be
used with caution.
The trace information is not authenticated, so it can be falsified.
This can be used as an attempt to get someone else in trouble for
access to questionable information. Administrators investigating
abuse need to realize that this trace information may be falsified.
A client that uses the user's correct email address as trace
information without explicit permission may violate that user's
privacy. Anyone who accesses an anonymous archive on a sensitive
subject (e.g., sexual abuse) likely has strong privacy needs.
Clients should not send the email address without the explicit
permission of the user and should offer the option of supplying no
trace information, thus only exposing the source IP address and time.
Zeilenga Standards Track [Page 4]
RFC 4505 Anonymous SASL Mechanism June 2006
Anonymous proxy servers could enhance this privacy but would have to
consider the resulting potential denial-of-service attacks.
Anonymous connections are susceptible to man-in-the-middle attacks
that view or alter the data transferred. Clients and servers are
encouraged to support external data security services.
Protocols that fail to require an explicit anonymous login are more
susceptible to break-ins given certain common implementation
techniques. Specifically, Unix servers that offer user login may
initially start up as root and switch to the appropriate user id
after an explicit login command. Normally, such servers refuse all
data access commands prior to explicit login and may enter a
restricted security environment (e.g., the Unix chroot(2) function)
for anonymous users. If anonymous access is not explicitly
requested, the entire data access machinery is exposed to external
security attacks without the chance for explicit protective measures.
Protocols that offer restricted data access should not allow
anonymous data access without an explicit login step.
General [SASL] security considerations apply to this mechanism.
[StringPrep] security considerations and [Unicode] security
considerations discussed in [StringPrep] apply to this mechanism.
[UTF-8] security considerations also apply.
6. IANA Considerations
The SASL Mechanism registry [IANA-SASL] entry for the ANONYMOUS
mechanism has been updated by the IANA to reflect that this document
now provides its technical specification.
To: iana@iana.org
Subject: Updated Registration of SASL mechanism ANONYMOUS
SASL mechanism name: ANONYMOUS
Security considerations: See RFC 4505.
Published specification (optional, recommended): RFC 4505
Person & email address to contact for further information:
Kurt Zeilenga <Kurt@OpenLDAP.org>
Chris Newman <Chris.Newman@sun.com>
Intended usage: COMMON
Author/Change controller: IESG <iesg@ietf.org>
Note: Updates existing entry for ANONYMOUS
Zeilenga Standards Track [Page 5]
RFC 4505 Anonymous SASL Mechanism June 2006
The [StringPrep] profile "trace", first defined in this RFC, has been
registered:
To: iana@iana.org
Subject: Initial Registration of Stringprep "trace" profile
Stringprep profile: trace
Published specification: RFC 4505
Person & email address to contact for further information:
Kurt Zeilenga <kurt@openldap.org>
7. Acknowledgement
This document is a revision of RFC 2245 by Chris Newman. Portions of
the grammar defined in Section 1 were borrowed from RFC 3629 by
Francois Yergeau.
This document is a product of the IETF SASL WG.
8. Normative References
[ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", RFC 4234, October 2005.
[IMAIL] Resnick, P., "Internet Message Format", RFC 2822, April
2001.
[SASL] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
Authentication and Security Layer (SASL)", RFC 4422,
June 2006.
[StringPrep] Hoffman, P. and M. Blanchet, "Preparation of
Internationalized Strings ('stringprep')", RFC 3454,
December 2002.
[Unicode] The Unicode Consortium, "The Unicode Standard, Version
3.2.0" is defined by "The Unicode Standard, Version 3.0"
(Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
as amended by the "Unicode Standard Annex #27: Unicode
3.1" (http://www.unicode.org/reports/tr27/) and by the
"Unicode Standard Annex #28: Unicode 3.2"
(http://www.unicode.org/reports/tr28/).
[UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
10646", RFC 3629 (also STD 63), November 2003.
Zeilenga Standards Track [Page 6]
RFC 4505 Anonymous SASL Mechanism June 2006
9. Informative References
[IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
4rev1", RFC 3501, March 2003.
[IANA-SASL] IANA, "SIMPLE AUTHENTICATION AND SECURITY LAYER (SASL)
MECHANISMS", <http://www.iana.org/assignments/sasl-
mechanisms>.
Zeilenga Standards Track [Page 7]
RFC 4505 Anonymous SASL Mechanism June 2006
Appendix A. Changes since RFC 2245
This appendix is non-normative.
RFC 2245 allows the client to include optional trace information in
the form of a human readable string. RFC 2245 restricted this string
to US-ASCII. As the Internet is international, this document uses a
string restricted to UTF-8 encoded Unicode characters. A
"stringprep" profile is defined to precisely define which Unicode
characters are allowed in this string. While the string remains
restricted to 255 characters, the encoded length of each character
may now range from 1 to 4 octets.
Additionally, a number of editorial changes were made.
Editor's Address
Kurt D. Zeilenga
OpenLDAP Foundation
EMail: Kurt@OpenLDAP.org
Zeilenga Standards Track [Page 8]
RFC 4505 Anonymous SASL Mechanism June 2006
Full Copyright Statement
Copyright (C) The Internet Society (2006).
This document is subject to the rights, licenses and restrictions
contained in BCP 78, and except as set forth therein, the authors
retain all their rights.
This document and the information contained herein are provided on an
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property
The IETF takes no position regarding the validity or scope of any
Intellectual Property Rights or other rights that might be claimed to
pertain to the implementation or use of the technology described in
this document or the extent to which any license under such rights
might or might not be available; nor does it represent that it has
made any independent effort to identify any such rights. Information
on the procedures with respect to rights in RFC documents can be
found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any
assurances of licenses to be made available, or the result of an
attempt made to obtain a general license or permission for the use of
such proprietary rights by implementers or users of this
specification can be obtained from the IETF on-line IPR repository at
http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any
copyrights, patents or patent applications, or other proprietary
rights that may cover technology that may be required to implement
this standard. Please address the information to the IETF at
ietf-ipr@ietf.org.
Acknowledgement
Funding for the RFC Editor function is provided by the IETF
Administrative Support Activity (IASA).
Zeilenga Standards Track [Page 9]

View File

@ -0,0 +1,619 @@
Network Working Group K. Zeilenga, Ed.
Request for Comments: 4616 OpenLDAP Foundation
Updates: 2595 August 2006
Category: Standards Track
The PLAIN Simple Authentication and Security Layer (SASL) Mechanism
Status of This Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (2006).
Abstract
This document defines a simple clear-text user/password Simple
Authentication and Security Layer (SASL) mechanism called the PLAIN
mechanism. The PLAIN mechanism is intended to be used, in
combination with data confidentiality services provided by a lower
layer, in protocols that lack a simple password authentication
command.
Zeilenga Standards Track [Page 1]
RFC 4616 The PLAIN SASL Mechanism August 2006
1. Introduction
Clear-text, multiple-use passwords are simple, interoperate with
almost all existing operating system authentication databases, and
are useful for a smooth transition to a more secure password-based
authentication mechanism. The drawback is that they are unacceptable
for use over network connections where data confidentiality is not
ensured.
This document defines the PLAIN Simple Authentication and Security
Layer ([SASL]) mechanism for use in protocols with no clear-text
login command (e.g., [ACAP] or [SMTP-AUTH]). This document updates
RFC 2595, replacing Section 6. Changes since RFC 2595 are detailed
in Appendix A.
The name associated with this mechanism is "PLAIN".
The PLAIN SASL mechanism does not provide a security layer.
The PLAIN mechanism should not be used without adequate data security
protection as this mechanism affords no integrity or confidentiality
protections itself. The mechanism is intended to be used with data
security protections provided by application-layer protocol,
generally through its use of Transport Layer Security ([TLS])
services.
By default, implementations SHOULD advertise and make use of the
PLAIN mechanism only when adequate data security services are in
place. Specifications for IETF protocols that indicate that this
mechanism is an applicable authentication mechanism MUST mandate that
implementations support an strong data security service, such as TLS.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [Keywords].
2. PLAIN SASL Mechanism
The mechanism consists of a single message, a string of [UTF-8]
encoded [Unicode] characters, from the client to the server. The
client presents the authorization identity (identity to act as),
followed by a NUL (U+0000) character, followed by the authentication
identity (identity whose password will be used), followed by a NUL
(U+0000) character, followed by the clear-text password. As with
other SASL mechanisms, the client does not provide an authorization
identity when it wishes the server to derive an identity from the
credentials and use that as the authorization identity.
Zeilenga Standards Track [Page 2]
RFC 4616 The PLAIN SASL Mechanism August 2006
The formal grammar for the client message using Augmented BNF [ABNF]
follows.
message = [authzid] UTF8NUL authcid UTF8NUL passwd
authcid = 1*SAFE ; MUST accept up to 255 octets
authzid = 1*SAFE ; MUST accept up to 255 octets
passwd = 1*SAFE ; MUST accept up to 255 octets
UTF8NUL = %x00 ; UTF-8 encoded NUL character
SAFE = UTF1 / UTF2 / UTF3 / UTF4
;; any UTF-8 encoded Unicode character except NUL
UTF1 = %x01-7F ;; except NUL
UTF2 = %xC2-DF UTF0
UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
%xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
%xF4 %x80-8F 2(UTF0)
UTF0 = %x80-BF
The authorization identity (authzid), authentication identity
(authcid), password (passwd), and NUL character deliminators SHALL be
transferred as [UTF-8] encoded strings of [Unicode] characters. As
the NUL (U+0000) character is used as a deliminator, the NUL (U+0000)
character MUST NOT appear in authzid, authcid, or passwd productions.
The form of the authzid production is specific to the application-
level protocol's SASL profile [SASL]. The authcid and passwd
productions are form-free. Use of non-visible characters or
characters that a user may be unable to enter on some keyboards is
discouraged.
Servers MUST be capable of accepting authzid, authcid, and passwd
productions up to and including 255 octets. It is noted that the
UTF-8 encoding of a Unicode character may be as long as 4 octets.
Upon receipt of the message, the server will verify the presented (in
the message) authentication identity (authcid) and password (passwd)
with the system authentication database, and it will verify that the
authentication credentials permit the client to act as the (presented
or derived) authorization identity (authzid). If both steps succeed,
the user is authenticated.
The presented authentication identity and password strings, as well
as the database authentication identity and password strings, are to
be prepared before being used in the verification process. The
[SASLPrep] profile of the [StringPrep] algorithm is the RECOMMENDED
preparation algorithm. The SASLprep preparation algorithm is
Zeilenga Standards Track [Page 3]
RFC 4616 The PLAIN SASL Mechanism August 2006
recommended to improve the likelihood that comparisons behave in an
expected manner. The SASLprep preparation algorithm is not mandatory
so as to allow the server to employ other preparation algorithms
(including none) when appropriate. For instance, use of a different
preparation algorithm may be necessary for the server to interoperate
with an external system.
When preparing the presented strings using [SASLPrep], the presented
strings are to be treated as "query" strings (Section 7 of
[StringPrep]) and hence unassigned code points are allowed to appear
in their prepared output. When preparing the database strings using
[SASLPrep], the database strings are to be treated as "stored"
strings (Section 7 of [StringPrep]) and hence unassigned code points
are prohibited from appearing in their prepared output.
Regardless of the preparation algorithm used, if the output of a
non-invertible function (e.g., hash) of the expected string is
stored, the string MUST be prepared before input to that function.
Regardless of the preparation algorithm used, if preparation fails or
results in an empty string, verification SHALL fail.
When no authorization identity is provided, the server derives an
authorization identity from the prepared representation of the
provided authentication identity string. This ensures that the
derivation of different representations of the authentication
identity produces the same authorization identity.
The server MAY use the credentials to initialize any new
authentication database, such as one suitable for [CRAM-MD5] or
[DIGEST-MD5].
3. Pseudo-Code
This section provides pseudo-code illustrating the verification
process (using hashed passwords and the SASLprep preparation
function) discussed above. This section is not definitive.
boolean Verify(string authzid, string authcid, string passwd) {
string pAuthcid = SASLprep(authcid, true); # prepare authcid
string pPasswd = SASLprep(passwd, true); # prepare passwd
if (pAuthcid == NULL || pPasswd == NULL) {
return false; # preparation failed
}
if (pAuthcid == "" || pPasswd == "") {
return false; # empty prepared string
}
Zeilenga Standards Track [Page 4]
RFC 4616 The PLAIN SASL Mechanism August 2006
storedHash = FetchPasswordHash(pAuthcid);
if (storedHash == NULL || storedHash == "") {
return false; # error or unknown authcid
}
if (!Compare(storedHash, Hash(pPasswd))) {
return false; # incorrect password
}
if (authzid == NULL ) {
authzid = DeriveAuthzid(pAuthcid);
if (authzid == NULL || authzid == "") {
return false; # could not derive authzid
}
}
if (!Authorize(pAuthcid, authzid)) {
return false; # not authorized
}
return true;
}
The second parameter of the SASLprep function, when true, indicates
that unassigned code points are allowed in the input. When the
SASLprep function is called to prepare the password prior to
computing the stored hash, the second parameter would be false.
The second parameter provided to the Authorize function is not
prepared by this code. The application-level SASL profile should be
consulted to determine what, if any, preparation is necessary.
Note that the DeriveAuthzid and Authorize functions (whether
implemented as one function or two, whether designed in a manner in
which these functions or whether the mechanism implementation can be
reused elsewhere) require knowledge and understanding of mechanism
and the application-level protocol specification and/or
implementation details to implement.
Note that the Authorize function outcome is clearly dependent on
details of the local authorization model and policy. Both functions
may be dependent on other factors as well.
Zeilenga Standards Track [Page 5]
RFC 4616 The PLAIN SASL Mechanism August 2006
4. Examples
This section provides examples of PLAIN authentication exchanges.
The examples are intended to help the readers understand the above
text. The examples are not definitive.
"C:" and "S:" indicate lines sent by the client and server,
respectively. "<NUL>" represents a single NUL (U+0000) character.
The Application Configuration Access Protocol ([ACAP]) is used in the
examples.
The first example shows how the PLAIN mechanism might be used for
user authentication.
S: * ACAP (SASL "CRAM-MD5") (STARTTLS)
C: a001 STARTTLS
S: a001 OK "Begin TLS negotiation now"
<TLS negotiation, further commands are under TLS layer>
S: * ACAP (SASL "CRAM-MD5" "PLAIN")
C: a002 AUTHENTICATE "PLAIN"
S: + ""
C: {21}
C: <NUL>tim<NUL>tanstaaftanstaaf
S: a002 OK "Authenticated"
The second example shows how the PLAIN mechanism might be used to
attempt to assume the identity of another user. In this example, the
server rejects the request. Also, this example makes use of the
protocol optional initial response capability to eliminate a round-
trip.
S: * ACAP (SASL "CRAM-MD5") (STARTTLS)
C: a001 STARTTLS
S: a001 OK "Begin TLS negotiation now"
<TLS negotiation, further commands are under TLS layer>
S: * ACAP (SASL "CRAM-MD5" "PLAIN")
C: a002 AUTHENTICATE "PLAIN" {20+}
C: Ursel<NUL>Kurt<NUL>xipj3plmq
S: a002 NO "Not authorized to requested authorization identity"
5. Security Considerations
As the PLAIN mechanism itself provided no integrity or
confidentiality protections, it should not be used without adequate
external data security protection, such as TLS services provided by
many application-layer protocols. By default, implementations SHOULD
NOT advertise and SHOULD NOT make use of the PLAIN mechanism unless
adequate data security services are in place.
Zeilenga Standards Track [Page 6]
RFC 4616 The PLAIN SASL Mechanism August 2006
When the PLAIN mechanism is used, the server gains the ability to
impersonate the user to all services with the same password
regardless of any encryption provided by TLS or other confidentiality
protection mechanisms. Whereas many other authentication mechanisms
have similar weaknesses, stronger SASL mechanisms address this issue.
Clients are encouraged to have an operational mode where all
mechanisms that are likely to reveal the user's password to the
server are disabled.
General [SASL] security considerations apply to this mechanism.
Unicode, [UTF-8], and [StringPrep] security considerations also
apply.
6. IANA Considerations
The SASL Mechanism registry [IANA-SASL] entry for the PLAIN mechanism
has been updated by the IANA to reflect that this document now
provides its technical specification.
To: iana@iana.org
Subject: Updated Registration of SASL mechanism PLAIN
SASL mechanism name: PLAIN
Security considerations: See RFC 4616.
Published specification (optional, recommended): RFC 4616
Person & email address to contact for further information:
Kurt Zeilenga <kurt@openldap.org>
IETF SASL WG <ietf-sasl@imc.org>
Intended usage: COMMON
Author/Change controller: IESG <iesg@ietf.org>
Note: Updates existing entry for PLAIN
7. Acknowledgements
This document is a revision of RFC 2595 by Chris Newman. Portions of
the grammar defined in Section 2 were borrowed from [UTF-8] by
Francois Yergeau.
This document is a product of the IETF Simple Authentication and
Security Layer (SASL) Working Group.
Zeilenga Standards Track [Page 7]
RFC 4616 The PLAIN SASL Mechanism August 2006
8. Normative References
[ABNF] Crocker, D., Ed. and P. Overell, "Augmented BNF for
Syntax Specifications: ABNF", RFC 4234, October 2005.
[Keywords] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[SASL] Melnikov, A., Ed., and K. Zeilenga, Ed., "Simple
Authentication and Security Layer (SASL)", RFC 4422,
June 2006.
[SASLPrep] Zeilenga, K., "SASLprep: Stringprep Profile for User
Names and Passwords", RFC 4013, February 2005.
[StringPrep] Hoffman, P. and M. Blanchet, "Preparation of
Internationalized Strings ("stringprep")", RFC 3454,
December 2002.
[Unicode] The Unicode Consortium, "The Unicode Standard, Version
3.2.0" is defined by "The Unicode Standard, Version
3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
61633-5), as amended by the "Unicode Standard Annex
#27: Unicode 3.1"
(http://www.unicode.org/reports/tr27/) and by the
"Unicode Standard Annex #28: Unicode 3.2"
(http://www.unicode.org/reports/tr28/).
[UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
10646", STD 63, RFC 3629, November 2003.
[TLS] Dierks, T. and E. Rescorla, "The Transport Layer
Security (TLS) Protocol Version 1.1", RFC 4346, April
2006.
9. Informative References
[ACAP] Newman, C. and J. Myers, "ACAP -- Application
Configuration Access Protocol", RFC 2244, November
1997.
[CRAM-MD5] Nerenberg, L., Ed., "The CRAM-MD5 SASL Mechanism", Work
in Progress, June 2006.
[DIGEST-MD5] Melnikov, A., Ed., "Using Digest Authentication as a
SASL Mechanism", Work in Progress, June 2006.
Zeilenga Standards Track [Page 8]
RFC 4616 The PLAIN SASL Mechanism August 2006
[IANA-SASL] IANA, "SIMPLE AUTHENTICATION AND SECURITY LAYER (SASL)
MECHANISMS",
<http://www.iana.org/assignments/sasl-mechanisms>.
[SMTP-AUTH] Myers, J., "SMTP Service Extension for Authentication",
RFC 2554, March 1999.
Zeilenga Standards Track [Page 9]
RFC 4616 The PLAIN SASL Mechanism August 2006
Appendix A. Changes since RFC 2595
This appendix is non-normative.
This document replaces Section 6 of RFC 2595.
The specification details how the server is to compare client-
provided character strings with stored character strings.
The ABNF grammar was updated. In particular, the grammar now allows
LINE FEED (U+000A) and CARRIAGE RETURN (U+000D) characters in the
authzid, authcid, passwd productions. However, whether these control
characters may be used depends on the string preparation rules
applicable to the production. For passwd and authcid productions,
control characters are prohibited. For authzid, one must consult the
application-level SASL profile. This change allows PLAIN to carry
all possible authorization identity strings allowed in SASL.
Pseudo-code was added.
The example section was expanded to illustrate more features of the
PLAIN mechanism.
Editor's Address
Kurt D. Zeilenga
OpenLDAP Foundation
EMail: Kurt@OpenLDAP.org
Zeilenga Standards Track [Page 10]
RFC 4616 The PLAIN SASL Mechanism August 2006
Full Copyright Statement
Copyright (C) The Internet Society (2006).
This document is subject to the rights, licenses and restrictions
contained in BCP 78, and except as set forth therein, the authors
retain all their rights.
This document and the information contained herein are provided on an
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property
The IETF takes no position regarding the validity or scope of any
Intellectual Property Rights or other rights that might be claimed to
pertain to the implementation or use of the technology described in
this document or the extent to which any license under such rights
might or might not be available; nor does it represent that it has
made any independent effort to identify any such rights. Information
on the procedures with respect to rights in RFC documents can be
found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any
assurances of licenses to be made available, or the result of an
attempt made to obtain a general license or permission for the use of
such proprietary rights by implementers or users of this
specification can be obtained from the IETF on-line IPR repository at
http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any
copyrights, patents or patent applications, or other proprietary
rights that may cover technology that may be required to implement
this standard. Please address the information to the IETF at
ietf-ipr@ietf.org.
Acknowledgement
Funding for the RFC Editor function is provided by the IETF
Administrative Support Activity (IASA).
Zeilenga Standards Track [Page 11]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
RFC 1854:
---------
PIPELINING extension
RFC 2222:
---------
SASL
RFC 4505:
---------
ANYNONYMOUS SASL
RFC 4616:
---------
PLAIN SASL
RFC 2487:
---------
STARTTLS extension
RFC 2554 & 4954:
----------------
AUTH extension
RFC 2821:
---------
SMTP protocol
RFC 2822:
---------
General message structure (focusing on important headers)
RFC 2045:
---------
Quoted Printable Encoding
Base 64 Encoding
Detailed message structure
RFC 2046:
---------
Media types (for subparts)
RFC 2047:
---------
Header Encoding
RFC 2183:
---------
The Content-Disposition header
RFC 2231:
---------
Encoded Text header/attribute extensions
RFC 2234:
---------
ABNF definitions
RFC 3676:
---------
Flowed formatting/delsp parameters

View File

@ -0,0 +1,48 @@
General Notes
--------------
* MX is NOT required, but an A record, or CNAME to a MX MUST be present at the least.
* EHLO should be tried, then fall back to HELO
* The 250 return code from RCPT TO is not actually clear-cut. A 251 may be
returned if the message was forwarded to another address. This could be a
useful indicator to end-users that an address should be updated.
* RCPT TO can accpet just "postmaster" without a domain name
* Server MUST not close connection before:
- QUIT and returning 221 response
- Forced requirement, and only after returning a 421 response
- Clients expriencing a forced connection closure, without prior warning should
just treat it like a 451 closure regardless
* ALWAYS use blocking sockets for the initial connection (this should prevent
Exim4's whining about sync).
Response codes
--------------
* From RFC2821, 4.2.
- In particular, the 220, 221, 251, 421, and 551 reply codes
are associated with message text that must be parsed and interpreted
by machines.
Error Codes
------------
* Numeric 5yz = Error
- 550/RCPT TO = Relay denied
- 500 = Unknown command
* Numeric 2yz = Normal
* Numeric 4yz = Temporary failure??
<?php
class EsmtpTransport implements Transport, EsmtpCommandWriter {
}
interface EsmtpCommandWriter {
public function getBuffer();
public function executeCommand($command, $expectedCodes);
}
interface Extension {
public function getKeyword();
public function afterEhlo($commandWriter);
public function getRcptParams();
public function getMailParams();
public function atCommand($commandWriter, $command, $expectedResponse) throws CommandSentException;
}

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.8.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>Swift</name>
<channel>pear.swiftmailer.org</channel>
<summary>Free Feature-rich PHP Mailer.</summary>
<description>
Swift Mailer integrates into any web app written in PHP 5, offering a flexible and elegant object-oriented approach to sending emails with a multitude of features.
</description>
<lead>
<name>Fabien Potencier</name>
<user>fabpot</user>
<email>fabien.potencier@symfony-project.org</email>
<active>yes</active>
</lead>
<lead>
<name>Chris Corbyn</name>
<user>d11wtq</user>
<email></email>
<active>no</active>
</lead>
<date>{{ date }}</date>
<time>{{ time }}</time>
<version>
<release>{{ version }}</release>
<api>{{ api_version }}</api>
</version>
<stability>
<release>{{ stability }}</release>
<api>{{ stability }}</api>
</stability>
<license uri="http://www.opensource.org/licenses/lgpl-3.0.html">LGPL</license>
<notes>-</notes>
<contents>
<dir name="/">
<file name="CHANGES" role="doc" />
<file name="LICENSE" role="doc" />
<file name="README" role="doc" />
<file name="VERSION" role="doc" />
<dir name="lib">
<file install-as="mime_types.php" name="mime_types.php" role="php" />
<file install-as="preferences.php" name="preferences.php" role="php" />
<file install-as="swift_init.php" name="swift_init.php" role="php" />
<file install-as="swift_required.php" name="swift_required_pear.php" role="php" />
<dir name="dependency_maps">
<file install-as="dependency_maps/cache_deps.php" name="cache_deps.php" role="php" />
<file install-as="dependency_maps/mime_deps.php" name="mime_deps.php" role="php" />
<file install-as="dependency_maps/transport_deps.php" name="transport_deps.php" role="php" />
<file install-as="dependency_maps/message_deps.php" name="message_deps.php" role="php" />
</dir>
<dir name="classes">
<file install-as="Swift.php" name="Swift.php" role="php" />
<dir name="Swift">
{{ files }}
</dir>
</dir>
</dir>
</dir>
</contents>
<dependencies>
<required>
<php>
<min>5.2.4</min>
</php>
<pearinstaller>
<min>1.4.0</min>
</pearinstaller>
</required>
</dependencies>
<phprelease />
</package>

View File

@ -0,0 +1,7 @@
Sweety SimpleTest Front-end, Version 0.1 - beta
-----------------------------------------------
17th November 2007, 0.1b:
Finished initial draft, giving up on dealing with invalid XML for now since
$test->dump() should be used for output where needed. Will address this again
later.

View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -0,0 +1,159 @@
Sweety SimpleTest Suite
-----------------------
Sweety is a wrapper around SimpleTest's XML reporting capabilities which
makes unit tests easier to manage and friendlier to run.
Tests are run in a grouped fashion, but each individual test runs in its own
environment and own memory space either via forking new PHP processes, or by
making new HTTP requests.
Sweety works with any vanilla version of SimpleTest since the XmlReporter was
added.
Tests can be run on command line, in an AJAX equipped web browser*, or in a
web browser with javascript turned off.
* Sweety has been tested with success in the following browsers:
- Mozilla Firefox 2.0
- Safari 3-beta
- Internet Explorer 7
- Opera 9
Configuring Sweety:
--------------------
All Sweety configuration is contained inside the config.php file, defined as
constants for the mostpart.
Make sure you at least indicate a path to a directory containing SimpleTest,
and also change the SWEETY_INCLUDE_PATH and SWEETY_TEST_PATH to fit your needs.
Paths are provided using the directory separator for your OS. Use the PHP
constant PATH_SEPARATOR if you need to run in different environments.
If you have test cases in directories /webdev/tests/unit and
/webdev/tests/integration your SWEETY_TEST_PATH should look like:
define('SWEETY_TEST_PATH', '/webdev/tests/unit' . PATH_SEPARATOR .
'/webdev/tests/integration');
If you want to run Sweety on the command line you'll need to specify the path
to your PHP executable (typically /usr/bin/php). Sweety needs to be able to
fork new processes using this executable.
What to do if your naming scheme doesn't use PEAR conventions:
--------------------------------------------------------------
By default Sweety looks for classes using PEAR naming conventions. If you use
some other naming convention you need to tell Sweety how to find your test cases.
This is a two step process:
1) Write a new Sweety_TestLocator -- don't worry, it's easy!
Refer to the interface in lib/Sweety/TestLocator.php for guidance on what
your TestLocator needs to include (just two methods for searching and including).
2) Add it to your config.php.
Once you've written a new TestLocator which works for your naming scheme,
change the config value SWEETY_TEST_LOCATOR to the name of your new class, then
include the class file somewhere inside the config.php. If you use multiple class
naming conventions, list your TestLocators as a comma separated string.
Making tests appear in Sweety's interface:
-------------------------------------------
No really, you just edit the configuration and they'll show up if a TestLocator
can find them ;)
Running sweety on the command line:
-----------------------------------
Interacting with Sweety on the command line you'll get almost as much detail
as you do in a web browser, although the formatting obviously isn't so pretty!
All operations are handled by the file named run.php in the sweety installation
directory.
-bash$ php run.php #runs all tests
-bash$ php run.php Name_Of_TestClass #runs a single test case
-bash$ php run.php Name_Of_TestClass xml #runs a single test case in XML
Runing Sweety with AJAX:
-------------------------
Open up an AJAX equipped web browsers (preferably supporting DOM 3 XPath, but at
least support basic DOM). Navigate to the index.php file at the installation
directory of Sweety. You'll see the screen is divided into two sections, left
and right. On the left there's a list of test cases which you can click to run.
On the right you get all the verbose output from running the tests.
Clicking the "Run Tests" button will run all tests you can currently see in
the list. As each test runs, a request is sent to the web server to get
SimpleTest to run your test case. If the test passes the test case will turn
green, if it fails it will turn red. Tests go yellow until a final conclusion
is drawn.
If you need to stop the tests at any time just click the button again (it
should say "Stop Tests" whilst the tests run).
Whilst the tests run, the large bar to the right of the screen will tally
up aggregated results and will eventually go either green or red indicating
a pass or failure. Failed assertion messages will appear in the page just like
they do with the HtmlReporter of SimpleTest.
Clicking a single test case will run just that test in isolation. If you want
(or need?) to run the test with SimpleTest's HtmlReporter just click the
HTML Icon (little world image) next to the test case. Tests can also be run in
XML if needed.
Above the list of tests there's a filter box which can be directly typed into.
Typing in here narrows down the list of testcases to show only the ones which
match the search query.
Refreshing the page with your browser's refresh button will reset the test suite.
When you change your code, you DO NOT need to refesh your browser window. Just
click the "Run Tests" button, or click the individual test to refresh the results.
If you add a new test case you will need to refresh your browser window however.
Running Sweety without JavaScript:
----------------------------------
If your web browser has JavaScript disabled you can still use the HTML version
of the test suite. Open up the index.php file in your web browser.
You'll see the screen is divided into two sections, left
and right. On the left there's a list of test cases with checkboxes next to them
which you can check to run. On the right you get all the verbose output from
running the tests.
Clicking the "Run Tests" button will run all the currently selected test cases.
This could take a long time depending upon how many tests you have to run,
but once complete you'll see the page again where the tests you selected will
either be red or green indicating a pass or failure. The bar at the right of
the screen will contain aggregate results for all the tests and will be either
red or green to indicate an overall pass or failure.
Assertion messages appear in the page just like with SimpleTest's HtmlReporter.
If you want to run just a single test case, click the "Run" icon at the right
of the test (little running man image).
You can run tests with SimpleTest's HtmlReporter by clicking the HTML icon
(little world image) next to the test case. Tests can be run in XML if needed
too.
Enjoy!

View File

@ -0,0 +1,68 @@
<?php
//Error reporting settings
error_reporting(E_ALL | E_STRICT); ini_set('display_errors', true);
if (defined('E_DEPRECATED'))
{
error_reporting(error_reporting() | E_DEPRECATED);
}
//E_STRICT compliance -- If you change this timezone some tests may fail -
// This only affects the tests, you need to ensure PHP is correctly set up in
// your own code
date_default_timezone_set('Australia/ACT');
//Time limit to process all tests
set_time_limit(30);
//The path to the PHP command line executable (auto-detected if none set)
define('SWEETY_PHP_EXE', '');
//The path to this file
define('SWEETY_HOME', dirname(__FILE__));
//The path to the libs being tested
define('SWEETY_INCLUDE_PATH',
SWEETY_HOME . '/../lib/classes' . PATH_SEPARATOR .
SWEETY_HOME . '/../lib' . PATH_SEPARATOR .
SWEETY_HOME . '/../tests/helpers'
);
//The path to the main test suite
define('SWEETY_LIB_PATH', SWEETY_HOME . '/lib');
//The path to simpletest
define('SWEETY_SIMPLETEST_PATH', SWEETY_LIB_PATH . '/simpletest');
//The path to any testing directories
define('SWEETY_TEST_PATH',
SWEETY_HOME . '/../tests/unit' .
PATH_SEPARATOR . SWEETY_HOME . '/../tests/acceptance' .
PATH_SEPARATOR . SWEETY_HOME . '/../tests/smoke' .
PATH_SEPARATOR . SWEETY_HOME . '/../tests/bug'
);
//Test locator strategies, separated by commas
define('SWEETY_TEST_LOCATOR', 'Sweety_TestLocator_PearStyleLocator');
//A pattern used for filtering out certain class names expected to be tests
define('SWEETY_IGNORED_CLASSES', '/(^|_)Abstract/');
//The name which appears at the top of the test suite
define('SWEETY_SUITE_NAME', 'Swift Mailer 4 Test Suite');
//The path to the template which renders the view
define('SWEETY_UI_TEMPLATE', SWEETY_HOME . '/templates/sweety/suite-ui.tpl.php');
//Most likely you won't want to modify the include_path
set_include_path(
dirname(__FILE__) . '/../lib' . PATH_SEPARATOR .
SWEETY_LIB_PATH . PATH_SEPARATOR .
SWEETY_INCLUDE_PATH . PATH_SEPARATOR .
SWEETY_TEST_PATH
);
//Load in any dependencies
require_once 'Sweety/TestLocator/PearStyleLocator.php';
require_once 'swift_required.php';
//Force init to be required
require_once 'swift_init.php';
//Load in some swift specific testig config
require_once SWEETY_HOME . '/../tests/acceptance.conf.php';
require_once SWEETY_HOME . '/../tests/smoke.conf.php';
require_once SWEETY_HOME . '/lib/yaymock/yay_mock.php';
require_once SWEETY_HOME . '/lib/yaymock/yay_convenience.php';

View File

@ -0,0 +1,40 @@
<?php
require_once dirname(__FILE__) . '/config.php';
require_once SWEETY_SIMPLETEST_PATH . '/unit_tester.php';
require_once SWEETY_SIMPLETEST_PATH . '/mock_objects.php';
require_once SWEETY_SIMPLETEST_PATH . '/reporter.php';
require_once SWEETY_SIMPLETEST_PATH . '/xml.php';
require_once 'Sweety/Runner.php';
require_once 'Sweety/Runner/HtmlRunner.php';
require_once 'Sweety/Reporter/HtmlReporter.php';
$runner = new Sweety_Runner_HtmlRunner(
explode(PATH_SEPARATOR, SWEETY_TEST_PATH),
SWEETY_UI_TEMPLATE,
SWEETY_SUITE_NAME
);
$runner->setReporter(new Sweety_Reporter_HtmlReporter());
$runner->setIgnoredClassRegex(SWEETY_IGNORED_CLASSES);
$locators = preg_split('/\s*,\s*/', SWEETY_TEST_LOCATOR);
foreach ($locators as $locator)
{
$runner->registerTestLocator(new $locator());
}
if (isset($_GET['test']))
{
$testName = $_GET['test'];
$format = isset($_GET['format']) ? $_GET['format'] : Sweety_Runner::REPORT_HTML;
$runner->runTestCase($testName, $format);
}
else
{
$runner->runAllTests();
}

View File

@ -0,0 +1,69 @@
<?php
/**
* Interface for sending output to the client.
* @package Sweety
* @author Chris Corbyn
*/
interface Sweety_Reporter
{
/**
* Get the reporter used to report on this specific test case.
* @param string $testCase
* @return Sweety_Reporter
*/
public function getReporterFor($testCase);
/**
* Returns true if start() has been invoked.
* @return boolean
*/
public function isStarted();
/**
* Start reporting.
*/
public function start();
/**
* Report a skipped test case.
* @param string $message
* @param string $path
*/
public function reportSkip($message, $path);
/**
* Report a passing assertion.
* @param string $message
* @param string $path
*/
public function reportPass($message, $path);
/**
* Report a failing assertion.
* @param string $message
* @param string $path
*/
public function reportFail($message, $path);
/**
* Report an unexpected exception.
* @param string $message
* @param string $path
*/
public function reportException($message, $path);
/**
* Report output from something like a dump().
* @param string $output
* @param string $path
*/
public function reportOutput($output, $path);
/**
* End reporting.
*/
public function finish();
}

View File

@ -0,0 +1,203 @@
<?php
require_once 'Sweety/Reporter.php';
require_once 'Sweety/Reporter/CliTestCaseReporter.php';
/**
* The reporter used in command line reporting.
* @package Sweety
* @author Chris Corbyn
*/
class Sweety_Reporter_CliReporter implements Sweety_Reporter
{
/**
* True if this repoter is running.
* @var boolean
* @access private
*/
private $_started = false;
/**
* The name to show this report as.
* @var string
* @access private
*/
private $_name;
/**
* Aggregate scores from tests run.
* @var int[]
*/
private $_aggregates = array();
/**
* Creates a new CliReporter.
*/
public function __construct($name)
{
$this->_name = $name;
$this->_aggregates = array(
'cases' => 0,
'run' => 0,
'passes' => 0,
'fails' => 0,
'exceptions' => 0
);
}
/**
* Used so test case reporters can notify this reporter when they've completed.
* @param string $testCase
*/
public function notifyEnded($testCase)
{
$this->_aggregates['run']++;
}
/**
* Get the reporter used to report on this specific test case.
* @param string $testCase
* @return Sweety_Reporter
*/
public function getReporterFor($testCase)
{
$this->_aggregates['cases']++;
$reporter = new Sweety_Reporter_CliTestCaseReporter($testCase, $this);
return $reporter;
}
/**
* Returns true if start() has been invoked.
* @return boolean
*/
public function isStarted()
{
return $this->_started;
}
/**
* Start reporting.
*/
public function start()
{
$this->_started = true;
echo $this->_name . PHP_EOL;
}
/**
* Report a skipped test case.
* @param string $message
* @param string $path
*/
public function reportSkip($message, $path)
{
echo " \033[34m\033[1m\033[4mSkip\033[0m:";
$messageLines = explode(PHP_EOL, wordwrap($message, 74, PHP_EOL));
foreach ($messageLines as $line)
{
echo ' ' . $line . PHP_EOL;
}
echo ' in: ' . $path . PHP_EOL;
}
/**
* Report a passing assertion.
* @param string $message
* @param string $path
*/
public function reportPass($message, $path)
{
$this->_aggregates['passes']++;
}
/**
* Report a failing assertion.
* @param string $message
* @param string $path
*/
public function reportFail($message, $path)
{
$this->_aggregates['fails']++;
echo "\033[31m" . $this->_aggregates['fails'] . ') ';
echo $message . "\033[0m" . PHP_EOL;
echo ' in: ' . $path . PHP_EOL;
}
/**
* Report an unexpected exception.
* @param string $message
* @param string $path
*/
public function reportException($message, $path)
{
$this->_aggregates['exceptions']++;
echo "\033[31m\033[1mException" . $this->_aggregates['exceptions'] . "\033[0m!" . PHP_EOL;
echo "\033[1m" . $message . "\033[0m" . PHP_EOL;
echo ' in ' . $path . PHP_EOL;
}
/**
* Report output from something like a dump().
* @param string $output
* @param string $path
*/
public function reportOutput($output, $path)
{
if (preg_match('/^\{image @ (.*?)\}$/D', $output, $matches))
{
echo " \033[33mSmoke Test\033[0m" . PHP_EOL;
echo ' Compare email sent with image @ ' . $matches[1] . PHP_EOL;
}
else
{
echo '--------------------' . PHP_EOL;
echo $output . PHP_EOL;
echo '--------------------' . PHP_EOL;
}
}
/**
* End reporting.
*/
public function finish()
{
$this->_started = false;
$incomplete = $this->_aggregates['cases'] - $this->_aggregates['run'];
if ($incomplete)
{
echo '**********************' . PHP_EOL;
echo $incomplete . ' test case(s) did not complete.' . PHP_EOL .
'This may be because invalid XML was output during the test run' . PHP_EOL .
'and/or because an error occured.' . PHP_EOL .
'Try running the tests separately for more detail.' . PHP_EOL;
echo '**********************' . PHP_EOL;
}
$success = (!$this->_aggregates['fails'] && !$this->_aggregates['exceptions']
&& $this->_aggregates['cases'] == $this->_aggregates['run']);
if ($success)
{
echo "\033[32m\033[1mOK\033[0m" . PHP_EOL;
}
else
{
echo "\033[31m\033[1mFAILURES!!!\033[0m" . PHP_EOL;
}
echo 'Test cases run: ';
echo $this->_aggregates['run'] . '/' . $this->_aggregates['cases'] . ', ';
echo 'Passes: ' . $this->_aggregates['passes'] . ', ';
echo 'Failures: ' . $this->_aggregates['fails'] . ', ';
echo 'Exceptions: '. $this->_aggregates['exceptions'] . PHP_EOL;
exit((int) !$success);
}
}

View File

@ -0,0 +1,160 @@
<?php
require_once 'Sweety/Reporter.php';
require_once 'Sweety/Reporter/CliReporter.php';
/**
* A command line reporter for an individual test case.
* @package Sweety
* @author Chris Corbyn
*/
class Sweety_Reporter_CliTestCaseReporter implements Sweety_Reporter
{
/**
* True if this reporter is running.
* @var boolean
* @access private
*/
private $_started = false;
/**
* The name of the test case being reported on.
* @var string
* @access private
*/
private $_testCase;
/**
* The parent reporter this was spawned from.
* @var Sweety_Reporter_CliReporter
* @access private
*/
private $_parent;
/**
* Aggregate totals for this test case.
* @var int[]
* @access private
*/
private $_aggregates = array();
/**
* Creates a new CliTestCaseReporter.
* @param string $testCase
* @param Sweety_Reporter_CliReporter The parent reporter this was spawned from.
*/
public function __construct($testCase, Sweety_Reporter_CliReporter $parent)
{
$this->_parent = $parent;
$this->_testCase = $testCase;
$this->_aggregates = array(
'passes' => 0,
'fails' => 0,
'exceptions' => 0
);
}
/**
* Get the reporter used to report on this specific test case.
* This method is stubbed only to return itself.
* @param string $testCase
* @return Sweety_Reporter
*/
public function getReporterFor($testCase)
{
return $this;
}
/**
* Returns true if start() has been invoked.
* @return boolean
*/
public function isStarted()
{
return $this->_started;
}
/**
* Start reporting.
*/
public function start()
{
$this->_started = true;
}
/**
* Report a skipped test case.
* @param string $message
* @param string $path
*/
public function reportSkip($message, $path)
{
$this->_parent->reportSkip($message, $path);
}
/**
* Report a passing assertion.
* @param string $message
* @param string $path
*/
public function reportPass($message, $path)
{
$this->_aggregates['passes']++;
$this->_parent->reportPass($message, $path);
}
/**
* Report a failing assertion.
* @param string $message
* @param string $path
*/
public function reportFail($message, $path)
{
$this->_aggregates['fails']++;
$this->_parent->reportFail($message, $path);
}
/**
* Report an unexpected exception.
* @param string $message
* @param string $path
*/
public function reportException($message, $path)
{
$this->_aggregates['exceptions']++;
$this->_parent->reportException($message, $path);
}
/**
* Report output from something like a dump().
* @param string $output
* @param string $path
*/
public function reportOutput($output, $path)
{
$this->_parent->reportOutput($output, $path);
}
/**
* End reporting.
*/
public function finish()
{
$this->_started = false;
if (!$this->_aggregates['fails'] && !$this->_aggregates['exceptions'])
{
echo ' >> ' . $this->_testCase . ' ';
echo "\033[32mOK\033[0m" . PHP_EOL;
}
else
{
echo ' !! ' . $this->_testCase . ' ';
echo "\033[31mFAILED\033[0m" . PHP_EOL;
}
$this->_parent->notifyEnded($this->_testCase);
}
}

View File

@ -0,0 +1,174 @@
<?php
require_once 'Sweety/Reporter.php';
require_once 'Sweety/Reporter/HtmlTestCaseReporter.php';
/**
* The reporter used for the Html (non JS) backend.
* @package Sweety
* @author Chris Corbyn
*/
class Sweety_Reporter_HtmlReporter implements Sweety_Reporter
{
/**
* Template data.
* @var mixed[]
*/
public $_tplVars;
/**
* True if this repoter is running.
* @var boolean
* @access private
*/
private $_started = false;
/**
* Set template vars.
* @param mixed[]
*/
public function setTemplateVars(&$vars)
{
$this->_tplVars =& $vars;
}
/**
* Used so test case reporters can notify this reporter when they've completed.
* @param string $testCase
*/
public function notifyEnded($testCase)
{
$this->_tplVars['runTests'][] = $testCase;
$this->_tplVars['runCount']++;
}
/**
* Get the reporter used to report on this specific test case.
* @param string $testCase
* @return Sweety_Reporter
*/
public function getReporterFor($testCase)
{
$this->_tplVars['caseCount']++;
$reporter = new Sweety_Reporter_HtmlTestCaseReporter($testCase, $this);
$reporter->setTemplateVars($this->_tplVars);
return $reporter;
}
/**
* Returns true if start() has been invoked.
* @return boolean
*/
public function isStarted()
{
return $this->_started;
}
/**
* Start reporting.
*/
public function start()
{
$this->_started = true;
}
/**
* Report a skipped test case.
* @param string $message
* @param string $path
*/
public function reportSkip($message, $path)
{
$this->_tplVars['messages'][] = array(
'type' => 'skip',
'path' => $path,
'text' => $message);
}
/**
* Report a passing assertion.
* @param string $message
* @param string $path
*/
public function reportPass($message, $path)
{
$this->_tplVars['passCount']++;
}
/**
* Report a failing assertion.
* @param string $message
* @param string $path
*/
public function reportFail($message, $path)
{
$this->_tplVars['failCount']++;
$this->_tplVars['messages'][] = array(
'type' => 'fail',
'path' => $path,
'text' => $message);
}
/**
* Report an unexpected exception.
* @param string $message
* @param string $path
*/
public function reportException($message, $path)
{
$this->_tplVars['exceptionCount']++;
$this->_tplVars['messages'][] = array(
'type' => 'exception',
'path' => $path,
'text' => $message);
}
/**
* Report output from something like a dump().
* @param string $output
* @param string $path
*/
public function reportOutput($output, $path)
{
$this->_tplVars['messages'][] = array(
'type' => 'output',
'path' => $path,
'text' => $output);
}
/**
* End reporting.
*/
public function finish()
{
$this->_started = false;
if (!$this->_tplVars['failCount'] && !$this->_tplVars['exceptionCount']
&& $this->_tplVars['caseCount'] == $this->_tplVars['runCount'])
{
$this->_tplVars['result'] = 'pass';
}
else
{
$this->_tplVars['result'] = 'fail';
}
$incomplete = $this->_tplVars['caseCount'] - $this->_tplVars['runCount'];
if (0 < $incomplete)
{
$this->_tplVars['messages'][] = array(
'type' => 'internal',
'path' => '',
'text' => $incomplete . ' test case(s) did not complete.' .
' This may be because invalid XML was output during the test run' .
' and/or because an error occured.' .
' Incomplete test cases are shown in yellow. Click the HTML link ' .
'next to the test for more detail.'
);
}
}
}

View File

@ -0,0 +1,174 @@
<?php
require_once 'Sweety/Reporter.php';
require_once 'Sweety/Reporter/HtmlReporter.php';
/**
* A command line reporter for an individual test case.
* @package Sweety
* @author Chris Corbyn
*/
class Sweety_Reporter_HtmlTestCaseReporter implements Sweety_Reporter
{
/**
* Template variables.
* @var mixed[]
*/
public $_tplVars;
/**
* True if this reporter is running.
* @var boolean
* @access private
*/
private $_started = false;
/**
* The name of the test case being reported on.
* @var string
* @access private
*/
private $_testCase;
/**
* The parent reporter this was spawned from.
* @var Sweety_Reporter_HtmlReporter
* @access private
*/
private $_parent;
/**
* Aggregate totals for this test case.
* @var int[]
* @access private
*/
private $_aggregates = array();
/**
* Creates a new CliTestCaseReporter.
* @param string $testCase
* @param Sweety_Reporter_CliReporter The parent reporter this was spawned from.
*/
public function __construct($testCase, Sweety_Reporter_HtmlReporter $parent)
{
$this->_parent = $parent;
$this->_testCase = $testCase;
$this->_aggregates = array(
'passes' => 0,
'fails' => 0,
'exceptions' => 0
);
}
/**
* Set template data.
* @param mixed[]
*/
public function setTemplateVars(&$vars)
{
$this->_tplVars =& $vars;
}
/**
* Get the reporter used to report on this specific test case.
* This method is stubbed only to return itself.
* @param string $testCase
* @return Sweety_Reporter
*/
public function getReporterFor($testCase)
{
return $this;
}
/**
* Returns true if start() has been invoked.
* @return boolean
*/
public function isStarted()
{
return $this->_started;
}
/**
* Start reporting.
*/
public function start()
{
$this->_started = true;
$this->_tplVars['runTests'][$this->_testCase] = 'running';
}
/**
* Report a skipped test case.
* @param string $message
* @param string $path
*/
public function reportSkip($message, $path)
{
$this->_parent->reportSkip($message, $path);
}
/**
* Report a passing assertion.
* @param string $message
* @param string $path
*/
public function reportPass($message, $path)
{
$this->_aggregates['passes']++;
$this->_parent->reportPass($message, $path);
}
/**
* Report a failing assertion.
* @param string $message
* @param string $path
*/
public function reportFail($message, $path)
{
$this->_aggregates['fails']++;
$this->_parent->reportFail($message, $path);
}
/**
* Report an unexpected exception.
* @param string $message
* @param string $path
*/
public function reportException($message, $path)
{
$this->_aggregates['exceptions']++;
$this->_parent->reportException($message, $path);
}
/**
* Report output from something like a dump().
* @param string $output
* @param string $path
*/
public function reportOutput($output, $path)
{
$this->_parent->reportOutput($output, $path);
}
/**
* End reporting.
*/
public function finish()
{
$this->_started = false;
if (!$this->_aggregates['fails'] && !$this->_aggregates['exceptions'])
{
$this->_tplVars['runTests'][$this->_testCase] = 'pass';
}
else
{
$this->_tplVars['runTests'][$this->_testCase] = 'fail';
}
$this->_parent->notifyEnded($this->_testCase);
}
}

View File

@ -0,0 +1,56 @@
<?php
require_once 'Sweety/TestLocator.php';
require_once 'Sweety/Reporter.php';
/**
* Provides the interface for a remote test runner.
* @author Chris Corbyn
* @package Sweety
*/
interface Sweety_Runner
{
/** Format for reporting in text mode */
const REPORT_TEXT = 'text';
/** Format for reporting in XML mode */
const REPORT_XML = 'xml';
/** Format for reporting in HTML mode */
const REPORT_HTML = 'html';
/**
* Provide a regular expression to filter away some classes.
* @param string $ignoredClassRegex
*/
public function setIgnoredClassRegex($ignoredClassRegex);
/**
* Set the reporter used for showing results/progress.
* @param Sweety_Reporter $reporter
*/
public function setReporter(Sweety_Reporter $reporter);
/**
* Register a new test locator instance.
* @param Sweety_TestLocator $locator
*/
public function registerTestLocator(Sweety_TestLocator $locator);
/**
* Run all tests in the provided directories.
* @param string[] $directories
* @return int
*/
public function runAllTests($dirs = array());
/**
* Run a single test case in isolation using the provided report format.
* @param string $testCase name
* @param string Report format
* @return int
*/
public function runTestCase($testName, $format = self::REPORT_TEXT);
}

View File

@ -0,0 +1,365 @@
<?php
require_once 'Sweety/Runner.php';
require_once 'Sweety/TestLocator.php';
require_once 'Sweety/Reporter.php';
/**
* Base functionality for the Sweety_Runner.
* @package Sweety
* @author Chris Corbyn
*/
abstract class Sweety_Runner_AbstractTestRunner implements Sweety_Runner
{
/**
* The reporter used for showing progress.
* @var Sweety_Reporter
* @access private
*/
private $_reporter;
/**
* TestLocator strategies.
* @var Sweety_TestLocator[]
* @access private
*/
private $_testLocators = array();
/**
* Regular expression for classes which should be ignored.
* @var string
* @access private
*/
private $_ignoredClassRegex = '/^$/D';
/**
* Set the reporter used for showing results.
* @param Sweety_Reporter $reporter
*/
public function setReporter(Sweety_Reporter $reporter)
{
$this->_reporter = $reporter;
}
/**
* Get the reporter used for showing results.
* @return Sweety_Reporter
*/
public function getReporter()
{
return $this->_reporter;
}
/**
* Register a test locator instance.
* @param Sweety_TestLocator $locator
*/
public function registerTestLocator(Sweety_TestLocator $locator)
{
$this->_testLocators[] = $locator;
}
/**
* Set the regular expression used to filter out certain class names.
* @param string $ignoredClassRegex
*/
public function setIgnoredClassRegex($ignoredClassRegex)
{
$this->_ignoredClassRegex = $ignoredClassRegex;
}
/**
* Get the filtering regular expression for ignoring certain classes.
* @return string
*/
public function getIgnoredClassRegex()
{
return $this->_ignoredClassRegex;
}
/**
* Run a single test case with the given name, using the provided output format.
* @param string $testName
* @param string $format (xml, text or html)
* @return int
*/
public function runTestCase($testName, $format = Sweety_Runner::REPORT_TEXT)
{
foreach ($this->_testLocators as $locator)
{
if ($locator->includeTest($testName))
{
break;
}
}
$testClass = new ReflectionClass($testName);
if ($testClass->getConstructor())
{
//We don't want test output to be cached
if (!SimpleReporter::inCli())
{
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
}
switch ($format)
{
case Sweety_Runner::REPORT_HTML:
$reporter = new HtmlReporter();
break;
case Sweety_Runner::REPORT_XML:
if (!SimpleReporter::inCli())
{
header("Content-Type: text/xml"); //Sigh! SimpleTest (skip() issues).
}
$reporter = new XmlReporter();
break;
case Sweety_Runner::REPORT_TEXT:
default:
$reporter = new TextReporter();
break;
}
$test = $testClass->newInstance();
return $test->run($reporter) ? 0 : 1;
}
return 1;
}
/**
* Use strategies to find tests which are runnable.
* @param string[] $dirs
* @return string[]
*/
protected function findTests($dirs = array())
{
$tests = array();
foreach ($this->_testLocators as $locator)
{
$tests += $locator->getTests($dirs);
}
return $tests;
}
/**
* Parse an XML response from a test case an report to the reporter.
* @param string $xml
* @param string $testCase
*/
protected function parseXml($xml, $testCase)
{
$reporter = $this->_reporter->getReporterFor($testCase);
if (!$reporter->isStarted())
{
$reporter->start();
}
$xml = str_replace("\0", '?', trim($xml));
$xml = preg_replace('/[^\x01-\x7F]/e', 'sprintf("&#%d;", ord("$0"));', $xml); //Do something better?
if (!empty($xml))
{
$document = @simplexml_load_string($xml);
if ($document)
{
$this->_parseDocument($document, $testCase, $reporter);
$reporter->finish();
return;
}
}
$reporter->reportException(
'Invalid XML response: ' .
trim(strip_tags(
preg_replace('/^\s*<\?xml.+<\/(?:name|pass|fail|exception)>/s', '', $xml)
)),
$testCase
);
}
/**
* Parse formatted test output.
* @param SimpleXMLElement The node containing the output
* @param string $path to this test method
* @access private
*/
private function _parseFormatted(SimpleXMLElement $formatted, $path = '',
Sweety_Reporter $reporter)
{
$reporter->reportOutput((string)$formatted, $path);
}
/**
* Parse test output.
* @param SimpleXMLElement The node containing the output
* @param string $path to this test method
* @access private
*/
private function _parseMessage(SimpleXMLElement $message, $path = '',
Sweety_Reporter $reporter)
{
$reporter->reportOutput((string)$message, $path);
}
/**
* Parse a test failure.
* @param SimpleXMLElement The node containing the fail
* @param string $path to this test method
* @access private
*/
private function _parseFailure(SimpleXMLElement $failure, $path = '',
Sweety_Reporter $reporter)
{
$reporter->reportFail((string)$failure, $path);
}
/**
* Parse an exception.
* @param SimpleXMLElement The node containing the exception
* @param string $path to this test method
* @access private
*/
private function _parseException(SimpleXMLElement $exception, $path = '',
Sweety_Reporter $reporter)
{
$reporter->reportException((string)$exception, $path);
}
/**
* Parse a pass.
* @param SimpleXMLElement The node containing this pass.
* @param string $path to this test method
* @access private
*/
private function _parsePass(SimpleXMLElement $pass, $path = '',
Sweety_Reporter $reporter)
{
$reporter->reportPass((string)$pass, $path);
}
/**
* Parse a single test case.
* @param SimpleXMLElement The node containing the test case
* @param string $path to this test case
* @access private
*/
private function _parseTestCase(SimpleXMLElement $testCase, $path = '',
Sweety_Reporter $reporter)
{
foreach ($testCase->xpath('./test') as $testMethod)
{
$testMethodName = (string) $this->_firstNodeValue($testMethod->xpath('./name'));
foreach ($testMethod->xpath('./formatted') as $formatted)
{
$this->_parseFormatted(
$formatted, $path . ' -> ' . $testMethodName, $reporter);
}
foreach ($testMethod->xpath('./message') as $message)
{
$this->_parseMessage(
$message, $path . ' -> ' . $testMethodName, $reporter);
}
foreach ($testMethod->xpath('./fail') as $failure)
{
$this->_parseFailure(
$failure, $path . ' -> ' . $testMethodName, $reporter);
}
foreach ($testMethod->xpath('./exception') as $exception)
{
$this->_parseException(
$exception, $path . ' -> ' . $testMethodName, $reporter);
}
foreach ($testMethod->xpath('./pass') as $pass)
{
$this->_parsePass($pass, $path . ' -> ' . $testMethodName, $reporter);
}
$stdout = trim((string) $testMethod);
if ($stdout)
{
$reporter->reportOutput($stdout, $path . ' -> ' . $testMethodName);
}
}
}
/**
* Parse the results of all tests.
* @param SimpleXMLElement The node containing the tests
* @param string $path to the tests
* @access private
*/
private function _parseResults(SimpleXMLElement $document, $path = '',
Sweety_Reporter $reporter)
{
$groups = $document->xpath('./group');
if (!empty($groups))
{
foreach ($groups as $group)
{
$groupName = (string) $this->_firstNodeValue($group->xpath('./name'));
$this->_parseResults($group, $path . ' -> ' . $groupName, $reporter);
}
}
else
{
foreach ($document->xpath('./case') as $testCase)
{
$this->_parseTestCase($testCase, $path, $reporter);
}
}
}
/**
* Parse the entire SimpleTest XML document from a test case.
* @param SimpleXMLElement $document to parse
* @param string $path to the test
* @access private
*/
private function _parseDocument(SimpleXMLElement $document, $path = '',
Sweety_Reporter $reporter)
{
if ($everything = $this->_firstNodeValue($document->xpath('/run')))
{
$this->_parseResults($everything, $path, $reporter);
}
elseif ($skip = $this->_firstNodeValue($document->xpath('/skip')))
{
$reporter->reportSkip((string) $skip, $path);
}
}
protected function _sort($a, $b)
{
$apkg = preg_replace('/_[^_]+$/D', '', $a);
$bpkg = preg_replace('/_[^_]+$/D', '', $b);
if ($apkg == $bpkg)
{
if ($a == $b)
{
return 0;
}
else
{
return ($a > $b) ? 1 : -1;
}
}
else
{
return ($apkg > $bpkg) ? 1 : -1;
}
}
private function _firstNodeValue($nodeSet)
{
$first = array_shift($nodeSet);
return $first;
}
}

View File

@ -0,0 +1,128 @@
<?php
require_once 'Sweety/Runner/AbstractTestRunner.php';
/**
* Runs SimpleTest cases as a group via the command line.
* @package Sweety
* @author Chris Corbyn
*/
class Sweety_Runner_CliRunner extends Sweety_Runner_AbstractTestRunner
{
/**
* Directories to scan for test cases.
* @var string[]
* @access private
*/
private $_dirs = array();
/**
* The command to invoke when running test cases.
* @var string
* @access private
*/
private $_command;
/**
* Creates a new CliRunner scanning the given directories, using the given
* command and having the given name.
* @param string[] $dirs
* @param string $command
* @param string $name
*/
public function __construct(array $dirs, $command)
{
$this->_dirs = $dirs;
$this->_command = $command;
}
/**
* Runs all test cases found under the given directories.
* @param string[] $directories to scan for test cases
* @param string To be prepended to class names
* @return int
*/
public function runAllTests($dirs = array())
{
if (empty($dirs))
{
$dirs = $this->_dirs;
}
$reporter = $this->getReporter();
if (!$reporter->isStarted())
{
$reporter->start();
}
$tests = $this->findTests($dirs);
usort($tests, array($this, '_sort'));
global $argv;
if (!empty($argv[1]))
{
if (substr($argv[1], 0, 1) == '!')
{
$argv[1] = substr($argv[1], 1);
foreach ($tests as $index => $name)
{
if (@preg_match($argv[1] . 'i', $name))
{
unset($tests[$index]);
}
}
}
else
{
foreach ($tests as $index => $name)
{
if (!@preg_match($argv[1] . 'i', $name))
{
unset($tests[$index]);
}
}
}
}
$ret = $this->_runTestList($tests);
$reporter->finish();
return $ret;
}
/**
* Run all possible tests from the given list.
* @param string[] $tests
* @return int
*/
protected function _runTestList(array $tests)
{
foreach ($tests as $testCase)
{
if (preg_match($this->getIgnoredClassRegex(), $testCase))
{
continue;
}
$command = $this->_command;
$command .= ' ' . $testCase;
$command .= ' ' . Sweety_Runner::REPORT_XML;
exec($command, $output, $status);
$xml = implode(PHP_EOL, $output);
$this->parseXml($xml, $testCase);
unset($status);
unset($output);
}
return 0;
}
}

View File

@ -0,0 +1,160 @@
<?php
require_once 'Sweety/Runner/AbstractTestRunner.php';
/**
* Runs SimpleTest cases as a group through a JS enabled browser.
* @package Sweety
* @author Chris Corbyn
*/
class Sweety_Runner_HtmlRunner extends Sweety_Runner_AbstractTestRunner
{
/**
* The path to a valid template file.
* @var string
*/
private $_template;
/**
* The name of the test suite.
* @var string
*/
private $_name;
/**
* Creates a new HtmlRunner scanning the given directories.
* @param string[] $dirs
* @param string $template to load
* @param string $name of the test suite
*/
public function __construct(array $dirs, $template, $name)
{
$this->_dirs = $dirs;
$this->_template = $template;
$this->_name = $name;
}
/**
* Runs all test cases found under the given directories.
* @param string[] $directories to scan for test cases
* @param string To be prepended to class names
* @return int
*/
public function runAllTests($dirs = array())
{
//We don't want test output to be cached
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
if (empty($dirs))
{
$dirs = $this->_dirs;
}
$testCases = $this->findTests($dirs);
foreach ($testCases as $k => $testCase)
{
if (preg_match($this->getIgnoredClassRegex(), $testCase))
{
unset($testCases[$k]);
}
}
usort($testCases, array($this, '_sort'));
$vars = array(
//String
'testCases' => $testCases,
//String
'suiteName' => $this->_name,
// testCase => pass | fail | running
'runTests' => array(),
//Integer
'caseCount' => 0,
//Integer
'runCount' => 0,
//Integer
'passCount' => 0,
//Integer
'failCount' => 0,
//Integer
'exceptionCount' => 0,
// type => pass | fail | exception | output | internal, path => testCase, text => ...
'messages' => array(),
// pass | fail
'result' => 'idle'
);
if (isset($_REQUEST['runtests']))
{
$reporter = $this->getReporter();
$reporter->setTemplateVars($vars);
if (!$reporter->isStarted())
{
$reporter->start();
}
$this->_runTestList((array)$_REQUEST['runtests'], $reporter);
$reporter->finish();
}
else
{
foreach ($testCases as $testCase)
{
$vars['runTests'][$testCase] = 'idle'; //Show all checked by default
}
}
$this->_render($vars);
}
/**
* Run tests in the given array using the REST API (kind of).
* @param string[] $tests
* @param Sweety_Reporter $reporter
* @return int
*/
protected function _runTestList(array $tests, Sweety_Reporter $reporter)
{
$protocol = !empty($_SERVER['HTTPS']) ? 'https://' : 'http://';
//Most likely a HTTP/1.0 server not supporting HOST header
$server = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '127.0.0.1';
$path = '/';
if (!empty($_SERVER['REQUEST_URI']))
{
$path = preg_replace('/\?.*$/sD', '', $_SERVER['REQUEST_URI']);
}
$baseUrl = $protocol . $server . $path;
foreach ($tests as $testCase)
{
$url = $baseUrl . '?test=' . $testCase . '&format=xml';
$xml = file_get_contents($url);
$this->parseXml($xml, $testCase);
}
return 0;
}
/**
* Renders the view for the suite.
* @param string[] $templateVars
*/
protected function _render($vars = array())
{
foreach ($vars as $k => $v)
{
$$k = $v;
}
require_once $this->_template;
}
}

View File

@ -0,0 +1,25 @@
<?php
/**
* Interface for any strategy for finding test cases.
* @package Sweety
* @author Chris Corbyn
*/
interface Sweety_TestLocator
{
/**
* Returns an array of all test cases found under the given directories.
* @param string[] $dirs
* @return string[]
*/
public function getTests($dirs = array());
/**
* Loads the test case of the given name.
* @param string $testCase
* @return boolean
*/
public function includeTest($testCase);
}

View File

@ -0,0 +1,71 @@
<?php
require_once 'Sweety/TestLocator.php';
class Sweety_TestLocator_PearStyleLocator implements Sweety_TestLocator
{
private $_testCache = array();
public function getTests($dirs = array())
{
return $this->_findTestCases($dirs);
}
public function includeTest($testCase)
{
$file = str_replace('_', '/', $testCase) . '.php';
foreach (explode(PATH_SEPARATOR, get_include_path()) as $dir)
{
if (is_file($dir . '/' . $file))
{
require_once $dir . '/' . $file;
return true;
}
}
return false;
}
protected function _findTestCases($dirs = array(), $prepend = '')
{
$ret = array();
foreach ($dirs as $dir)
{
if (array_key_exists($dir, $this->_testCache))
{
$ret += $this->_testCache[$dir];
continue;
}
$this->_testCache[$dir] = array();
$handle = opendir($dir);
while (false !== $file = readdir($handle))
{
if (substr($file, 0, 1) != '.' && is_dir($dir . '/' . $file))
{
foreach ($this->_findTestCases(
array($dir . '/' . $file), $prepend . $file . '_') as $add)
{
$this->_testCache[$dir][] = $add;
$ret[] = $add;
}
}
elseif (substr($file, -4) == '.php')
{
$className = $prepend . basename($file, '.php');
$this->_testCache[$dir][] = $className;
$ret[] = $className;
}
}
closedir($handle);
}
sort($ret);
return $ret;
}
}

View File

@ -0,0 +1,383 @@
Simple Test interface changes
=============================
Because the SimpleTest tool set is still evolving it is likely that tests
written with earlier versions will fail with the newest ones. The most
dramatic changes are in the alpha releases. Here is a list of possible
problems and their fixes...
Fatal error: Call to undefined method Classname::classname()
------------------------------------------------------------
SimpleTest renamed all of its constructors from
Classname to __construct; derived classes invoking
their parent constructors should replace parent::Classname()
with parent::__construct().
Custom CSS in HtmlReporter not being applied
--------------------------------------------
Batch rename of protected and private methods
means that _getCss() was renamed to getCss();
replace your function definition accordingly.
setReturnReference() throws errors in E_STRICT
----------------------------------------------
Happens when an object is passed by reference.
This also happens with setReturnReferenceAt().
If you want to return objects then replace these
with calls to returns() and returnsAt() with the
same arguments. This change was forced in the 1.1
version for E_STRICT compatibility.
assertReference() throws errors in E_STRICT
-------------------------------------------
Due to language restrictions you cannot compare
both variables and objects in E_STRICT mode. Use
assertSame() in this mode with objects. This change
was forced the 1.1 version.
Cannot create GroupTest
-----------------------
The GroupTest has been renamed TestSuite (see below).
It was removed completely in 1.1 in favour of this
name.
No method getRelativeUrls() or getAbsoluteUrls()
------------------------------------------------
These methods were always a bit weird anyway, and
the new parsing of the base tag makes them more so.
They have been replaced with getUrls() instead. If
you want the old functionality then simply chop
off the current domain from getUrl().
Method setWildcard() removed in mocks
-------------------------------------
Even setWildcard() has been removed in 1.0.1beta now.
If you want to test explicitely for a '*' string, then
simply pass in new IdenticalExpectation('*') instead.
No method _getTest() on mocks
-----------------------------
This has finally been removed. It was a pretty esoteric
flex point anyway. It was there to allow the mocks to
work with other test tools, but no one does this.
No method assertError(), assertNoErrors(), swallowErrors()
----------------------------------------------------------
These have been deprecated in 1.0.1beta in favour of
expectError() and expectException(). assertNoErrors() is
redundant if you use expectError() as failures are now reported
immediately.
No method TestCase::signal()
----------------------------
This has been deprecated in favour of triggering an error or
throwing an exception. Deprecated as of 1.0.1beta.
No method TestCase::sendMessage()
---------------------------------
This has been deprecated as of 1.0.1beta.
Failure to connect now emits failures
-------------------------------------
It used to be that you would have to use the
getTransferError() call on the web tester to see if
there was a socket level error in a fetch. This check
is now always carried out by the WebTestCase unless
the fetch is prefaced with WebTestCase::ignoreErrors().
The ignore directive only lasts for test case fetching
action such as get() and click().
No method SimpleTestOptions::ignore()
-------------------------------------
This is deprecated in version 1.0.1beta and has been moved
to SimpleTest::ignore() as that is more readable. In
addition, parent classes are also ignored automatically.
If you are using PHP5 you can skip this directive simply
by marking your test case as abstract.
No method assertCopy()
----------------------
This is deprecated in 1.0.1 in favour of assertClone().
The assertClone() method is slightly different in that
the objects must be identical, but without being a
reference. It is thus not a strict inversion of
assertReference().
Constructor wildcard override has no effect in mocks
----------------------------------------------------
As of 1.0.1beta this is now set with setWildcard() instead
of in the constructor.
No methods setStubBaseClass()/getStubBaseClass()
------------------------------------------------
As mocks are now used instead of stubs, these methods
stopped working and are now removed as of the 1.0.1beta
release. The mock objects may be freely used instead.
No method addPartialMockCode()
------------------------------
The ability to insert arbitrary partial mock code
has been removed. This was a low value feature
causing needless complications. It was removed
in the 1.0.1beta release.
No method setMockBaseClass()
----------------------------
The ability to change the mock base class has been
scheduled for removal and is deprecated since the
1.0.1beta version. This was a rarely used feature
except as a workaround for PHP5 limitations. As
these limitations are being resolved it's hoped
that the bundled mocks can be used directly.
No class Stub
-------------
Server stubs are deprecated from 1.0.1 as the mocks now
have exactly the same interface. Just use mock objects
instead.
No class SimpleTestOptions
--------------------------
This was replced by the shorter SimpleTest in 1.0.1beta1
and is since deprecated.
No file simple_test.php
-----------------------
This was renamed test_case.php in 1.0.1beta to more accurately
reflect it's purpose. This file should never be directly
included in test suites though, as it's part of the
underlying mechanics and has a tendency to be refactored.
No class WantedPatternExpectation
---------------------------------
This was deprecated in 1.0.1alpha in favour of the simpler
name PatternExpectation.
No class NoUnwantedPatternExpectation
-------------------------------------
This was deprecated in 1.0.1alpha in favour of the simpler
name NoPatternExpectation.
No method assertNoUnwantedPattern()
-----------------------------------
This has been renamed to assertNoPattern() in 1.0.1alpha and
the old form is deprecated.
No method assertWantedPattern()
-------------------------------
This has been renamed to assertPattern() in 1.0.1alpha and
the old form is deprecated.
No method assertExpectation()
-----------------------------
This was renamed as assert() in 1.0.1alpha and the old form
has been deprecated.
No class WildcardExpectation
----------------------------
This was a mostly internal class for the mock objects. It was
renamed AnythingExpectation to bring it closer to JMock and
NMock in version 1.0.1alpha.
Missing UnitTestCase::assertErrorPattern()
------------------------------------------
This method is deprecated for version 1.0.1 onwards.
This method has been subsumed by assertError() that can now
take an expectation. Simply pass a PatternExpectation
into assertError() to simulate the old behaviour.
No HTML when matching page elements
-----------------------------------
This behaviour has been switched to using plain text as if it
were seen by the user of the browser. This means that HTML tags
are suppressed, entities are converted and whitespace is
normalised. This should make it easier to match items in forms.
Also images are replaced with their "alt" text so that they
can be matched as well.
No method SimpleRunner::_getTestCase()
--------------------------------------
This was made public as getTestCase() in 1.0RC2.
No method restartSession()
--------------------------
This was renamed to restart() in the WebTestCase, SimpleBrowser
and the underlying SimpleUserAgent in 1.0RC2. Because it was
undocumented anyway, no attempt was made at backward
compatibility.
My custom test case ignored by tally()
--------------------------------------
The _assertTrue method has had it's signature changed due to a bug
in the PHP 5.0.1 release. You must now use getTest() from within
that method to get the test case. Mock compatibility with other
unit testers is now deprecated as of 1.0.1alpha as PEAR::PHPUnit2
should soon have mock support of it's own.
Broken code extending SimpleRunner
----------------------------------
This was replaced with SimpleScorer so that I could use the runner
name in another class. This happened in RC1 development and there
is no easy backward compatibility fix. The solution is simply to
extend SimpleScorer instead.
Missing method getBaseCookieValue()
-----------------------------------
This was renamed getCurrentCookieValue() in RC1.
Missing files from the SimpleTest suite
---------------------------------------
Versions of SimpleTest prior to Beta6 required a SIMPLE_TEST constant
to point at the SimpleTest folder location before any of the toolset
was loaded. This is no longer documented as it is now unnecessary
for later versions. If you are using an earlier version you may
need this constant. Consult the documentation that was bundled with
the release that you are using or upgrade to Beta6 or later.
No method SimpleBrowser::getCurrentUrl()
--------------------------------------
This is replaced with the more versatile showRequest() for
debugging. It only existed in this context for version Beta5.
Later versions will have SimpleBrowser::getHistory() for tracking
paths through pages. It is renamed as getUrl() since 1.0RC1.
No method Stub::setStubBaseClass()
----------------------------------
This method has finally been removed in 1.0RC1. Use
SimpleTestOptions::setStubBaseClass() instead.
No class CommandLineReporter
----------------------------
This was renamed to TextReporter in Beta3 and the deprecated version
was removed in 1.0RC1.
No method requireReturn()
-------------------------
This was deprecated in Beta3 and is now removed.
No method expectCookie()
------------------------
This method was abruptly removed in Beta4 so as to simplify the internals
until another mechanism can replace it. As a workaround it is necessary
to assert that the cookie has changed by setting it before the page
fetch and then assert the desired value.
No method clickSubmitByFormId()
-------------------------------
This method had an incorrect name as no button was involved. It was
renamed to submitByFormId() in Beta4 and the old version deprecated.
Now removed.
No method paintStart() or paintEnd()
------------------------------------
You should only get this error if you have subclassed the lower level
reporting and test runner machinery. These methods have been broken
down into events for test methods, events for test cases and events
for group tests. The new methods are...
paintStart() --> paintMethodStart(), paintCaseStart(), paintGroupStart()
paintEnd() --> paintMethodEnd(), paintCaseEnd(), paintGroupEnd()
This change was made in Beta3, ironically to make it easier to subclass
the inner machinery. Simply duplicating the code you had in the previous
methods should provide a temporary fix.
No class TestDisplay
--------------------
This has been folded into SimpleReporter in Beta3 and is now deprecated.
It was removed in RC1.
No method WebTestCase::fetch()
------------------------------
This was renamed get() in Alpha8. It is removed in Beta3.
No method submit()
------------------
This has been renamed clickSubmit() in Beta1. The old method was
removed in Beta2.
No method clearHistory()
------------------------
This method is deprecated in Beta2 and removed in RC1.
No method getCallCount()
------------------------
This method has been deprecated since Beta1 and has now been
removed. There are now more ways to set expectations on counts
and so this method should be unecessery. Removed in RC1.
Cannot find file *
------------------
The following public name changes have occoured...
simple_html_test.php --> reporter.php
simple_mock.php --> mock_objects.php
simple_unit.php --> unit_tester.php
simple_web.php --> web_tester.php
The old names were deprecated in Alpha8 and removed in Beta1.
No method attachObserver()
--------------------------
Prior to the Alpha8 release the old internal observer pattern was
gutted and replaced with a visitor. This is to trade flexibility of
test case expansion against the ease of writing user interfaces.
Code such as...
$test = &new MyTestCase();
$test->attachObserver(new TestHtmlDisplay());
$test->run();
...should be rewritten as...
$test = &new MyTestCase();
$test->run(new HtmlReporter());
If you previously attached multiple observers then the workaround
is to run the tests twice, once with each, until they can be combined.
For one observer the old method is simulated in Alpha 8, but is
removed in Beta1.
No class TestHtmlDisplay
------------------------
This class has been renamed to HtmlReporter in Alpha8. It is supported,
but deprecated in Beta1 and removed in Beta2. If you have subclassed
the display for your own design, then you will have to extend this
class (HtmlReporter) instead.
If you have accessed the event queue by overriding the notify() method
then I am afraid you are in big trouble :(. The reporter is now
carried around the test suite by the runner classes and the methods
called directly. In the unlikely event that this is a problem and
you don't want to upgrade the test tool then simplest is to write your
own runner class and invoke the tests with...
$test->accept(new MyRunner(new MyReporter()));
...rather than the run method. This should be easier to extend
anyway and gives much more control. Even this method is overhauled
in Beta3 where the runner class can be set within the test case. Really
the best thing to do is to upgrade to this version as whatever you were
trying to achieve before should now be very much easier.
Missing set options method
--------------------------
All test suite options are now in one class called SimpleTestOptions.
This means that options are set differently...
GroupTest::ignore() --> SimpleTestOptions::ignore()
Mock::setMockBaseClass() --> SimpleTestOptions::setMockBaseClass()
These changed in Alpha8 and the old versions are now removed in RC1.
No method setExpected*()
------------------------
The mock expectations changed their names in Alpha4 and the old names
ceased to be supported in Alpha8. The changes are...
setExpectedArguments() --> expectArguments()
setExpectedArgumentsSequence() --> expectArgumentsAt()
setExpectedCallCount() --> expectCallCount()
setMaximumCallCount() --> expectMaximumCallCount()
The parameters remained the same.

View File

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,108 @@
SimpleTest
==========
You probably got this package from...
http://simpletest.sourceforge.net/projects/simpletest/
If there is no licence agreement with this package please download
a version from the location above. You must read and accept that
licence to use this software. The file is titled simply LICENSE.
What is it? It's a framework for unit testing, web site testing and
mock objects for PHP 5.0.5+.
If you have used JUnit, you will find this PHP unit testing version very
similar. Also included is a mock objects and server stubs generator.
The stubs can have return values set for different arguments, can have
sequences set also by arguments and can return items by reference.
The mocks inherit all of this functionality and can also have
expectations set, again in sequences and for different arguments.
A web tester similar in concept to JWebUnit is also included. There is no
JavaScript or tables support, but forms, authentication, cookies and
frames are handled.
You can see a release schedule at http://simpletest.org/en/overview.html
which is also copied to the documentation folder with this release.
A full PHPDocumenter API documentation exists at
http://simpletest.org/api/.
The user interface is minimal
in the extreme, but a lot of information flows from the test suite.
After version 1.0 we will release a better web UI, but we are leaving XUL
and GTk versions to volunteers as everybody has their own opinion
on a good GUI, and we don't want to discourage development by shipping
one with the toolkit. You can download an Eclipse plug-in separately.
You are looking at a second full release. The unit tests for SimpleTest
itself can be run here...
simpletest/test/unit_tests.php
And tests involving live network connections as well are here...
simpletest/test/all_tests.php
The full tests will typically overrun the 8Mb limit often allowed
to a PHP process. A workaround is to run the tests on the command
with a custom php.ini file if you do not have access to your server
version.
You will have to edit the all_tests.php file if you are accesssing
the internet through a proxy server. See the comments in all_tests.php
for instructions.
The full tests read some test data from the LastCraft site. If the site
is down or has been modified for a later version then you will get
spurious errors. A unit_tests.php failure on the other hand would be
very serious. As far as we know we haven't yet managed to check in any
unit test failures, so please correct us if you find one.
Even if all of the tests run please verify that your existing test suites
also function as expected. If they don't see the file...
HELP_MY_TESTS_DONT_WORK_ANYMORE
This contains information on interface changes. It also points out
deprecated interfaces, so you should read this even if all of
your current tests appear to run.
There is a documentation folder which contains the core reference information
in English and French, although this information is fairly basic.
You can find a tutorial on...
http://simpletest.org/en/first_test_tutorial.html
...to get you started and this material will eventually become included
with the project documentation. A French translation exists at...
http://simpletest.org/fr/first_test_tutorial.html
If you download and use, and possibly even extend this tool, please let us
know. Any feedback, even bad, is always welcome and we will work to get
your suggestions into the next release. Ideally please send your
comments to...
simpletest-support@lists.sourceforge.net
...so that others can read them too. We usually try to respond within 48
hours.
There is no change log except at Sourceforge. You can visit the
release notes to see the completed TODO list after each cycle and also the
status of any bugs, but if the bug is recent then it will be fixed in SVN only.
The SVN check-ins always have all the tests passing and so SVN snapshots should
be pretty usable, although the code may not look so good internally.
Oh, yes. It is called "Simple" because it should be simple to
use. We intend to add a complete set of tools for a test first
and "test as you code" type of development. "Simple" does not
mean "Lite" in this context.
Thanks to everyone who has sent comments and offered suggestions. They
really are invaluable, but sadly you are too many to mention in full.
Thanks to all on the advanced PHP forum on SitePoint, especially Harry
Fuecks. Early adopters are always an inspiration.
Marcus Baker, Jason Sweat, Travis Swicegood, Perrick Penet and Edward Z. Yang.
--
marcus@lastcraft.com

View File

@ -0,0 +1,176 @@
<?xml version="1.0"?>
<page title="TODO tasks for the current iteration" here="TODO">
<long_title>TODO tasks for the current iteration</long_title>
<content>
<section name="release-process" title="Release process">
<p>
The following is the approximate plan for the next full point release.
</p>
<p>
Before each release we hope to have the following done.
More may get done, depending on the interest of the volunteers,
but this is the current minimum.
</p>
<p>
The aim of this release cycle is to produce a functionally
identical version to the 1.0.1 release, but bug-fixed and
fully compatible with PHP 5.0.5+ under E_STRICT.
We are also hoping to flush out issues and use cases
caused by people hacking against unpublished flex points
in SimpleTest.
We want to break people's code now, not while we are developing
features down the line.
</p>
<p>
With the website move to a new server, and more developers,
we are able and need to improve the test automation and developer
cooperation.
This release is a deep drawing of breath before going forward.
</p>
</section>
<section name="plan" title="Release plan" version="1.1">
<milestone version="1.1beta">
<concern name="unit-tester">
<bug tracker="1896582">Undefined property $_reporter + fatal error</bug>
</concern>
<concern name="reporter"/>
<concern name="mock-objects"/>
<concern name="parser"/>
<concern name="browser"/>
<concern name="web-tester"/>
<concern name="documentation">
<task>
The HELP_MY_TESTS_DONT_WORK_ANYMORE needs to be updated.
</task>
<task>README needs to be updated.</task>
<task status="done">
Write XSLT code for this file so Perrick doesn't strangle me
</task>
</concern>
<concern name="extensions">
<task>Ensure extensions are compatible with PHP5 constructor renaming in the current trunk.</task>
</concern>
<concern name="build">
<task>Update PEAR package task to be compatible with latest PEAR installer.</task>
<task status="done">PHP 5.3 compatible under E_STRICT</task>
<task>PHP 5.2.0-5 compatible under E_STRICT</task>
<task>PHP 5.1.0-6 compatible under E_STRICT</task>
<bug tracker="1884013">continuous integration</bug>
<bug tracker="1853765">error_reporting(E_ALL|E_STRICT)gives lots of warning</bug>
<task>Remove all deprecated methods</task>
<task status="done">
Drop underscores from protected methods and
private variables.
Make all variables private and add protected
accessors where we use them internally.
<note>
That way people will start complaining.
Upon each complaint we'll add an accessor and
capture the use case from them.
</note>
<note>We'll stick the use cases in the feature request tracker for now</note>
</task>
<task status="done">Move web site to new server</task>
</concern>
</milestone>
<milestone version="1.1beta2">
<concern name="unit-tester"/>
<concern name="reporter">
<task>
Deprecate all mentions of GroupTest without breaking
existing code.
<note>
Need to swap the terminology for TestSuite
in method names, etc.
</note>
</task>
<bug tracker="1864974">XmlReporter generating invalid XML</bug>
</concern>
<concern name="mock-objects">
<task>Remove reflection facade for PHP4</task>
</concern>
<concern name="parser"/>
<concern name="browser">
<bug tracker="1913229">label not assigned to radio and checkbox</bug>
<bug tracker="1706283">incorrect proxy requests</bug>
</concern>
<concern name="web-tester"/>
<concern name="documentation">
<task>Docblocks need to be cut back to a minimum</task>
</concern>
<concern name="extensions"/>
<concern name="build">
<task>PHP 5.0.5 compatible under E_STRICT</task>
<task>Move acceptance tests sample pages to new server</task>
</concern>
</milestone>
<milestone version="1.1RC1">
<concern name="unit-tester"/>
<concern name="reporter"/>
<concern name="mock-objects">
<task>Remove reflection facade for PHP4</task>
</concern>
<concern name="parser"/>
<concern name="browser"/>
<concern name="web-tester"/>
<concern name="documentation">
<task>Throw away old tutorial</task>
</concern>
<concern name="extensions"/>
<concern name="build">
<task>PHP 6 compatible under E_STRICT</task>
<task>
Automated nightly test script that runs tests on all
targeted PHP versions.
</task>
</concern>
</milestone>
</section>
</content>
<internal>
<link>
<a href="#plan">Current iteration</a> is 1.1beta.
</link>
<link>
Upcoming tasks for
<a href="#unit-tester">Unit tester</a>,
<a href="#reporter">Reporter</a>,
<a href="#mock-objects">Mock objects</a>,
<a href="#parser">Parser</a>,
<a href="#browser">Browser</a>,
<a href="#web-tester">Web tester</a>,
<a href="#documentation">Documentation</a>,
<a href="#extensions">Extensions</a> and
<a href="#build">Build</a>.
</link>
</internal>
<external>
<link>
Trackers for :
<a href="https://sourceforge.net/tracker/?group_id=76550&amp;atid=547458">feature requests</a>,
<a href="https://sourceforge.net/tracker/?group_id=76550&amp;atid=547455">bugs</a> and
<a href="https://sourceforge.net/tracker/?group_id=76550&amp;atid=547457">patches</a>.
</link>
</external>
<meta>
<keywords>
software development,
computer programmer,
php programming,
programming php,
software development company,
software development uk,
php tutorial,
bespoke software development uk,
corporate web development,
architecture,
freelancer,
php resources,
wordtracker,
web marketing,
serach engines,
web positioning,
internet marketing
</keywords>
</meta>
</page>

View File

@ -0,0 +1 @@
1.1beta

View File

@ -0,0 +1,237 @@
<?php
/**
* Base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: authentication.php 1784 2008-04-26 13:07:14Z pp11 $
*/
/**
* include http class
*/
require_once(dirname(__FILE__) . '/http.php');
/**
* Represents a single security realm's identity.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleRealm {
private $type;
private $root;
private $username;
private $password;
/**
* Starts with the initial entry directory.
* @param string $type Authentication type for this
* realm. Only Basic authentication
* is currently supported.
* @param SimpleUrl $url Somewhere in realm.
* @access public
*/
function SimpleRealm($type, $url) {
$this->type = $type;
$this->root = $url->getBasePath();
$this->username = false;
$this->password = false;
}
/**
* Adds another location to the realm.
* @param SimpleUrl $url Somewhere in realm.
* @access public
*/
function stretch($url) {
$this->root = $this->getCommonPath($this->root, $url->getPath());
}
/**
* Finds the common starting path.
* @param string $first Path to compare.
* @param string $second Path to compare.
* @return string Common directories.
* @access private
*/
protected function getCommonPath($first, $second) {
$first = explode('/', $first);
$second = explode('/', $second);
for ($i = 0; $i < min(count($first), count($second)); $i++) {
if ($first[$i] != $second[$i]) {
return implode('/', array_slice($first, 0, $i)) . '/';
}
}
return implode('/', $first) . '/';
}
/**
* Sets the identity to try within this realm.
* @param string $username Username in authentication dialog.
* @param string $username Password in authentication dialog.
* @access public
*/
function setIdentity($username, $password) {
$this->username = $username;
$this->password = $password;
}
/**
* Accessor for current identity.
* @return string Last succesful username.
* @access public
*/
function getUsername() {
return $this->username;
}
/**
* Accessor for current identity.
* @return string Last succesful password.
* @access public
*/
function getPassword() {
return $this->password;
}
/**
* Test to see if the URL is within the directory
* tree of the realm.
* @param SimpleUrl $url URL to test.
* @return boolean True if subpath.
* @access public
*/
function isWithin($url) {
if ($this->isIn($this->root, $url->getBasePath())) {
return true;
}
if ($this->isIn($this->root, $url->getBasePath() . $url->getPage() . '/')) {
return true;
}
return false;
}
/**
* Tests to see if one string is a substring of
* another.
* @param string $part Small bit.
* @param string $whole Big bit.
* @return boolean True if the small bit is
* in the big bit.
* @access private
*/
protected function isIn($part, $whole) {
return strpos($whole, $part) === 0;
}
}
/**
* Manages security realms.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleAuthenticator {
private $realms;
/**
* Clears the realms.
* @access public
*/
function SimpleAuthenticator() {
$this->restartSession();
}
/**
* Starts with no realms set up.
* @access public
*/
function restartSession() {
$this->realms = array();
}
/**
* Adds a new realm centered the current URL.
* Browsers privatey wildly on their behaviour in this
* regard. Mozilla ignores the realm and presents
* only when challenged, wasting bandwidth. IE
* just carries on presenting until a new challenge
* occours. SimpleTest tries to follow the spirit of
* the original standards committee and treats the
* base URL as the root of a file tree shaped realm.
* @param SimpleUrl $url Base of realm.
* @param string $type Authentication type for this
* realm. Only Basic authentication
* is currently supported.
* @param string $realm Name of realm.
* @access public
*/
function addRealm($url, $type, $realm) {
$this->realms[$url->getHost()][$realm] = new SimpleRealm($type, $url);
}
/**
* Sets the current identity to be presented
* against that realm.
* @param string $host Server hosting realm.
* @param string $realm Name of realm.
* @param string $username Username for realm.
* @param string $password Password for realm.
* @access public
*/
function setIdentityForRealm($host, $realm, $username, $password) {
if (isset($this->realms[$host][$realm])) {
$this->realms[$host][$realm]->setIdentity($username, $password);
}
}
/**
* Finds the name of the realm by comparing URLs.
* @param SimpleUrl $url URL to test.
* @return SimpleRealm Name of realm.
* @access private
*/
protected function findRealmFromUrl($url) {
if (! isset($this->realms[$url->getHost()])) {
return false;
}
foreach ($this->realms[$url->getHost()] as $name => $realm) {
if ($realm->isWithin($url)) {
return $realm;
}
}
return false;
}
/**
* Presents the appropriate headers for this location.
* @param SimpleHttpRequest $request Request to modify.
* @param SimpleUrl $url Base of realm.
* @access public
*/
function addHeaders(&$request, $url) {
if ($url->getUsername() && $url->getPassword()) {
$username = $url->getUsername();
$password = $url->getPassword();
} elseif ($realm = $this->findRealmFromUrl($url)) {
$username = $realm->getUsername();
$password = $realm->getPassword();
} else {
return;
}
$this->addBasicHeaders($request, $username, $password);
}
/**
* Presents the appropriate headers for this
* location for basic authentication.
* @param SimpleHttpRequest $request Request to modify.
* @param string $username Username for realm.
* @param string $password Password for realm.
* @access public
*/
static function addBasicHeaders(&$request, $username, $password) {
if ($username && $password) {
$request->addHeaderLine(
'Authorization: Basic ' . base64_encode("$username:$password"));
}
}
}
?>

View File

@ -0,0 +1,97 @@
<?php
/**
* Autorunner which runs all tests cases found in a file
* that includes this module.
* @package SimpleTest
* @version $Id: autorun.php 1809 2008-09-12 00:46:55Z lastcraft $
*/
require_once dirname(__FILE__) . '/unit_tester.php';
require_once dirname(__FILE__) . '/mock_objects.php';
require_once dirname(__FILE__) . '/collector.php';
require_once dirname(__FILE__) . '/default_reporter.php';
$GLOBALS['SIMPLETEST_AUTORUNNER_INITIAL_CLASSES'] = get_declared_classes();
register_shutdown_function('simpletest_autorun');
/**
* Exit handler to run all recent test cases if no test has
* so far been run. Uses the DefaultReporter which can have
* it's output controlled with SimpleTest::prefer().
*/
function simpletest_autorun() {
try {
if (tests_have_run()) {
return;
}
$candidates = array_intersect(
capture_new_classes(),
classes_defined_in_initial_file());
$loader = new SimpleFileLoader();
$suite = $loader->createSuiteFromClasses(
basename(initial_file()),
$loader->selectRunnableTests($candidates));
$result = $suite->run(new DefaultReporter());
} catch (Exception $stack_frame_fix) {
print $stack_frame_fix->getMessage();
$result = false;
}
if (SimpleReporter::inCli()) {
exit($result ? 0 : 1);
}
}
/**
* Checks the current test context to see if a test has
* ever been run.
* @return boolean True if tests have run.
*/
function tests_have_run() {
if ($context = SimpleTest::getContext()) {
return (boolean)$context->getTest();
}
return false;
}
/**
* The first autorun file.
* @return string Filename of first autorun script.
*/
function initial_file() {
static $file = false;
if (! $file) {
if (isset($_SERVER, $_SERVER['SCRIPT_FILENAME'])) {
$file = $_SERVER['SCRIPT_FILENAME'];
} else {
$included_files = get_included_files();
$file = reset($included_files);
}
}
return $file;
}
/**
* Just the classes from the first autorun script. May
* get a few false positives, as it just does a regex based
* on following the word "class".
* @return array List of all possible classes in first
* autorun script.
*/
function classes_defined_in_initial_file() {
if (preg_match_all('/\bclass\s+(\w+)/i', file_get_contents(initial_file()), $matches)) {
return array_map('strtolower', $matches[1]);
}
return array();
}
/**
* Every class since the first autorun include. This
* is safe enough if require_once() is alwyas used.
* @return array Class names.
*/
function capture_new_classes() {
global $SIMPLETEST_AUTORUNNER_INITIAL_CLASSES;
return array_map('strtolower', array_diff(get_declared_classes(),
$SIMPLETEST_AUTORUNNER_INITIAL_CLASSES ?
$SIMPLETEST_AUTORUNNER_INITIAL_CLASSES : array()));
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
<?php
/**
* This file contains the following classes: {@link SimpleCollector},
* {@link SimplePatternCollector}.
*
* @author Travis Swicegood <development@domain51.com>
* @package SimpleTest
* @subpackage UnitTester
* @version $Id: collector.php 1784 2008-04-26 13:07:14Z pp11 $
*/
/**
* The basic collector for {@link GroupTest}
*
* @see collect(), GroupTest::collect()
* @package SimpleTest
* @subpackage UnitTester
*/
class SimpleCollector {
/**
* Strips off any kind of slash at the end so as to normalise the path.
* @param string $path Path to normalise.
* @return string Path without trailing slash.
*/
protected function removeTrailingSlash($path) {
if (substr($path, -1) == DIRECTORY_SEPARATOR) {
return substr($path, 0, -1);
} elseif (substr($path, -1) == '/') {
return substr($path, 0, -1);
} else {
return $path;
}
}
/**
* Scans the directory and adds what it can.
* @param object $test Group test with {@link GroupTest::addTestFile()} method.
* @param string $path Directory to scan.
* @see _attemptToAdd()
*/
function collect(&$test, $path) {
$path = $this->removeTrailingSlash($path);
if ($handle = opendir($path)) {
while (($entry = readdir($handle)) !== false) {
if ($this->isHidden($entry)) {
continue;
}
$this->handle($test, $path . DIRECTORY_SEPARATOR . $entry);
}
closedir($handle);
}
}
/**
* This method determines what should be done with a given file and adds
* it via {@link GroupTest::addTestFile()} if necessary.
*
* This method should be overriden to provide custom matching criteria,
* such as pattern matching, recursive matching, etc. For an example, see
* {@link SimplePatternCollector::_handle()}.
*
* @param object $test Group test with {@link GroupTest::addTestFile()} method.
* @param string $filename A filename as generated by {@link collect()}
* @see collect()
* @access protected
*/
protected function handle(&$test, $file) {
if (is_dir($file)) {
return;
}
$test->addFile($file);
}
/**
* Tests for hidden files so as to skip them. Currently
* only tests for Unix hidden files.
* @param string $filename Plain filename.
* @return boolean True if hidden file.
* @access private
*/
protected function isHidden($filename) {
return strncmp($filename, '.', 1) == 0;
}
}
/**
* An extension to {@link SimpleCollector} that only adds files matching a
* given pattern.
*
* @package SimpleTest
* @subpackage UnitTester
* @see SimpleCollector
*/
class SimplePatternCollector extends SimpleCollector {
private $pattern;
/**
*
* @param string $pattern Perl compatible regex to test name against
* See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE}
* for full documentation of valid pattern.s
*/
function __construct($pattern = '/php$/i') {
$this->pattern = $pattern;
}
/**
* Attempts to add files that match a given pattern.
*
* @see SimpleCollector::_handle()
* @param object $test Group test with {@link GroupTest::addTestFile()} method.
* @param string $path Directory to scan.
* @access protected
*/
protected function handle(&$test, $filename) {
if (preg_match($this->pattern, $filename)) {
parent::handle($test, $filename);
}
}
}
?>

View File

@ -0,0 +1,166 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @version $Id: compatibility.php 1747 2008-04-13 18:26:47Z pp11 $
*/
/**
* Static methods for compatibility between different
* PHP versions.
* @package SimpleTest
*/
class SimpleTestCompatibility {
/**
* Creates a copy whether in PHP5 or PHP4.
* @param object $object Thing to copy.
* @return object A copy.
* @access public
*/
static function copy($object) {
if (version_compare(phpversion(), '5') >= 0) {
eval('$copy = clone $object;');
return $copy;
}
return $object;
}
/**
* Identity test. Drops back to equality + types for PHP5
* objects as the === operator counts as the
* stronger reference constraint.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if identical.
* @access public
*/
static function isIdentical($first, $second) {
if (version_compare(phpversion(), '5') >= 0) {
return SimpleTestCompatibility::isIdenticalType($first, $second);
}
if ($first != $second) {
return false;
}
return ($first === $second);
}
/**
* Recursive type test.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if same type.
* @access private
*/
protected static function isIdenticalType($first, $second) {
if (gettype($first) != gettype($second)) {
return false;
}
if (is_object($first) && is_object($second)) {
if (get_class($first) != get_class($second)) {
return false;
}
return SimpleTestCompatibility::isArrayOfIdenticalTypes(
get_object_vars($first),
get_object_vars($second));
}
if (is_array($first) && is_array($second)) {
return SimpleTestCompatibility::isArrayOfIdenticalTypes($first, $second);
}
if ($first !== $second) {
return false;
}
return true;
}
/**
* Recursive type test for each element of an array.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if identical.
* @access private
*/
protected static function isArrayOfIdenticalTypes($first, $second) {
if (array_keys($first) != array_keys($second)) {
return false;
}
foreach (array_keys($first) as $key) {
$is_identical = SimpleTestCompatibility::isIdenticalType(
$first[$key],
$second[$key]);
if (! $is_identical) {
return false;
}
}
return true;
}
/**
* Test for two variables being aliases.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if same.
* @access public
*/
static function isReference(&$first, &$second) {
if (version_compare(phpversion(), '5', '>=') && is_object($first)) {
return ($first === $second);
}
if (is_object($first) && is_object($second)) {
$id = uniqid("test");
$first->$id = true;
$is_ref = isset($second->$id);
unset($first->$id);
return $is_ref;
}
$temp = $first;
$first = uniqid("test");
$is_ref = ($first === $second);
$first = $temp;
return $is_ref;
}
/**
* Test to see if an object is a member of a
* class hiearchy.
* @param object $object Object to test.
* @param string $class Root name of hiearchy.
* @return boolean True if class in hiearchy.
* @access public
*/
static function isA($object, $class) {
if (version_compare(phpversion(), '5') >= 0) {
if (! class_exists($class, false)) {
if (function_exists('interface_exists')) {
if (! interface_exists($class, false)) {
return false;
}
}
}
eval("\$is_a = \$object instanceof $class;");
return $is_a;
}
if (function_exists('is_a')) {
return is_a($object, $class);
}
return ((strtolower($class) == get_class($object))
or (is_subclass_of($object, $class)));
}
/**
* Sets a socket timeout for each chunk.
* @param resource $handle Socket handle.
* @param integer $timeout Limit in seconds.
* @access public
*/
static function setTimeout($handle, $timeout) {
if (function_exists('stream_set_timeout')) {
stream_set_timeout($handle, $timeout, 0);
} elseif (function_exists('socket_set_timeout')) {
socket_set_timeout($handle, $timeout, 0);
} elseif (function_exists('set_socket_timeout')) {
set_socket_timeout($handle, $timeout, 0);
}
}
}
?>

View File

@ -0,0 +1,380 @@
<?php
/**
* Base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: cookies.php 1784 2008-04-26 13:07:14Z pp11 $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/url.php');
/**#@-*/
/**
* Cookie data holder. Cookie rules are full of pretty
* arbitary stuff. I have used...
* http://wp.netscape.com/newsref/std/cookie_spec.html
* http://www.cookiecentral.com/faq/
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleCookie {
private $host;
private $name;
private $value;
private $path;
private $expiry;
private $is_secure;
/**
* Constructor. Sets the stored values.
* @param string $name Cookie key.
* @param string $value Value of cookie.
* @param string $path Cookie path if not host wide.
* @param string $expiry Expiry date as string.
* @param boolean $is_secure Currently ignored.
*/
function __construct($name, $value = false, $path = false, $expiry = false, $is_secure = false) {
$this->host = false;
$this->name = $name;
$this->value = $value;
$this->path = ($path ? $this->fixPath($path) : "/");
$this->expiry = false;
if (is_string($expiry)) {
$this->expiry = strtotime($expiry);
} elseif (is_integer($expiry)) {
$this->expiry = $expiry;
}
$this->is_secure = $is_secure;
}
/**
* Sets the host. The cookie rules determine
* that the first two parts are taken for
* certain TLDs and three for others. If the
* new host does not match these rules then the
* call will fail.
* @param string $host New hostname.
* @return boolean True if hostname is valid.
* @access public
*/
function setHost($host) {
if ($host = $this->truncateHost($host)) {
$this->host = $host;
return true;
}
return false;
}
/**
* Accessor for the truncated host to which this
* cookie applies.
* @return string Truncated hostname.
* @access public
*/
function getHost() {
return $this->host;
}
/**
* Test for a cookie being valid for a host name.
* @param string $host Host to test against.
* @return boolean True if the cookie would be valid
* here.
*/
function isValidHost($host) {
return ($this->truncateHost($host) === $this->getHost());
}
/**
* Extracts just the domain part that determines a
* cookie's host validity.
* @param string $host Host name to truncate.
* @return string Domain or false on a bad host.
* @access private
*/
protected function truncateHost($host) {
$tlds = SimpleUrl::getAllTopLevelDomains();
if (preg_match('/[a-z\-]+\.(' . $tlds . ')$/i', $host, $matches)) {
return $matches[0];
} elseif (preg_match('/[a-z\-]+\.[a-z\-]+\.[a-z\-]+$/i', $host, $matches)) {
return $matches[0];
}
return false;
}
/**
* Accessor for name.
* @return string Cookie key.
* @access public
*/
function getName() {
return $this->name;
}
/**
* Accessor for value. A deleted cookie will
* have an empty string for this.
* @return string Cookie value.
* @access public
*/
function getValue() {
return $this->value;
}
/**
* Accessor for path.
* @return string Valid cookie path.
* @access public
*/
function getPath() {
return $this->path;
}
/**
* Tests a path to see if the cookie applies
* there. The test path must be longer or
* equal to the cookie path.
* @param string $path Path to test against.
* @return boolean True if cookie valid here.
* @access public
*/
function isValidPath($path) {
return (strncmp(
$this->fixPath($path),
$this->getPath(),
strlen($this->getPath())) == 0);
}
/**
* Accessor for expiry.
* @return string Expiry string.
* @access public
*/
function getExpiry() {
if (! $this->expiry) {
return false;
}
return gmdate("D, d M Y H:i:s", $this->expiry) . " GMT";
}
/**
* Test to see if cookie is expired against
* the cookie format time or timestamp.
* Will give true for a session cookie.
* @param integer/string $now Time to test against. Result
* will be false if this time
* is later than the cookie expiry.
* Can be either a timestamp integer
* or a cookie format date.
* @access public
*/
function isExpired($now) {
if (! $this->expiry) {
return true;
}
if (is_string($now)) {
$now = strtotime($now);
}
return ($this->expiry < $now);
}
/**
* Ages the cookie by the specified number of
* seconds.
* @param integer $interval In seconds.
* @public
*/
function agePrematurely($interval) {
if ($this->expiry) {
$this->expiry -= $interval;
}
}
/**
* Accessor for the secure flag.
* @return boolean True if cookie needs SSL.
* @access public
*/
function isSecure() {
return $this->is_secure;
}
/**
* Adds a trailing and leading slash to the path
* if missing.
* @param string $path Path to fix.
* @access private
*/
protected function fixPath($path) {
if (substr($path, 0, 1) != '/') {
$path = '/' . $path;
}
if (substr($path, -1, 1) != '/') {
$path .= '/';
}
return $path;
}
}
/**
* Repository for cookies. This stuff is a
* tiny bit browser dependent.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleCookieJar {
private $cookies;
/**
* Constructor. Jar starts empty.
* @access public
*/
function __construct() {
$this->cookies = array();
}
/**
* Removes expired and temporary cookies as if
* the browser was closed and re-opened.
* @param string/integer $now Time to test expiry against.
* @access public
*/
function restartSession($date = false) {
$surviving_cookies = array();
for ($i = 0; $i < count($this->cookies); $i++) {
if (! $this->cookies[$i]->getValue()) {
continue;
}
if (! $this->cookies[$i]->getExpiry()) {
continue;
}
if ($date && $this->cookies[$i]->isExpired($date)) {
continue;
}
$surviving_cookies[] = $this->cookies[$i];
}
$this->cookies = $surviving_cookies;
}
/**
* Ages all cookies in the cookie jar.
* @param integer $interval The old session is moved
* into the past by this number
* of seconds. Cookies now over
* age will be removed.
* @access public
*/
function agePrematurely($interval) {
for ($i = 0; $i < count($this->cookies); $i++) {
$this->cookies[$i]->agePrematurely($interval);
}
}
/**
* Sets an additional cookie. If a cookie has
* the same name and path it is replaced.
* @param string $name Cookie key.
* @param string $value Value of cookie.
* @param string $host Host upon which the cookie is valid.
* @param string $path Cookie path if not host wide.
* @param string $expiry Expiry date.
* @access public
*/
function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
$cookie = new SimpleCookie($name, $value, $path, $expiry);
if ($host) {
$cookie->setHost($host);
}
$this->cookies[$this->findFirstMatch($cookie)] = $cookie;
}
/**
* Finds a matching cookie to write over or the
* first empty slot if none.
* @param SimpleCookie $cookie Cookie to write into jar.
* @return integer Available slot.
* @access private
*/
protected function findFirstMatch($cookie) {
for ($i = 0; $i < count($this->cookies); $i++) {
$is_match = $this->isMatch(
$cookie,
$this->cookies[$i]->getHost(),
$this->cookies[$i]->getPath(),
$this->cookies[$i]->getName());
if ($is_match) {
return $i;
}
}
return count($this->cookies);
}
/**
* Reads the most specific cookie value from the
* browser cookies. Looks for the longest path that
* matches.
* @param string $host Host to search.
* @param string $path Applicable path.
* @param string $name Name of cookie to read.
* @return string False if not present, else the
* value as a string.
* @access public
*/
function getCookieValue($host, $path, $name) {
$longest_path = '';
foreach ($this->cookies as $cookie) {
if ($this->isMatch($cookie, $host, $path, $name)) {
if (strlen($cookie->getPath()) > strlen($longest_path)) {
$value = $cookie->getValue();
$longest_path = $cookie->getPath();
}
}
}
return (isset($value) ? $value : false);
}
/**
* Tests cookie for matching against search
* criteria.
* @param SimpleTest $cookie Cookie to test.
* @param string $host Host must match.
* @param string $path Cookie path must be shorter than
* this path.
* @param string $name Name must match.
* @return boolean True if matched.
* @access private
*/
protected function isMatch($cookie, $host, $path, $name) {
if ($cookie->getName() != $name) {
return false;
}
if ($host && $cookie->getHost() && ! $cookie->isValidHost($host)) {
return false;
}
if (! $cookie->isValidPath($path)) {
return false;
}
return true;
}
/**
* Uses a URL to sift relevant cookies by host and
* path. Results are list of strings of form "name=value".
* @param SimpleUrl $url Url to select by.
* @return array Valid name and value pairs.
* @access public
*/
function selectAsPairs($url) {
$pairs = array();
foreach ($this->cookies as $cookie) {
if ($this->isMatch($cookie, $url->getHost(), $url->getPath(), $cookie->getName())) {
$pairs[] = $cookie->getName() . '=' . $cookie->getValue();
}
}
return $pairs;
}
}
?>

View File

@ -0,0 +1,163 @@
<?php
/**
* Optional include file for SimpleTest
* @package SimpleTest
* @subpackage UnitTester
* @version $Id: default_reporter.php 1784 2008-04-26 13:07:14Z pp11 $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/simpletest.php');
require_once(dirname(__FILE__) . '/scorer.php');
require_once(dirname(__FILE__) . '/reporter.php');
require_once(dirname(__FILE__) . '/xml.php');
/**#@-*/
/**
* Parser for command line arguments. Extracts
* the a specific test to run and engages XML
* reporting when necessary.
* @package SimpleTest
* @subpackage UnitTester
*/
class SimpleCommandLineParser {
private $to_property = array(
'case' => 'case', 'c' => 'case',
'test' => 'test', 't' => 'test',
);
private $case = '';
private $test = '';
private $xml = false;
private $help = false;
private $no_skips = false;
/**
* Parses raw command line arguments into object properties.
* @param string $arguments Raw commend line arguments.
*/
function __construct($arguments) {
if (! is_array($arguments)) {
return;
}
foreach ($arguments as $i => $argument) {
if (preg_match('/^--?(test|case|t|c)=(.+)$/', $argument, $matches)) {
$property = $this->to_property[$matches[1]];
$this->$property = $matches[2];
} elseif (preg_match('/^--?(test|case|t|c)$/', $argument, $matches)) {
$property = $this->to_property[$matches[1]];
if (isset($arguments[$i + 1])) {
$this->$property = $arguments[$i + 1];
}
} elseif (preg_match('/^--?(xml|x)$/', $argument)) {
$this->xml = true;
} elseif (preg_match('/^--?(no-skip|no-skips|s)$/', $argument)) {
$this->no_skips = true;
} elseif (preg_match('/^--?(help|h)$/', $argument)) {
$this->help = true;
}
}
}
/**
* Run only this test.
* @return string Test name to run.
*/
function getTest() {
return $this->test;
}
/**
* Run only this test suite.
* @return string Test class name to run.
*/
function getTestCase() {
return $this->case;
}
/**
* Output should be XML or not.
* @return boolean True if XML desired.
*/
function isXml() {
return $this->xml;
}
/**
* Output should suppress skip messages.
* @return boolean True for no skips.
*/
function noSkips() {
return $this->no_skips;
}
/**
* Output should be a help message. Disabled during XML mode.
* @return boolean True if help message desired.
*/
function help() {
return $this->help && !$this->xml;
}
/**
* Returns plain-text help message for command line runner.
* @return string String help message
*/
function getHelpText() {
return <<<HELP
SimpleTest command line default reporter (autorun)
Usage: php <test_file> [args...]
-c <class> Run only the test-case <class>
-t <method> Run only the test method <method>
-s Suppress skip messages
-x Return test results in XML
-h Display this help message
HELP;
}
}
/**
* The default reporter used by SimpleTest's autorun
* feature. The actual reporters used are dependency
* injected and can be overridden.
* @package SimpleTest
* @subpackage UnitTester
*/
class DefaultReporter extends SimpleReporterDecorator {
/**
* Assembles the appopriate reporter for the environment.
*/
function __construct() {
if (SimpleReporter::inCli()) {
$parser = new SimpleCommandLineParser($_SERVER['argv']);
$interfaces = $parser->isXml() ? array('XmlReporter') : array('TextReporter');
if ($parser->help()) {
// I'm not sure if we should do the echo'ing here -- ezyang
echo $parser->getHelpText();
exit(1);
}
$reporter = new SelectiveReporter(
SimpleTest::preferred($interfaces),
$parser->getTestCase(),
$parser->getTest());
if ($parser->noSkips()) {
$reporter = new NoSkipsReporter($reporter);
}
} else {
$reporter = new SelectiveReporter(
SimpleTest::preferred('HtmlReporter'),
@$_GET['c'],
@$_GET['t']);
if (@$_GET['skips'] == 'no' || @$_GET['show-skips'] == 'no') {
$reporter = new NoSkipsReporter($reporter);
}
}
parent::__construct($reporter);
}
}
?>

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