diff --git a/app/Console/Commands/ReceiveEmail.php b/app/Console/Commands/ReceiveEmail.php index 52b2242..20884d6 100644 --- a/app/Console/Commands/ReceiveEmail.php +++ b/app/Console/Commands/ReceiveEmail.php @@ -8,6 +8,7 @@ use App\Domain; use App\EmailData; use App\Mail\ForwardEmail; use App\Mail\ReplyToEmail; +use App\Mail\SendFromEmail; use App\Notifications\NearBandwidthLimit; use App\User; use Illuminate\Console\Command; @@ -130,6 +131,8 @@ class ReceiveEmail extends Command // Check whether this email is a reply or a new email to be forwarded. if ($recipient['extension'] === sha1(config('anonaddy.secret').$displayTo)) { $this->handleReply($user, $recipient, $displayTo); + } elseif (filter_var(Str::replaceLast('=', '@', $recipient['extension']), FILTER_VALIDATE_EMAIL)) { + $this->handleSendFrom($user, $recipient, $aliasable ?? null); } else { $this->handleForward($user, $recipient, $aliasable ?? null); } @@ -172,6 +175,36 @@ class ReceiveEmail extends Command } } + protected function handleSendFrom($user, $recipient, $aliasable) + { + $alias = $user->aliases()->where([ + 'email' => $recipient['local_part'] . '@' . $recipient['domain'], + 'local_part' => $recipient['local_part'], + 'domain' => $recipient['domain'], + 'aliasable_id' => $aliasable->id ?? null, + 'aliasable_type' => $aliasable ? 'App\\'.class_basename($aliasable) : null + ])->first(); + + if (!$alias || !$user->isVerifiedRecipient($this->option('sender'))) { + exit(0); + } + + $sendTo = Str::replaceLast('=', '@', $recipient['extension']); + + $emailData = new EmailData($this->parser); + + $message = new SendFromEmail($user, $alias, $emailData); + + Mail::to($sendTo)->queue($message); + + if (!Mail::failures()) { + $alias->increment('emails_sent'); + + $user->bandwidth += $this->size; + $user->save(); + } + } + protected function handleForward($user, $recipient, $aliasable) { $alias = $user->aliases()->firstOrNew([ diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 75e2e75..b49ff69 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -63,7 +63,7 @@ class ForgotPasswordController extends Controller */ protected function validateUsername(Request $request) { - $request->validate(['username' => 'required|alpha_num|max:20']); + $request->validate(['username' => 'required|regex:/^[a-zA-Z0-9]*$/|max:20']); } /** diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index f492670..4209321 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -58,7 +58,7 @@ class RegisterController extends Controller return Validator::make($data, [ 'username' => [ 'required', - 'alpha_num', + 'regex:/^[a-zA-Z0-9]*$/', 'max:20', 'unique:users,username', 'unique:additional_usernames,username', diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index c75aecd..124600f 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -63,7 +63,7 @@ class ResetPasswordController extends Controller { return [ 'token' => 'required', - 'username' => 'required|alpha_num|max:20', + 'username' => 'required|regex:/^[a-zA-Z0-9]*$/|max:20', 'password' => 'required|confirmed|min:8', ]; } diff --git a/app/Http/Requests/StoreAdditionalUsernameRequest.php b/app/Http/Requests/StoreAdditionalUsernameRequest.php index 5ca9018..33d85f2 100644 --- a/app/Http/Requests/StoreAdditionalUsernameRequest.php +++ b/app/Http/Requests/StoreAdditionalUsernameRequest.php @@ -28,7 +28,7 @@ class StoreAdditionalUsernameRequest extends FormRequest return [ 'username' => [ 'required', - 'alpha_num', + 'regex:/^[a-zA-Z0-9]*$/', 'max:20', 'unique:users,username', 'unique:additional_usernames,username', diff --git a/app/Mail/ReplyToEmail.php b/app/Mail/ReplyToEmail.php index 7118bd2..3ad6eaa 100644 --- a/app/Mail/ReplyToEmail.php +++ b/app/Mail/ReplyToEmail.php @@ -45,7 +45,7 @@ class ReplyToEmail extends Mailable implements ShouldQueue */ public function build() { - $fromName = $this->user->from_name ? $this->user->from_name : $this->alias->email; + $fromName = $this->user->from_name ?? null; if ($this->alias->isCustomDomain()) { if ($this->alias->aliasable->isVerifiedForSending()) { diff --git a/app/Mail/SendFromEmail.php b/app/Mail/SendFromEmail.php new file mode 100644 index 0000000..589a06e --- /dev/null +++ b/app/Mail/SendFromEmail.php @@ -0,0 +1,103 @@ +user = $user; + $this->alias = $alias; + $this->emailSubject = $emailData->subject; + $this->emailText = $emailData->text; + $this->emailHtml = $emailData->html; + $this->emailAttachments = $emailData->attachments; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $fromName = $this->user->from_name ?? null; + + if ($this->alias->isCustomDomain()) { + if ($this->alias->aliasable->isVerifiedForSending()) { + $fromEmail = $this->alias->email; + $returnPath = $this->alias->email; + + $this->dkimSigner = new Swift_Signers_DKIMSigner(config('anonaddy.dkim_signing_key'), $this->alias->domain, config('anonaddy.dkim_selector')); + $this->dkimSigner->ignoreHeader('Return-Path'); + } else { + $fromEmail = config('mail.from.address'); + $returnPath = config('anonaddy.return_path'); + } + } else { + $fromEmail = $this->alias->email; + $returnPath = 'mailer@'.$this->alias->parentDomain(); + } + + $email = $this + ->from($fromEmail, $fromName) + ->subject(base64_decode($this->emailSubject)) + ->text('emails.reply.text')->with([ + 'text' => base64_decode($this->emailText) + ]) + ->withSwiftMessage(function ($message) use ($returnPath) { + $message->getHeaders() + ->addTextHeader('Return-Path', config('anonaddy.return_path')); + + $message->setId(bin2hex(random_bytes(16)).'@'.$this->alias->domain); + + if ($this->dkimSigner) { + $message->attachSigner($this->dkimSigner); + } + }); + + if ($this->alias->isCustomDomain() && !$this->dkimSigner) { + $email->replyTo($this->alias->email, $fromName); + } + + if ($this->emailHtml) { + $email->view('emails.reply.html')->with([ + 'html' => base64_decode($this->emailHtml) + ]); + } + + foreach ($this->emailAttachments as $attachment) { + $email->attachData( + base64_decode($attachment['stream']), + base64_decode($attachment['file_name']), + ['mime' => base64_decode($attachment['mime'])] + ); + } + + return $email; + } +} diff --git a/app/User.php b/app/User.php index 417b14d..81b51c4 100644 --- a/app/User.php +++ b/app/User.php @@ -298,7 +298,7 @@ class User extends Authenticatable implements MustVerifyEmail public function domainOptions() { - $customDomains = $this->domains()->pluck('domain')->toArray(); + $customDomains = $this->verifiedDomains()->pluck('domain')->toArray(); return $this->additionalUsernames() ->pluck('username') diff --git a/composer.lock b/composer.lock index 9e5a219..647d012 100644 --- a/composer.lock +++ b/composer.lock @@ -1269,16 +1269,16 @@ }, { "name": "laravel/framework", - "version": "v6.11.0", + "version": "v6.13.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "17af23842c259edcfd8c5b9e6a7c86596e040034" + "reference": "f0059760814b76fb5f98bb80628607c7560ebe58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/17af23842c259edcfd8c5b9e6a7c86596e040034", - "reference": "17af23842c259edcfd8c5b9e6a7c86596e040034", + "url": "https://api.github.com/repos/laravel/framework/zipball/f0059760814b76fb5f98bb80628607c7560ebe58", + "reference": "f0059760814b76fb5f98bb80628607c7560ebe58", "shasum": "" }, "require": { @@ -1353,7 +1353,7 @@ "moontoast/math": "^1.1", "orchestra/testbench-core": "^4.0", "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8.4|^9.0", + "phpunit/phpunit": "^7.5.15|^8.4|^9.0", "predis/predis": "^1.1.1", "symfony/cache": "^4.3.4" }, @@ -1412,20 +1412,20 @@ "framework", "laravel" ], - "time": "2020-01-14T15:12:09+00:00" + "time": "2020-01-28T21:44:01+00:00" }, { "name": "laravel/passport", - "version": "v8.2.0", + "version": "v8.3.1", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "4c163b7821d29b6166fc2e93ad7649428b51c6db" + "reference": "98456cb16efd2ef7b41797e0a8559c9d8b4112f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/4c163b7821d29b6166fc2e93ad7649428b51c6db", - "reference": "4c163b7821d29b6166fc2e93ad7649428b51c6db", + "url": "https://api.github.com/repos/laravel/passport/zipball/98456cb16efd2ef7b41797e0a8559c9d8b4112f8", + "reference": "98456cb16efd2ef7b41797e0a8559c9d8b4112f8", "shasum": "" }, "require": { @@ -1484,7 +1484,7 @@ "oauth", "passport" ], - "time": "2020-01-07T19:25:00+00:00" + "time": "2020-01-29T13:25:24+00:00" }, { "name": "laravel/tinker", @@ -2103,16 +2103,16 @@ }, { "name": "nesbot/carbon", - "version": "2.29.0", + "version": "2.29.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "faf862506030dc48c061c840c7f50933f1df4ed8" + "reference": "e509be5bf2d703390e69e14496d9a1168452b0a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/faf862506030dc48c061c840c7f50933f1df4ed8", - "reference": "faf862506030dc48c061c840c7f50933f1df4ed8", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e509be5bf2d703390e69e14496d9a1168452b0a2", + "reference": "e509be5bf2d703390e69e14496d9a1168452b0a2", "shasum": "" }, "require": { @@ -2169,7 +2169,7 @@ "datetime", "time" ], - "time": "2020-01-21T07:29:55+00:00" + "time": "2020-01-21T09:36:43+00:00" }, { "name": "nikic/php-parser", @@ -2678,16 +2678,16 @@ }, { "name": "pragmarx/google2fa-laravel", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa-laravel.git", - "reference": "4ccc17dbf5ab5f752113cb6088a5afce5191309e" + "reference": "876cd52ddef2f39e029441ea2b8c1b9aa4549e58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/4ccc17dbf5ab5f752113cb6088a5afce5191309e", - "reference": "4ccc17dbf5ab5f752113cb6088a5afce5191309e", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/876cd52ddef2f39e029441ea2b8c1b9aa4549e58", + "reference": "876cd52ddef2f39e029441ea2b8c1b9aa4549e58", "shasum": "" }, "require": { @@ -2745,7 +2745,7 @@ "google2fa", "laravel" ], - "time": "2019-10-21T18:34:37+00:00" + "time": "2020-01-26T00:46:00+00:00" }, { "name": "pragmarx/google2fa-qrcode", @@ -3366,16 +3366,16 @@ }, { "name": "symfony/console", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "82437719dab1e6bdd28726af14cb345c2ec816d0" + "reference": "e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/82437719dab1e6bdd28726af14cb345c2ec816d0", - "reference": "82437719dab1e6bdd28726af14cb345c2ec816d0", + "url": "https://api.github.com/repos/symfony/console/zipball/e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f", + "reference": "e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f", "shasum": "" }, "require": { @@ -3438,20 +3438,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-12-17T10:32:23+00:00" + "time": "2020-01-10T21:54:01+00:00" }, { "name": "symfony/css-selector", - "version": "v5.0.2", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "19d29e7098b7b2c3313cb03902ca30f100dcb837" + "reference": "ff60c90cb7950b592ebc84ad1289d0345bf24f9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/19d29e7098b7b2c3313cb03902ca30f100dcb837", - "reference": "19d29e7098b7b2c3313cb03902ca30f100dcb837", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ff60c90cb7950b592ebc84ad1289d0345bf24f9f", + "reference": "ff60c90cb7950b592ebc84ad1289d0345bf24f9f", "shasum": "" }, "require": { @@ -3491,20 +3491,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-11-18T17:27:11+00:00" + "time": "2020-01-04T14:08:26+00:00" }, { "name": "symfony/debug", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "5c4c1db977dc70bb3250e1308d3e8c6341aa38f5" + "reference": "89c3fd5c299b940333bc6fe9f1b8db1b0912c759" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/5c4c1db977dc70bb3250e1308d3e8c6341aa38f5", - "reference": "5c4c1db977dc70bb3250e1308d3e8c6341aa38f5", + "url": "https://api.github.com/repos/symfony/debug/zipball/89c3fd5c299b940333bc6fe9f1b8db1b0912c759", + "reference": "89c3fd5c299b940333bc6fe9f1b8db1b0912c759", "shasum": "" }, "require": { @@ -3547,20 +3547,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-12-16T14:46:54+00:00" + "time": "2020-01-08T17:29:02+00:00" }, { "name": "symfony/error-handler", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "6d7d7712a6ff5215ec26215672293b154f1db8c1" + "reference": "a59789092e40ad08465dc2cdc55651be503d0d5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/6d7d7712a6ff5215ec26215672293b154f1db8c1", - "reference": "6d7d7712a6ff5215ec26215672293b154f1db8c1", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/a59789092e40ad08465dc2cdc55651be503d0d5a", + "reference": "a59789092e40ad08465dc2cdc55651be503d0d5a", "shasum": "" }, "require": { @@ -3603,20 +3603,20 @@ ], "description": "Symfony ErrorHandler Component", "homepage": "https://symfony.com", - "time": "2019-12-16T14:46:54+00:00" + "time": "2020-01-08T17:29:02+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b3c3068a72623287550fe20b84a2b01dcba2686f" + "reference": "9e3de195e5bc301704dd6915df55892f6dfc208b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b3c3068a72623287550fe20b84a2b01dcba2686f", - "reference": "b3c3068a72623287550fe20b84a2b01dcba2686f", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9e3de195e5bc301704dd6915df55892f6dfc208b", + "reference": "9e3de195e5bc301704dd6915df55892f6dfc208b", "shasum": "" }, "require": { @@ -3673,7 +3673,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-11-28T13:33:56+00:00" + "time": "2020-01-10T21:54:01+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -3735,16 +3735,16 @@ }, { "name": "symfony/finder", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ce8743441da64c41e2a667b8eb66070444ed911e" + "reference": "3a50be43515590faf812fbd7708200aabc327ec3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ce8743441da64c41e2a667b8eb66070444ed911e", - "reference": "ce8743441da64c41e2a667b8eb66070444ed911e", + "url": "https://api.github.com/repos/symfony/finder/zipball/3a50be43515590faf812fbd7708200aabc327ec3", + "reference": "3a50be43515590faf812fbd7708200aabc327ec3", "shasum": "" }, "require": { @@ -3780,20 +3780,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-11-17T21:56:56+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "fcae1cff5b57b2a9c3aabefeb1527678705ddb62" + "reference": "c33998709f3fe9b8e27e0277535b07fbf6fde37a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/fcae1cff5b57b2a9c3aabefeb1527678705ddb62", - "reference": "fcae1cff5b57b2a9c3aabefeb1527678705ddb62", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c33998709f3fe9b8e27e0277535b07fbf6fde37a", + "reference": "c33998709f3fe9b8e27e0277535b07fbf6fde37a", "shasum": "" }, "require": { @@ -3835,20 +3835,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-12-19T15:57:49+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "fe310d2e95cd4c356836c8ecb0895a46d97fede2" + "reference": "16f2aa3c54b08483fba5375938f60b1ff83b6bd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/fe310d2e95cd4c356836c8ecb0895a46d97fede2", - "reference": "fe310d2e95cd4c356836c8ecb0895a46d97fede2", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/16f2aa3c54b08483fba5375938f60b1ff83b6bd2", + "reference": "16f2aa3c54b08483fba5375938f60b1ff83b6bd2", "shasum": "" }, "require": { @@ -3925,20 +3925,20 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2019-12-19T16:23:40+00:00" + "time": "2020-01-21T13:23:17+00:00" }, { "name": "symfony/mime", - "version": "v5.0.2", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "0e6a4ced216e49d457eddcefb61132173a876d79" + "reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/0e6a4ced216e49d457eddcefb61132173a876d79", - "reference": "0e6a4ced216e49d457eddcefb61132173a876d79", + "url": "https://api.github.com/repos/symfony/mime/zipball/2a3c7fee1f1a0961fa9cf360d5da553d05095e59", + "reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59", "shasum": "" }, "require": { @@ -3987,7 +3987,7 @@ "mime", "mime-type" ], - "time": "2019-11-30T14:12:50+00:00" + "time": "2020-01-04T14:08:26+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4450,16 +4450,16 @@ }, { "name": "symfony/process", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b84501ad50adb72a94fb460a5b5c91f693e99c9b" + "reference": "f5697ab4cb14a5deed7473819e63141bf5352c36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b84501ad50adb72a94fb460a5b5c91f693e99c9b", - "reference": "b84501ad50adb72a94fb460a5b5c91f693e99c9b", + "url": "https://api.github.com/repos/symfony/process/zipball/f5697ab4cb14a5deed7473819e63141bf5352c36", + "reference": "f5697ab4cb14a5deed7473819e63141bf5352c36", "shasum": "" }, "require": { @@ -4495,7 +4495,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-12-06T10:06:46+00:00" + "time": "2020-01-09T09:50:08+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -4564,16 +4564,16 @@ }, { "name": "symfony/routing", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "628bcafae1b2043969378dcfbf9c196539a38722" + "reference": "7bf4e38573728e317b926ca4482ad30470d0e86a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/628bcafae1b2043969378dcfbf9c196539a38722", - "reference": "628bcafae1b2043969378dcfbf9c196539a38722", + "url": "https://api.github.com/repos/symfony/routing/zipball/7bf4e38573728e317b926ca4482ad30470d0e86a", + "reference": "7bf4e38573728e317b926ca4482ad30470d0e86a", "shasum": "" }, "require": { @@ -4636,7 +4636,7 @@ "uri", "url" ], - "time": "2019-12-12T12:53:52+00:00" + "time": "2020-01-08T17:29:02+00:00" }, { "name": "symfony/service-contracts", @@ -4698,16 +4698,16 @@ }, { "name": "symfony/translation", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "f7669f48a9633bf8139bc026c755e894b7206677" + "reference": "f5d2ac46930238b30a9c2f1b17c905f3697d808c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/f7669f48a9633bf8139bc026c755e894b7206677", - "reference": "f7669f48a9633bf8139bc026c755e894b7206677", + "url": "https://api.github.com/repos/symfony/translation/zipball/f5d2ac46930238b30a9c2f1b17c905f3697d808c", + "reference": "f5d2ac46930238b30a9c2f1b17c905f3697d808c", "shasum": "" }, "require": { @@ -4770,7 +4770,7 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2019-12-12T12:53:52+00:00" + "time": "2020-01-15T13:29:06+00:00" }, { "name": "symfony/translation-contracts", @@ -4831,16 +4831,16 @@ }, { "name": "symfony/var-dumper", - "version": "v4.4.2", + "version": "v4.4.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "be330f919bdb395d1e0c3f2bfb8948512d6bdd99" + "reference": "7cfa470bc3b1887a7b2a47c0a702a84ad614fa92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/be330f919bdb395d1e0c3f2bfb8948512d6bdd99", - "reference": "be330f919bdb395d1e0c3f2bfb8948512d6bdd99", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7cfa470bc3b1887a7b2a47c0a702a84ad614fa92", + "reference": "7cfa470bc3b1887a7b2a47c0a702a84ad614fa92", "shasum": "" }, "require": { @@ -4903,7 +4903,7 @@ "debug", "dump" ], - "time": "2019-12-18T13:41:29+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -5359,16 +5359,16 @@ }, { "name": "facade/ignition", - "version": "1.15.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/facade/ignition.git", - "reference": "c3cb2e85cf65917c50e519d64d879c5634336715" + "reference": "37f094775814b68d0c6cc8b8ff3c3be243f20725" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facade/ignition/zipball/c3cb2e85cf65917c50e519d64d879c5634336715", - "reference": "c3cb2e85cf65917c50e519d64d879c5634336715", + "url": "https://api.github.com/repos/facade/ignition/zipball/37f094775814b68d0c6cc8b8ff3c3be243f20725", + "reference": "37f094775814b68d0c6cc8b8ff3c3be243f20725", "shasum": "" }, "require": { @@ -5395,7 +5395,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "v2.x-dev" }, "laravel": { "providers": [ @@ -5426,7 +5426,7 @@ "laravel", "page" ], - "time": "2020-01-21T07:56:45+00:00" + "time": "2020-01-21T17:46:02+00:00" }, { "name": "facade/ignition-contracts", @@ -6601,16 +6601,16 @@ }, { "name": "scrivo/highlight.php", - "version": "v9.17.1.0", + "version": "v9.17.1.1", "source": { "type": "git", "url": "https://github.com/scrivo/highlight.php.git", - "reference": "5451a9ad6d638559cf2a092880f935c39776134e" + "reference": "abe5a2f0b21de310c9fccf4daafeb93e597ec7bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/5451a9ad6d638559cf2a092880f935c39776134e", - "reference": "5451a9ad6d638559cf2a092880f935c39776134e", + "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/abe5a2f0b21de310c9fccf4daafeb93e597ec7bc", + "reference": "abe5a2f0b21de310c9fccf4daafeb93e597ec7bc", "shasum": "" }, "require": { @@ -6620,8 +6620,8 @@ }, "require-dev": { "phpunit/phpunit": "^4.8|^5.7", - "symfony/finder": "^3.4", - "symfony/var-dumper": "^3.4" + "symfony/finder": "^2.8|^3.4", + "symfony/var-dumper": "^2.8|^3.4" }, "suggest": { "ext-dom": "Needed to make use of the features in the utilities namespace" @@ -6665,7 +6665,7 @@ "highlight.php", "syntax" ], - "time": "2019-12-13T21:54:06+00:00" + "time": "2020-01-27T08:38:19+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -7284,16 +7284,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.0.2", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "1d71f670bc5a07b9ccc97dc44f932177a322d4e6" + "reference": "3afadc0f57cd74f86379d073e694b0f2cda2a88c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/1d71f670bc5a07b9ccc97dc44f932177a322d4e6", - "reference": "1d71f670bc5a07b9ccc97dc44f932177a322d4e6", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3afadc0f57cd74f86379d073e694b0f2cda2a88c", + "reference": "3afadc0f57cd74f86379d073e694b0f2cda2a88c", "shasum": "" }, "require": { @@ -7330,20 +7330,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2019-11-26T23:25:11+00:00" + "time": "2020-01-21T08:40:24+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.0.2", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "1ad3d0ffc00cc1990e5c9c7bb6b81578ec3f5f68" + "reference": "b1ab86ce52b0c0abe031367a173005a025e30e04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1ad3d0ffc00cc1990e5c9c7bb6b81578ec3f5f68", - "reference": "1ad3d0ffc00cc1990e5c9c7bb6b81578ec3f5f68", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b1ab86ce52b0c0abe031367a173005a025e30e04", + "reference": "b1ab86ce52b0c0abe031367a173005a025e30e04", "shasum": "" }, "require": { @@ -7384,7 +7384,7 @@ "configuration", "options" ], - "time": "2019-11-18T17:27:11+00:00" + "time": "2020-01-04T14:08:26+00:00" }, { "name": "symfony/polyfill-php70", @@ -7447,16 +7447,16 @@ }, { "name": "symfony/stopwatch", - "version": "v5.0.2", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "d410282956706e0b08681a5527447a8e6b6f421e" + "reference": "5d9add8034135b9a5f7b101d1e42c797e7f053e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/d410282956706e0b08681a5527447a8e6b6f421e", - "reference": "d410282956706e0b08681a5527447a8e6b6f421e", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5d9add8034135b9a5f7b101d1e42c797e7f053e4", + "reference": "5d9add8034135b9a5f7b101d1e42c797e7f053e4", "shasum": "" }, "require": { @@ -7493,7 +7493,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-11-18T17:27:11+00:00" + "time": "2020-01-04T14:08:26+00:00" }, { "name": "theseer/tokenizer", diff --git a/database/migrations/2020_01_21_132726_add_emails_sent_to_aliases_table.php b/database/migrations/2020_01_21_132726_add_emails_sent_to_aliases_table.php new file mode 100644 index 0000000..dd6a416 --- /dev/null +++ b/database/migrations/2020_01_21_132726_add_emails_sent_to_aliases_table.php @@ -0,0 +1,32 @@ +unsignedInteger('emails_sent')->default(0)->after('emails_replied'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('aliases', function (Blueprint $table) { + $table->dropColumn('emails_sent'); + }); + } +} diff --git a/resources/js/pages/Aliases.vue b/resources/js/pages/Aliases.vue index 10443ed..554edcf 100644 --- a/resources/js/pages/Aliases.vue +++ b/resources/js/pages/Aliases.vue @@ -268,7 +268,8 @@ v-else-if="props.column.field == 'emails_replied'" class="font-semibold text-indigo-800" > - {{ props.row.emails_replied }} + {{ props.row.emails_replied }} / + {{ props.row.emails_sent }} post('/register', [ + 'username' => 'Ω', + 'email' => 'johndoe@example.com', + 'email_confirmation' => 'johndoe@example.com', + 'password' => 'mypassword', + 'terms' => true, + ]); + + $response->assertSessionHasErrors(['username']); + + $this->assertDatabaseMissing('users', [ + 'username' => 'Ω' + ]); + } + /** @test */ public function user_can_verify_email_successfully() { diff --git a/tests/Feature/SendFromEmailTest.php b/tests/Feature/SendFromEmailTest.php new file mode 100644 index 0000000..0a7f55c --- /dev/null +++ b/tests/Feature/SendFromEmailTest.php @@ -0,0 +1,123 @@ +user = factory(User::class)->create(['username' => 'johndoe']); + $this->user->recipients()->save($this->user->defaultRecipient); + } + + /** @test */ + public function it_can_send_email_from_alias_from_file() + { + Mail::fake(); + + Mail::assertNothingSent(); + + $alias = factory(Alias::class)->create([ + 'user_id' => $this->user->id, + 'email' => 'ebay@johndoe.'.config('anonaddy.domain'), + 'local_part' => 'ebay', + 'domain' => 'johndoe.'.config('anonaddy.domain'), + ]); + + $extension = 'contact=ebay.com'; + + $this->artisan( + 'anonaddy:receive-email', + [ + 'file' => base_path('tests/emails/email_send_from_alias.eml'), + '--sender' => $this->user->defaultRecipient->email, + '--recipient' => ['ebay+'.$extension.'@johndoe.anonaddy.com'], + '--local_part' => ['ebay'], + '--extension' => [$extension], + '--domain' => ['johndoe.anonaddy.com'], + '--size' => '1000' + ] + )->assertExitCode(0); + + $this->assertDatabaseHas('aliases', [ + 'email' => $alias->email, + 'local_part' => $alias->local_part, + 'domain' => $alias->domain, + 'emails_forwarded' => 0, + 'emails_blocked' => 0, + 'emails_replied' => 0, + 'emails_sent' => 1 + ]); + $this->assertEquals(1, $this->user->aliases()->count()); + + Mail::assertQueued(SendFromEmail::class, function ($mail) { + return $mail->hasTo('contact@ebay.com'); + }); + } + + /** @test */ + public function it_can_send_from_alias_to_multiple_emails_from_file() + { + Mail::fake(); + + Mail::assertNothingSent(); + + $alias = factory(Alias::class)->create([ + 'user_id' => $this->user->id, + 'email' => 'ebay@johndoe.'.config('anonaddy.domain'), + 'local_part' => 'ebay', + 'domain' => 'johndoe.'.config('anonaddy.domain'), + ]); + + $extension1 = 'contact=ebay.com'; + $extension2 = 'support=ebay.com'; + + $this->artisan( + 'anonaddy:receive-email', + [ + 'file' => base_path('tests/emails/email_multiple_send_from.eml'), + '--sender' => $this->user->defaultRecipient->email, + '--recipient' => [ + 'ebay+'.$extension1.'@johndoe.anonaddy.com', + 'ebay+'.$extension2.'@johndoe.anonaddy.com' + ], + '--local_part' => ['ebay', 'ebay'], + '--extension' => [$extension1, $extension2], + '--domain' => ['johndoe.anonaddy.com', 'johndoe.anonaddy.com'], + '--size' => '1000' + ] + )->assertExitCode(0); + + $this->assertDatabaseHas('aliases', [ + 'email' => $alias->email, + 'local_part' => $alias->local_part, + 'domain' => $alias->domain, + 'emails_forwarded' => 0, + 'emails_blocked' => 0, + 'emails_replied' => 0, + 'emails_sent' => 2 + ]); + $this->assertEquals(1, $this->user->aliases()->count()); + + Mail::assertQueued(SendFromEmail::class, function ($mail) { + return $mail->hasTo('contact@ebay.com'); + }); + + Mail::assertQueued(SendFromEmail::class, function ($mail) { + return $mail->hasTo('support@ebay.com'); + }); + } +} diff --git a/tests/emails/email_multiple_send_from.eml b/tests/emails/email_multiple_send_from.eml new file mode 100644 index 0000000..bd388d3 --- /dev/null +++ b/tests/emails/email_multiple_send_from.eml @@ -0,0 +1,28 @@ +Date: Wed, 20 Feb 2019 15:00:00 +0100 (CET) +From: Will +To: , +Subject: Test Email +Content-Type: multipart/mixed; boundary="----=_Part_10031_1199410393.1550677940425" + +------=_Part_10031_1199410393.1550677940425 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hi,
+
+This is a test email.
+
+Will + + +------=_Part_10031_1199410393.1550677940425 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hi, + +This is a test email. + +Will + +------=_Part_10031_1199410393.1550677940425-- diff --git a/tests/emails/email_send_from_alias.eml b/tests/emails/email_send_from_alias.eml new file mode 100644 index 0000000..391085e --- /dev/null +++ b/tests/emails/email_send_from_alias.eml @@ -0,0 +1,29 @@ +Date: Wed, 20 Feb 2019 15:00:00 +0100 (CET) +From: Will +To: ebay+contact=ebay.com@johndoe.anonaddy.com +Subject: Test Email +In-Reply-To: <9f2ada5308f1a3f88515a370504a66b3@swift.generated> +Content-Type: multipart/mixed; boundary="----=_Part_10031_1199410393.1550677940425" + +------=_Part_10031_1199410393.1550677940425 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hi,
+
+This is a test email.
+
+Will + + +------=_Part_10031_1199410393.1550677940425 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hi, + +This is a test email. + +Will + +------=_Part_10031_1199410393.1550677940425--