email); $own = [strtolower((string) Settings::get('reply_to')), strtolower((string) Settings::get('imap_username')), strtolower((string) admin_setting('email_from_address', config('mail.from.address')))]; if (!filter_var($sender, FILTER_VALIDATE_EMAIL) || in_array($sender, $own, true) || preg_match('/(?:mailer-daemon|postmaster|no-?reply)@/i', $sender) || (isset($headers['auto-submitted']) && strtolower($headers['auto-submitted']) !== 'no') || isset($headers['list-id']) || isset($headers['x-auto-response-suppress']) || in_array(strtolower($headers['precedence'] ?? ''), ['bulk', 'list', 'junk'], true) || trim($headers['return-path'] ?? '') === '<>') { return null; } // Binding is optional; never send to an arbitrary incoming Reply-To address. $user = User::byEmail($sender)->first(); if ($user?->banned) { return null; } if (MailMessage::where('parent_id', $incoming->id)->exists()) { return null; } $day = now()->timezone('Asia/Shanghai')->startOfDay(); if (MailMessage::where('direction', 'out')->where('purpose', 'auto_reply')->where('email', $sender) ->where('created_at', '>=', $day->copy()->timezone(config('app.timezone', 'UTC')))->exists()) { return null; } $outHeaders = ['Auto-Submitted' => 'auto-replied', 'X-Auto-Response-Suppress' => 'All']; if (preg_match('/^<[^<>\r\n]{1,250}>$/', $headers['message-id'] ?? '')) { $outHeaders['In-Reply-To'] = $headers['message-id']; $outHeaders['References'] = $headers['message-id']; } $html = view('mail.default.notify', array_replace([ 'name' => admin_setting('app_name', 'dpl'), 'url' => admin_setting('app_url', config('app.url')), 'content' => e((string) Settings::get('auto_reply_text')), ], Branding::values()))->render(); // The unique key reserves one reply per sender/day, including concurrent arrivals. $reply = MailMessage::firstOrCreate(['message_key' => 'reply:' . $day->format('Y-m-d') . ':' . hash('sha256', $sender)], [ 'parent_id' => $incoming->id, 'purpose' => 'auto_reply', 'user_id' => $user?->id, 'email' => $sender, 'subject' => Settings::get('auto_reply_subject'), 'expires_at' => $day->copy()->addDay()->timezone(config('app.timezone', 'UTC')), 'payload' => ['to' => $sender, 'subject' => Settings::get('auto_reply_subject'), 'html' => $html, 'attachments' => Branding::attachments(), 'reply_to' => Settings::get('reply_to'), 'headers' => $outHeaders], ]); return $reply->wasRecentlyCreated ? $reply : null; } }