InboundEmail::isAutoReply()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 25
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 37
rs 8.8977
1
<?php
2
3
namespace BeyondCode\Mailbox;
4
5
use Carbon\Carbon;
6
use EmailReplyParser\EmailReplyParser;
7
use Illuminate\Contracts\Mail\Mailable;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Support\Collection;
10
use Illuminate\Support\Facades\Mail;
11
use Illuminate\Support\Str;
12
use ZBateson\MailMimeParser\Header\AddressHeader;
13
use ZBateson\MailMimeParser\Header\Part\AddressPart;
14
use ZBateson\MailMimeParser\Message as MimeMessage;
15
use ZBateson\MailMimeParser\Message\Part\MessagePart;
0 ignored issues
show
Bug introduced by
The type ZBateson\MailMimeParser\Message\Part\MessagePart was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
class InboundEmail extends Model
18
{
19
    protected $table = 'mailbox_inbound_emails';
20
21
    /** @var MimeMessage */
22
    protected $mimeMessage;
23
24
    protected $fillable = [
25
        'message',
26
    ];
27
28
    protected static function boot()
29
    {
30
        parent::boot();
31
32
        static::creating(function ($model) {
33
            $model->message_id = $model->id();
34
        });
35
    }
36
37
    public static function fromMessage($message)
38
    {
39
        return new static([
40
            'message' => $message,
41
        ]);
42
    }
43
44
    public function id(): string
45
    {
46
        return $this->message()->getHeaderValue('Message-Id', Str::random());
47
    }
48
49
    public function date(): Carbon
50
    {
51
        return Carbon::make($this->message()->getHeaderValue('Date'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return Carbon\Carbon::ma...getHeaderValue('Date')) could return the type null which is incompatible with the type-hinted return Carbon\Carbon. Consider adding an additional type-check to rule them out.
Loading history...
52
    }
53
54
    public function text(): ?string
55
    {
56
        return $this->message()->getTextContent();
57
    }
58
59
    public function visibleText(): ?string
60
    {
61
        return EmailReplyParser::parseReply($this->text());
62
    }
63
64
    public function html(): ?string
65
    {
66
        return $this->message()->getHtmlContent();
67
    }
68
69
    public function headerValue($headerName): ?string
70
    {
71
        return $this->message()->getHeaderValue($headerName, null);
72
    }
73
74
    public function subject(): ?string
75
    {
76
        return $this->message()->getHeaderValue('Subject');
77
    }
78
79
    public function from(): string
80
    {
81
        $from = $this->message()->getHeader('From');
82
83
        if ($from instanceof AddressHeader) {
84
            return $from->getEmail();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $from->getEmail() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
85
        }
86
87
        return '';
88
    }
89
90
    public function fromName(): string
91
    {
92
        $from = $this->message()->getHeader('From');
93
94
        if ($from instanceof AddressHeader) {
95
            return $from->getPersonName();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $from->getPersonName() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
96
        }
97
98
        return '';
99
    }
100
101
    /**
102
     * @return AddressPart[]
103
     */
104
    public function to(): array
105
    {
106
        return $this->convertAddressHeader($this->message()->getHeader('To'));
107
    }
108
109
    /**
110
     * @return AddressPart[]
111
     */
112
    public function cc(): array
113
    {
114
        return $this->convertAddressHeader($this->message()->getHeader('Cc'));
115
    }
116
117
    /**
118
     * @return AddressPart[]
119
     */
120
    public function bcc(): array
121
    {
122
        return $this->convertAddressHeader($this->message()->getHeader('Bcc'));
123
    }
124
125
    protected function convertAddressHeader($header): array
126
    {
127
        if ($header instanceof AddressHeader) {
128
            return Collection::make($header->getAddresses())->toArray();
0 ignored issues
show
Bug introduced by
$header->getAddresses() of type ZBateson\MailMimeParser\Header\Part\AddressPart[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::make(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
            return Collection::make(/** @scrutinizer ignore-type */ $header->getAddresses())->toArray();
Loading history...
129
        }
130
131
        return [];
132
    }
133
134
    /**
135
     * @return MessagePart[]
136
     */
137
    public function attachments()
138
    {
139
        return $this->message()->getAllAttachmentParts();
140
    }
141
142
    public function message(): MimeMessage
143
    {
144
        $this->mimeMessage = $this->mimeMessage ?: MimeMessage::from($this->message, true);
0 ignored issues
show
Bug introduced by
The property message does not exist on BeyondCode\Mailbox\InboundEmail. Did you mean message_id?
Loading history...
145
146
        return $this->mimeMessage;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->mimeMessage could return the type ZBateson\MailMimeParser\IMessage which includes types incompatible with the type-hinted return ZBateson\MailMimeParser\Message. Consider adding an additional type-check to rule them out.
Loading history...
147
    }
148
149
    public function reply(Mailable $mailable)
150
    {
151
        if ($mailable instanceof \Illuminate\Mail\Mailable) {
152
            $usesSymfonyMailer = version_compare(app()->version(), '9.0.0', '>');
0 ignored issues
show
introduced by
The method version() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
            $usesSymfonyMailer = version_compare(app()->/** @scrutinizer ignore-call */ version(), '9.0.0', '>');
Loading history...
153
154
            if ($usesSymfonyMailer) {
155
                $mailable->withSymfonyMessage(function (\Symfony\Component\Mime\Email $email) {
156
                    $email->getHeaders()->addIdHeader('In-Reply-To', $this->id());
157
                });
158
            } else {
159
                $mailable->withSwiftMessage(function (\Swift_Message $message) {
0 ignored issues
show
Bug introduced by
The type Swift_Message was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
160
                    $message->getHeaders()->addIdHeader('In-Reply-To', $this->id());
161
                });
162
            }
163
        }
164
165
        return Mail::to($this->headerValue('Reply-To') ?: $this->from())->send($mailable);
166
    }
167
168
    public function forward($recipients)
169
    {
170
        return Mail::send([], [], function ($message) use ($recipients) {
171
            $message->to($recipients)
172
                ->subject($this->subject())
173
                ->setBody($this->body(), $this->message()->getContentType());
174
        });
175
    }
176
177
    public function body(): ?string
178
    {
179
        return $this->isHtml() ? $this->html() : $this->text();
180
    }
181
182
    public function isHtml(): bool
183
    {
184
        return ! empty($this->html());
185
    }
186
187
    public function isText(): bool
188
    {
189
        return ! empty($this->text());
190
    }
191
192
    public function isValid(): bool
193
    {
194
        return $this->from() !== '' && ($this->isText() || $this->isHtml());
195
    }
196
197
    public function isAutoReply($checkCommonSubjects = true): bool
198
    {
199
        if ($this->headerValue('x-autorespond')) {
200
            return true;
201
        }
202
203
        if (in_array($this->headerValue('precedence'), ['auto_reply', 'bulk', 'junk'])) {
204
            return true;
205
        }
206
207
        if (in_array($this->headerValue('x-precedence'), ['auto_reply', 'bulk', 'junk'])) {
208
            return true;
209
        }
210
        if (in_array($this->headerValue('auto-submitted'), ['auto-replied', 'auto-generated'])) {
211
            return true;
212
        }
213
214
        if ($checkCommonSubjects) {
215
            return Str::startsWith($this->subject(), [
216
                'Auto:',
217
                'Automatic reply',
218
                'Autosvar',
219
                'Automatisk svar',
220
                'Automatisch antwoord',
221
                'Abwesenheitsnotiz',
222
                'Risposta Non al computer',
223
                'Automatisch antwoord',
224
                'Auto Response',
225
                'Respuesta automática',
226
                'Fuori sede',
227
                'Out of Office',
228
                'Frånvaro',
229
                'Réponse automatique',
230
            ]);
231
        }
232
233
        return false;
234
    }
235
}
236