Skip to content

Commit

Permalink
Refactor socket handling & mailbox … and add tests!
Browse files Browse the repository at this point in the history
  • Loading branch information
GwendolenLynch committed Sep 23, 2022
1 parent 5a5305f commit 5aa6870
Show file tree
Hide file tree
Showing 133 changed files with 5,516 additions and 1,057 deletions.
14 changes: 14 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Log levels:
# - Debug
# - Info
# - Notice
# - Warning
# - Error
# - Critical
# - Alert
# - Emergency
SMTP_LOG_LEVEL=debug
SMTP_LOG_FILE="%kernel.project_dir%/var/log/smtp.log"
SMTP_SPOOL_DIR="%kernel.project_dir%/var/spool"
HTTP_LOG_LEVEL=debug
HTTP_LOG_FILE="%kernel.project_dir%/var/log/http.log"
5 changes: 5 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SMTP_LOG_LEVEL=debug
SMTP_LOG_FILE="%kernel.project_dir%/var/test/smtp.log"
SMTP_SPOOL_DIR="%kernel.project_dir%/var/test/spool"
HTTP_LOG_LEVEL=debug
HTTP_LOG_FILE="%kernel.project_dir%/var/test/http.log"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/vendor/

/composer.lock
/.env.local
/.php-cs-fixer.php
/.php-cs-fixer.cache
/phpunit.xml
Expand Down
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
'@PhpCsFixer:risky' => true,
])
->in('src')
->in('tests')
;
73 changes: 61 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
SMTP Development Server
=======================

A simple and very fake SMTP server for development/testing purposes.
**FOR USE IN DEVELOPMENT ENVIRONMENTS ONLY**

Now even with a HTTP server to provide a read-only mail client for messages received. Because, why not.
A simple and very fake SMTP server for development/testing purposes. Because, why not.

**Features**
- SMTP server that can accept & store valid RFC822/RFC2822 email
- HTTP server/site as a client interface to the messages received

**WARNING: Do not expose running server ports to open networks and do not run as
`root` or an admin user! There are ZERO security features built into these
servers.**

Installation
------------
Expand All @@ -20,20 +28,53 @@ As a development dependency for your project:
$ composer require --dev camelot/smtp-dev-server
```

Configuration
-------------

Configuration is handled via environment variables.

```shell
SMTP_LOG_LEVEL=debug
SMTP_LOG_FILE="/path/to/smtp.log"
SMTP_SPOOL_DIR="/path/to/spool"
HTTP_LOG_LEVEL=debug
HTTP_LOG_FILE="/path/to/http.log"
```

See the `.env` file in this directory for an example if you're cloning this
repository, you can create a `.env.local` file to override any of the values in
the `.env` file.

Use
---

Both servers have two output targets, console and PSR logger.

Console output levels are managed by passing `-v`, `-vv`, or `-vvv` as options
on the command line.

Logger output is managed via environment variables that are used internally to
configure the loggers.

For example:
```
$ SMTP_LOG_LEVEL=debug vendor/bin/smtp-dev-server -vvv
```

### Server

![image](https://user-images.githubusercontent.com/1427081/194073098-e655ea46-bda4-4c63-8d7e-c193f4636a82.png)

To start the server, simply run:

```console
$ vendor/bin/smtp-dev-server
```

This will output all incoming request data to STDOUT.
This will output internal information to STDOUT. You can specify verbosity with
the command options below.

**NOTE:** Currently the server will also log transactions to `./var/log/smtp.log`
Server can be stopped by sending a signal, e.g. `CTRL+C`.

#### Arguments

Expand All @@ -44,22 +85,28 @@ This will output all incoming request data to STDOUT.
#### Options

```
-i, --ip=IP TCP/IP address [default: "127.0.0.1"]
-p, --port=PORT Port [default: 2525]
-h, --help Show help
-i, --ip=IP TCP/IP address [default: "127.0.0.1"]
-p, --port=PORT Port to listen on [default: 2525]
-r, --retries=RETRIES Number of times to retry connecting to the server socket address if it is currently in use [default: 10]
-h, --help Show help
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
```

### Client

![image](https://user-images.githubusercontent.com/1427081/194073411-604cb0b9-0901-48ee-ae0a-86459394ae3c.png)
![image](https://user-images.githubusercontent.com/1427081/194208872-28f7b627-846a-43ce-81d3-52c6c0a3b90d.png)

To start the server, simply run:

```console
$ vendor/bin/smtp-dev-client
```

This will output all incoming request data to STDOUT.
This will output internal information to STDOUT. You can specify verbosity with
the command options below.

**NOTE:** Currently the server will also log transactions to `./var/log/http.log`
Server can be stopped by sending a signal, e.g. `CTRL+C`.

#### Arguments

Expand All @@ -70,9 +117,11 @@ This will output all incoming request data to STDOUT.
#### Options

```
-i, --ip=IP TCP/IP address [default: "127.0.0.1"]
-p, --port=PORT Port [default: 2580]
-h, --help Show help
-i, --ip=IP TCP/IP address [default: "127.0.0.1"]
-p, --port=PORT Port to listen on [default: 2580]
-r, --retries=RETRIES Number of times to retry connecting to the server socket address if it is currently in use [default: 10]
-h, --help Show help
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
```

Open (default) `http://127.0.0.1:2580` to view & manage messages received by the SMTP server component.
46 changes: 0 additions & 46 deletions bin/app.php

This file was deleted.

26 changes: 18 additions & 8 deletions bin/smtp-dev-client
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@
<?php

use Camelot\SmtpDevServer\Command\HttpServerCommand;
use Camelot\SmtpDevServer\DependencyInjection\Kernel;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\DependencyInjection\Container;

$run = function (): void {
$builder = require_once __DIR__ . '/app.php';
/** @var Container $container */
$container = $builder();
$command = $container->get(HttpServerCommand::class);
$command->run(new ArgvInput(), new ConsoleOutput());
$run = function (): int {
require_once dirname(__DIR__) . '/config/bootstrap.php';
$kernel = new Kernel('dev', true);
$kernel->boot();
$command = $kernel->getContainer()->get(HttpServerCommand::class);

$input = new ArgvInput(null, $command->getDefinition());
if ($input->getOption('help')) {
$help = new HelpCommand();
$help->setCommand($command);

return $help->run($input, new ConsoleOutput());
}

return $command->run(new ArgvInput(), new ConsoleOutput());
};

$run();
return $run();

26 changes: 18 additions & 8 deletions bin/smtp-dev-server
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@
<?php

use Camelot\SmtpDevServer\Command\SmtpServerCommand;
use Camelot\SmtpDevServer\DependencyInjection\Kernel;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\DependencyInjection\Container;

$run = function (): void {
$builder = require_once __DIR__ . '/app.php';
/** @var Container $container */
$container = $builder();
$command = $container->get(SmtpServerCommand::class);
$command->run(new ArgvInput(), new ConsoleOutput());
$run = function (): int {
require_once dirname(__DIR__) . '/config/bootstrap.php';
$kernel = new Kernel('dev', true);
$kernel->boot();
$command = $kernel->getContainer()->get(SmtpServerCommand::class);

$input = new ArgvInput(null, $command->getDefinition());
if ($input->getOption('help')) {
$help = new HelpCommand();
$help->setCommand($command);

return $help->run($input, new ConsoleOutput());
}

return $command->run(new ArgvInput(), new ConsoleOutput());
};

$run();
return $run();
8 changes: 7 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,26 @@
"minimum-stability": "stable",
"require": {
"php": ">=8.1",
"ext-pcntl": "*",
"ext-sockets": "*",
"monolog/monolog": "^3.0",
"php-mime-mail-parser/php-mime-mail-parser": "^7.1",
"psr/log": "^2.0 || ^3.0",
"symfony/config": "^6.0",
"symfony/console": "^6.0",
"symfony/dependency-injection": "^6.0",
"symfony/dotenv": "^6.0",
"symfony/event-dispatcher": "^6.0",
"symfony/http-foundation": "^6.0",
"symfony/finder": "^6.0",
"symfony/http-kernel": "^6.0",
"symfony/routing": "^6.0",
"symfony/stopwatch": "^6.0",
"twig/twig": "^3.4"
},
"require-dev": {
"camelot/coding-style": "^3.0",
"friendsofphp/php-cs-fixer": "^3.11",
"symfony/mailer": "^6.0",
"phpunit/phpunit": "^9.5",
"symfony/phpunit-bridge": "^6.0"
},
Expand All @@ -42,6 +47,7 @@
}
},
"bin": [
"bin/smtp-dev-client",
"bin/smtp-dev-server"
]
}
17 changes: 17 additions & 0 deletions config/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

use Symfony\Component\Dotenv\Dotenv;

if (is_file(dirname(__DIR__) . '/vendor/autoload.php')) {
$projectDir = dirname(__DIR__);
require_once $projectDir . '/vendor/autoload.php';
} elseif (is_file(dirname(__DIR__, 3) . '/autoload.php')) {
$projectDir = dirname(__DIR__, 3);
require_once $projectDir . '/autoload.php';
} else {
throw new LogicException('Composer autoload is missing. Try running "composer install".');
}

(new DotEnv())->bootEnv("{$projectDir}/.env");
8 changes: 8 additions & 0 deletions config/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

declare(strict_types=1);

use Symfony\Config\SmtpDevServerConfig;

return static function (SmtpDevServerConfig $serverConfig): void {
};
9 changes: 8 additions & 1 deletion config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
declare(strict_types=1);

use Camelot\SmtpDevServer\Controller\AssetController;
use Camelot\SmtpDevServer\Controller\AttachmentController;
use Camelot\SmtpDevServer\Controller\MailboxController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes): void {
$routes->add('mailbox', '/')
->controller(MailboxController::class)
->methods([Request::METHOD_GET])
->methods([Request::METHOD_GET, Request::METHOD_POST])
;
$routes->add('asset', '/asset/{asset}')
->controller(AssetController::class)
->methods([Request::METHOD_GET])
->requirements(['asset' => '.+'])
;
$routes->add('attachment', '/attachment/{messageId}/{filename}')
->controller(AttachmentController::class)
->methods([Request::METHOD_GET])
->requirements(['filename' => '.+'])
;
};
Loading

0 comments on commit 5aa6870

Please sign in to comment.