Skip to content

Commit

Permalink
Fix for anonaddy#47 and other minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Will committed Oct 6, 2020
1 parent e69424e commit 8ecc246
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 130 deletions.
78 changes: 37 additions & 41 deletions app/Console/Commands/ReceiveEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,45 +74,51 @@ public function handle()
$this->size = $this->option('size') / ($recipientCount ? $recipientCount : 1);

foreach ($recipients as $recipient) {
$parentDomain = collect(config('anonaddy.all_domains'))

// First determine if the alias already exists in the database
if ($alias = Alias::where('email', $recipient['local_part'] . '@' . $recipient['domain'])->first()) {
$user = $alias->user;

if ($alias->aliasable_id) {
$aliasable = $alias->aliasable;
}
} else {
// Does not exist, must be a standard, additional username or custom domain alias
$parentDomain = collect(config('anonaddy.all_domains'))
->filter(function ($name) use ($recipient) {
return Str::endsWith($recipient['domain'], $name);
})
->first();

if ($parentDomain) {
$subdomain = substr($recipient['domain'], 0, strrpos($recipient['domain'], '.'.$parentDomain));
if (!empty($parentDomain)) {
// It is standard or additional username alias
$subdomain = substr($recipient['domain'], 0, strrpos($recipient['domain'], '.' . $parentDomain)); // e.g. johndoe

if ($subdomain === 'unsubscribe') {
$this->handleUnsubscribe($recipient);
continue;
}
if ($subdomain === 'unsubscribe') {
$this->handleUnsubscribe($recipient);
continue;
}

// Check if this is an additional username.
if ($additionalUsername = AdditionalUsername::where('username', $subdomain)->first()) {
$user = $additionalUsername->user;
$aliasable = $additionalUsername;
} else {
$user = User::where('username', $subdomain)->first();
}
}
// Check if this is an additional username or standard alias
if (!empty($subdomain)) {
$user = User::where('username', $subdomain)->first();

if (!isset($user)) {
// Check if this is a custom domain.
if ($customDomain = Domain::where('domain', $recipient['domain'])->first()) {
$user = $customDomain->user;
$aliasable = $customDomain;
if (!isset($user)) {
$additionalUsername = AdditionalUsername::where('username', $subdomain)->first();
$user = $additionalUsername->user;
$aliasable = $additionalUsername;
}
}
} else {
// It is a custom domain
if ($customDomain = Domain::where('domain', $recipient['domain'])->first()) {
$user = $customDomain->user;
$aliasable = $customDomain;
}
}

// check if this is a uuid generated alias
if ($alias = Alias::find($recipient['local_part'])) {
$user = $alias->user;
} elseif ($recipient['domain'] === $parentDomain) {
if ($alias = Alias::where('email', $recipient['local_part'] . '@' . $recipient['domain'])->first()) {
$user = $alias->user;
} elseif (!empty(config('anonaddy.admin_username'))) {
$user = User::where('username', config('anonaddy.admin_username'))->first();
}
if (!isset($user) && !empty(config('anonaddy.admin_username'))) {
$user = User::where('username', config('anonaddy.admin_username'))->first();
}
}

Expand Down Expand Up @@ -175,15 +181,10 @@ protected function handleSendFrom($user, $recipient, $aliasable)
'email' => $recipient['local_part'] . '@' . $recipient['domain'],
'local_part' => $recipient['local_part'],
'domain' => $recipient['domain'],
'aliasable_id' => $aliasable->id ?? null,
'aliasable_type' => $aliasable ? 'App\\Models\\' . class_basename($aliasable) : null
]);

$aliasableId = $aliasable->id ?? null;

if ($alias->aliasable_id !== $aliasableId) {
$alias->aliasable_id = $aliasableId;
}

// This is a new alias but at a shared domain or the sender is not a verified recipient.
if (!isset($alias->id) && in_array($recipient['domain'], config('anonaddy.all_domains'))) {
exit(0);
Expand All @@ -207,15 +208,10 @@ protected function handleForward($user, $recipient, $aliasable)
'email' => $recipient['local_part'] . '@' . $recipient['domain'],
'local_part' => $recipient['local_part'],
'domain' => $recipient['domain'],
'aliasable_id' => $aliasable->id ?? null,
'aliasable_type' => $aliasable ? 'App\\Models\\' . class_basename($aliasable) : null
]);

$aliasableId = $aliasable->id ?? null;

if ($alias->aliasable_id !== $aliasableId) {
$alias->aliasable_id = $aliasableId;
}

if (!isset($alias->id)) {
// This is a new alias.
if ($user->hasExceededNewAliasLimit()) {
Expand Down
7 changes: 6 additions & 1 deletion app/Http/Controllers/ShowAdditionalUsernameController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ class ShowAdditionalUsernameController extends Controller
public function index()
{
return view('usernames.index', [
'usernames' => user()->additionalUsernames()->with(['aliases', 'defaultRecipient'])->latest()->get()
'usernames' => user()
->additionalUsernames()
->with('defaultRecipient:id,email')
->withCount('aliases')
->latest()
->get()
]);
}
}
28 changes: 20 additions & 8 deletions app/Http/Controllers/ShowAliasController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,30 @@ class ShowAliasController extends Controller
{
public function index()
{
$totals = user()
->aliases()
->withTrashed()
->toBase()
->selectRaw("sum(emails_forwarded) as forwarded")
->selectRaw("sum(emails_blocked) as blocked")
->selectRaw("sum(emails_replied) as replies")
->first();

return view('aliases.index', [
'user' => user(),
'defaultRecipient' => user()->defaultRecipient,
'aliases' => user()->aliases()->with(['recipients', 'aliasable.defaultRecipient'])->latest()->get(),
'recipients' => user()->verifiedRecipients,
'totalForwarded' => user()->totalEmailsForwarded(),
'totalBlocked' => user()->totalEmailsBlocked(),
'totalReplies' => user()->totalEmailsReplied(),
'aliases' => user()
->aliases()
->with([
'recipients:recipient_id,email',
'aliasable.defaultRecipient:id,email'
])
->latest()
->get(),
'recipients' => user()->verifiedRecipients()->select(['id', 'email'])->get(),
'totals' => $totals,
'domain' => user()->username.'.'.config('anonaddy.domain'),
'bandwidthMb' => user()->bandwidth_mb,
'domainOptions' => user()->domainOptions(),
'defaultAliasDomain' => user()->default_alias_domain,
'defaultAliasFormat' => user()->default_alias_format
]);
}
}
7 changes: 6 additions & 1 deletion app/Http/Controllers/ShowDomainController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ class ShowDomainController extends Controller
public function index()
{
return view('domains.index', [
'domains' => user()->domains()->with(['aliases', 'defaultRecipient'])->latest()->get()
'domains' => user()
->domains()
->with('defaultRecipient:id,email')
->withCount('aliases')
->latest()
->get()
]);
}
}
25 changes: 23 additions & 2 deletions app/Http/Controllers/ShowRecipientController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,27 @@ class ShowRecipientController extends Controller
{
public function index()
{
$recipients = user()->recipients()->with('aliases')->latest()->get();
$recipients = user()->recipients()->with([
'aliases:alias_id,aliasable_id,email',
'domainsUsingAsDefault.aliases:id,aliasable_id,email',
'AdditionalUsernamesUsingAsDefault.aliases:id,aliasable_id,email'
])->latest()->get();

$recipients->each(function ($recipient) {
if ($recipient->domainsUsingAsDefault) {
$domainAliases = $recipient->domainsUsingAsDefault->flatMap(function ($domain) {
return $domain->aliases;
});
$recipient->setRelation('aliases', $recipient->aliases->concat($domainAliases)->unique('email'));
}

if ($recipient->AdditionalUsernamesUsingAsDefault) {
$AdditionalUsernameAliases = $recipient->AdditionalUsernamesUsingAsDefault->flatMap(function ($domain) {
return $domain->aliases;
});
$recipient->setRelation('aliases', $recipient->aliases->concat($AdditionalUsernameAliases)->unique('email'));
}
});

$count = $recipients->count();

Expand All @@ -16,7 +36,8 @@ public function index()

return view('recipients.index', [
'recipients' => $recipients,
'aliasesUsingDefault' => user()->aliasesUsingDefault
'aliasesUsingDefault' => user()->aliasesUsingDefault()->take(5)->get(),
'aliasesUsingDefaultCount' => user()->aliasesUsingDefault()->count(),
]);
}
}
5 changes: 4 additions & 1 deletion app/Http/Controllers/ShowRuleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ class ShowRuleController extends Controller
public function index()
{
return view('rules.index', [
'rules' => user()->rules()->orderBy('order')->get()
'rules' => user()
->rules()
->orderBy('order')
->get()
]);
}
}
4 changes: 3 additions & 1 deletion app/Http/Requests/StoreDomainRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Requests;

use App\Rules\NotLocalDomain;
use App\Rules\NotUsedAsRecipientDomain;
use App\Rules\ValidDomain;
use Illuminate\Foundation\Http\FormRequest;

Expand Down Expand Up @@ -32,7 +33,8 @@ public function rules()
'max:50',
'unique:domains',
new ValidDomain,
new NotLocalDomain
new NotLocalDomain,
new NotUsedAsRecipientDomain
]
];
}
Expand Down
16 changes: 16 additions & 0 deletions app/Models/Recipient.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ public function aliases()
return $this->belongsToMany(Alias::class, 'alias_recipients')->using(AliasRecipient::class);
}

/**
* Get all of the user's custom domains.
*/
public function domainsUsingAsDefault()
{
return $this->hasMany(Domain::class, 'default_recipient_id', 'id');
}

/**
* Get all of the user's custom domains.
*/
public function additionalUsernamesUsingAsDefault()
{
return $this->hasMany(AdditionalUsername::class, 'default_recipient_id', 'id');
}

/**
* Determine if the recipient has a verified email address.
*
Expand Down
11 changes: 10 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Traits\HasEncryptedAttributes;
use App\Traits\HasUuid;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
Expand Down Expand Up @@ -215,7 +216,15 @@ public function aliasRecipients()
*/
public function aliasesUsingDefault()
{
return $this->aliases()->whereDoesntHave('recipients');
return $this->aliases()->whereDoesntHave('recipients')->where(function (Builder $q) {
return $q->whereDoesntHaveMorph(
'aliasable',
['App\Models\Domain', 'App\Models\AdditionalUsername'],
function (Builder $query) {
$query->whereNotNull('default_recipient_id');
}
)->orWhereNull('aliasable_id');
});
}

public function hasVerifiedDefaultRecipient()
Expand Down
7 changes: 6 additions & 1 deletion app/Rules/NotLocalRecipient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Rules;

use App\Models\Domain;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Str;

Expand All @@ -28,7 +29,11 @@ public function passes($attribute, $value)
{
$emailDomain = Str::afterLast($value, '@');

// Make sure the recipient domain is not added as a verified custom domain
$customDomains = Domain::whereNotNull('domain_verified_at')->pluck('domain');

$count = collect(config('anonaddy.all_domains'))
->concat($customDomains)
->filter(function ($domain) use ($emailDomain) {
return Str::endsWith(strtolower($emailDomain), $domain);
})
Expand All @@ -44,6 +49,6 @@ public function passes($attribute, $value)
*/
public function message()
{
return 'The recipient cannot be a local one or alias.';
return 'The recipient cannot use a local domain or be an alias.';
}
}
49 changes: 49 additions & 0 deletions app/Rules/NotUsedAsRecipientDomain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App\Rules;

use App\Models\Recipient;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Str;

class NotUsedAsRecipientDomain implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}

/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return ! Recipient::whereNotNull('email_verified_at')
->get()
->pluck('email')
->map(function ($recipientEmail) {
return Str::afterLast($recipientEmail, '@');
})
->unique()
->contains($value);
}

/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The domain must not already be used by a verified recipient.';
}
}
2 changes: 1 addition & 1 deletion resources/js/pages/Domains.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
</div>
</span>
<span v-else-if="props.column.field === 'aliases_count'">
{{ props.row.aliases.length }}
{{ props.row.aliases_count }}
</span>
<span v-else-if="props.column.field === 'active'" class="flex items-center">
<Toggle
Expand Down
Loading

0 comments on commit 8ecc246

Please sign in to comment.