Skip to content

Commit

Permalink
Updated Rules
Browse files Browse the repository at this point in the history
  • Loading branch information
willbrowningme committed Jun 26, 2024
1 parent 5e186b4 commit bc626e8
Show file tree
Hide file tree
Showing 20 changed files with 1,313 additions and 754 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This is the source code for self-hosting addy.io.
- [What is the max email size limit?](#what-is-the-max-email-size-limit)
- [What happens if I have a subscription but then cancel it?](#what-happens-if-i-have-a-subscription-but-then-cancel-it)
- [If I subscribe will Stripe see my real email address?](#if-i-subscribe-will-stripe-see-my-real-email-address)
- [Do you offer student discount?](#do-you-offer-student-discount)
- [How do you prevent spammers?](#how-do-you-prevent-spammers)
- [What do you use to do DNS lookups on domain names?](#what-do-you-use-to-do-dns-lookups-on-domain-names)
- [Is there a limit to how many emails I can forward?](#is-there-a-limit-to-how-many-emails-i-can-forward)
Expand Down Expand Up @@ -363,6 +364,10 @@ You will not be able to activate any of the above again until you resubscribe.

When you subscribe you can choose which email to provide to Stripe, feel free to use an alias. This email will be used for notifications from Stripe such as; if your card payment fails or if your card has expired.

## Do you offer student discount?

Currently, addy.io does not offer any student discounts.

## How do you prevent spammers?

The following is in place to help prevent spam:
Expand Down
2 changes: 1 addition & 1 deletion app/Console/Commands/EmailUsersWithTokenExpiringSoon.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function __construct()
*/
public function handle()
{
User::with(['defaultUsername', 'defaultRecipient'])
User::with(['defaultUsername', 'defaultRecipient', 'tokens'])
->whereHas('tokens', function ($query) {
$query->whereDate('expires_at', now()->addWeek());
})
Expand Down
19 changes: 7 additions & 12 deletions app/Console/Commands/ReceiveEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ public function handle()
}

if ($this->parser->getHeader('In-Reply-To') && $alias) {
$this->handleReply($user, $recipient, $alias);
$this->handleReply($user, $alias, $validEmailDestination);
} else {
$this->handleSendFrom($user, $recipient, $alias ?? null, $aliasable ?? null);
$this->handleSendFrom($user, $recipient, $alias ?? null, $aliasable ?? null, $validEmailDestination);
}
} elseif ($verifiedRecipient?->can_reply_send === false) {
// Notify user that they have not allowed this recipient to reply and send from aliases
Expand Down Expand Up @@ -232,18 +232,16 @@ protected function handleUnsubscribe($recipient)
}
}

protected function handleReply($user, $recipient, $alias)
protected function handleReply($user, $alias, $destination)
{
$sendTo = Str::replaceLast('=', '@', $recipient['extension']);

$emailData = new EmailData($this->parser, $this->option('sender'), $this->size, 'R');

$message = new ReplyToEmail($user, $alias, $emailData);

Mail::to($sendTo)->queue($message);
Mail::to($destination)->queue($message);
}

protected function handleSendFrom($user, $recipient, $alias, $aliasable)
protected function handleSendFrom($user, $recipient, $alias, $aliasable, $destination)
{
if (is_null($alias)) {
$alias = $user->aliases()->create([
Expand All @@ -258,13 +256,11 @@ protected function handleSendFrom($user, $recipient, $alias, $aliasable)
$alias->refresh();
}

$sendTo = Str::replaceLast('=', '@', $recipient['extension']);

$emailData = new EmailData($this->parser, $this->option('sender'), $this->size, 'S');

$message = new SendFromEmail($user, $alias, $emailData);

Mail::to($sendTo)->queue($message);
Mail::to($destination)->queue($message);
}

protected function handleForward($user, $recipient, $alias, $aliasable, $isSpam)
Expand Down Expand Up @@ -470,8 +466,7 @@ protected function checkRateLimit($user)
->allow(config('anonaddy.limit'))
->every(3600)
->then(
function () {
},
function () {},
function () use ($user) {
$user->update(['defer_until' => now()->addHour()]);

Expand Down
30 changes: 24 additions & 6 deletions app/CustomMailDriver/CustomMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

class CustomMailer extends Mailer
{
private $data;

/**
* Send a new message using a view.
*
Expand All @@ -34,6 +36,8 @@ class CustomMailer extends Mailer
*/
public function send($view, array $data = [], $callback = null)
{
$this->data = $data;

if ($view instanceof MailableContract) {
return $this->sendMailable($view);
}
Expand Down Expand Up @@ -134,9 +138,9 @@ public function send($view, array $data = [], $callback = null)

// If the message is a forward, reply or send then use the verp domain
if (isset($data['emailType']) && in_array($data['emailType'], ['F', 'R', 'S'])) {
$message->returnPath($verpLocalPart.'@'.$data['verpDomain']);
$symfonyMessage->returnPath($verpLocalPart.'@'.$data['verpDomain']);
} else {
$message->returnPath($verpLocalPart.'@'.config('anonaddy.domain'));
$symfonyMessage->returnPath($verpLocalPart.'@'.config('anonaddy.domain'));
}

try {
Expand Down Expand Up @@ -246,10 +250,24 @@ protected function sendSymfonyMessage(Email $message)
{
try {
$envelopeMessage = clone $message;
// This allows us to have the To: header set as the alias whilst still delivering to the correct RCPT TO.
if ($aliasTo = $message->getHeaders()->get('Alias-To')) {
$message->to($aliasTo->getValue());
$message->getHeaders()->remove('Alias-To');

// Add in original Tos that have been updated
if ($tos = $this->data['tos'] ?? null) {
foreach ($tos as $key => $to) {
if ($key === 0) {
// This allows us to have the To: header set as the alias whilst still delivering to the correct RCPT TO for forwards.
$message->to($to); // In order to override recipient email for forwards
} else {
$message->addTo($to);
}
}
}

// Add in original CCs that have been updated
if ($ccs = $this->data['ccs'] ?? null) {
foreach ($ccs as $cc) {
$message->addCc($cc);
}
}

// Add the original sender header here to prevent it altering the envelope from address
Expand Down
15 changes: 15 additions & 0 deletions app/Helpers/Helper.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

function user()
{
Expand All @@ -25,3 +26,17 @@ function randomString(int $length): string

return $str;
}

function stripEmailExtension(string $email): string
{
if (! Str::contains($email, '@')) {
return $email;
}

// Strip the email of extensions
[$localPart, $domain] = explode('@', strtolower($email));
// Remove plus extension from local part if present
$localPart = Str::contains($localPart, '+') ? Str::before($localPart, '+') : $localPart;

return $localPart.'@'.$domain;
}
1 change: 1 addition & 0 deletions app/Http/Controllers/ShowRuleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function index(Request $request)
})
->orderBy('order')
->get(),
'recipientOptions' => user()->verifiedRecipients()->select(['id', 'email'])->get(),
'search' => $validated['search'] ?? null,
]);
}
Expand Down
19 changes: 14 additions & 5 deletions app/Http/Requests/StoreRuleRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function rules()
'subject',
'sender',
'alias',
'alias_description',
]),
],
'conditions.*.match' => [
Expand Down Expand Up @@ -79,13 +80,21 @@ public function rules()
'encryption',
'banner',
'block',
'webhook',
'removeAttachments',
'forwardTo',
//'webhook',
]),
],
'actions.*.value' => [
'required',
'max:50',
],
'actions.*.value' => Rule::forEach(function ($value, $attribute, $data, $action) {
if ($action['type'] === 'forwardTo') {
return [Rule::in(user()->verifiedRecipients()->pluck('id')->toArray())]; // Must be a valid verified recipient
}

return [
'required',
'max:50',
];
}),
'operator' => [
'required',
'in:AND,OR',
Expand Down
71 changes: 67 additions & 4 deletions app/Mail/ForwardEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class ForwardEmail extends Mailable implements ShouldBeEncrypted, ShouldQueue

protected $sender;

protected $ccs;

protected $tos;

protected $originalCc;

protected $originalTo;
Expand Down Expand Up @@ -105,8 +109,69 @@ public function __construct(Alias $alias, EmailData $emailData, Recipient $recip
$this->user = $alias->user;
$this->alias = $alias;
$this->sender = $emailData->sender;
$this->ccs = $emailData->ccs;
$this->tos = $emailData->tos;
$this->originalCc = $emailData->originalCc ?? null;
$this->originalTo = $emailData->originalTo ?? null;

// Create and swap with alias reply-to addresses to allow easy reply-all
if (count($this->ccs)) {
$this->ccs = collect($this->ccs)
->map(function ($cc) {
// Leave alias email Cc as it is
if (stripEmailExtension($cc['address']) === $this->alias->email) {
return [
'display' => $cc['display'] != $cc['address'] ? $cc['display'] : null,
'address' => $this->alias->email,
];
}

return [
'display' => $cc['display'] != $cc['address'] ? $cc['display'] : null,
'address' => $this->alias->local_part.'+'.Str::replaceLast('@', '=', $cc['address']).'@'.$this->alias->domain,
];
})
->filter(fn ($cc) => filter_var($cc['address'], FILTER_VALIDATE_EMAIL))
->map(function ($cc) {
// Only add in display if it exists
if ($cc['display']) {
return $cc['display'].' <'.$cc['address'].'>';
}

return '<'.$cc['address'].'>';
})
->toArray();
}

// Create and swap with alias reply-to addresses to allow easy reply-all
if (count($this->tos)) {
$this->tos = collect($this->tos)
->map(function ($to) {
// Leave alias email To as it is
if (stripEmailExtension($to['address']) === $this->alias->email) {
return [
'display' => $to['display'] != $to['address'] ? $to['display'] : null,
'address' => $this->alias->email,
];
}

return [
'display' => $to['display'] != $to['address'] ? $to['display'] : null,
'address' => $this->alias->local_part.'+'.Str::replaceLast('@', '=', $to['address']).'@'.$this->alias->domain,
];
})
->filter(fn ($to) => filter_var($to['address'], FILTER_VALIDATE_EMAIL))
->map(function ($to) {
// Only add in display if it exists
if ($to['display']) {
return $to['display'].' <'.$to['address'].'>';
}

return '<'.$to['address'].'>';
})
->toArray();
}

$this->displayFrom = $emailData->display_from;
$this->replyToAddress = $emailData->reply_to_address ?? $this->sender;
$this->emailSubject = $emailData->subject;
Expand Down Expand Up @@ -173,10 +238,6 @@ public function build()
$message->getHeaders()
->addTextHeader('Feedback-ID', 'F:'.$this->alias->id.':anonaddy');

// This header is used to set the To: header as the alias just before sending.
$message->getHeaders()
->addTextHeader('Alias-To', $this->alias->email);

$message->getHeaders()->remove('Message-ID');

if ($this->messageId) {
Expand Down Expand Up @@ -327,6 +388,8 @@ public function build()
'shouldBlock' => $this->size === 0,
'needsDkimSignature' => $this->needsDkimSignature(),
'verpDomain' => $this->verpDomain ?? $this->alias->domain,
'ccs' => $this->ccs,
'tos' => $this->tos,
]);

if (isset($replyToEmail)) {
Expand Down
Loading

0 comments on commit bc626e8

Please sign in to comment.