296 lines
11 KiB
PHP
296 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Webklex\PHPIMAP\ClientManager;
|
|
use Webklex\PHPIMAP\Client;
|
|
use Webklex\PHPIMAP\Folder;
|
|
|
|
class ImapService
|
|
{
|
|
private ClientManager $manager;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->manager = new ClientManager(config('imap'));
|
|
}
|
|
|
|
public function client(string $email, string $password): Client
|
|
{
|
|
$client = $this->manager->make([
|
|
'host' => config('imap.accounts.webmail.host', '127.0.0.1'),
|
|
'port' => config('imap.accounts.webmail.port', 143),
|
|
'protocol' => 'imap',
|
|
'encryption' => config('imap.accounts.webmail.encryption', 'notls'),
|
|
'validate_cert' => false,
|
|
'username' => $email,
|
|
'password' => $password,
|
|
]);
|
|
|
|
$client->connect();
|
|
return $client;
|
|
}
|
|
|
|
public function folders(Client $client): array
|
|
{
|
|
$order = ['INBOX' => 0, 'Drafts' => 1, 'Sent' => 2, 'Junk' => 3, 'Trash' => 4, 'Archive' => 5];
|
|
|
|
$folders = [];
|
|
foreach ($client->getFolders(false) as $folder) {
|
|
$folders[] = [
|
|
'name' => $folder->name,
|
|
'full_name' => $folder->full_name,
|
|
'path' => $folder->path,
|
|
];
|
|
}
|
|
|
|
usort($folders, fn ($a, $b) =>
|
|
($order[$a['name']] ?? 99) <=> ($order[$b['name']] ?? 99)
|
|
);
|
|
|
|
return $folders;
|
|
}
|
|
|
|
public function starredMessages(Client $client, int $page = 1, int $perPage = 25): array
|
|
{
|
|
$rows = [];
|
|
foreach ($client->getFolders(false) as $folder) {
|
|
if (in_array($folder->name, ['Trash', 'Spam', 'Junk'])) continue;
|
|
try {
|
|
$msgs = $folder->query()->where('FLAGGED')->setFetchFlags(true)->setFetchBody(false)->get();
|
|
foreach ($msgs as $msg) {
|
|
$toAttr = $msg->getTo();
|
|
$toAddr = '';
|
|
foreach ($toAttr ?? [] as $r) { $toAddr = (string)($r->mail ?? ''); break; }
|
|
$rows[] = [
|
|
'uid' => $msg->getUid(),
|
|
'folder' => $folder->path,
|
|
'subject' => (string) $msg->getSubject(),
|
|
'from' => (string) ($msg->getFrom()[0]->mail ?? ''),
|
|
'from_name' => (string) ($msg->getFrom()[0]->personal ?? ''),
|
|
'to' => $toAddr,
|
|
'date' => $msg->getDate()?->toDate()?->toDateTimeString(),
|
|
'seen' => $msg->getFlags()->has('seen'),
|
|
'flagged' => true,
|
|
'has_attachments' => count($msg->getAttachments()) > 0,
|
|
];
|
|
}
|
|
} catch (\Throwable) {}
|
|
}
|
|
|
|
usort($rows, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
|
|
$total = count($rows);
|
|
$paged = array_slice($rows, ($page - 1) * $perPage, $perPage);
|
|
|
|
return ['messages' => $paged, 'total' => $total, 'page' => $page, 'perPage' => $perPage];
|
|
}
|
|
|
|
public function messages(Client $client, string $folderPath = 'INBOX', int $page = 1, int $perPage = 25): array
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$query = $folder->query()->all();
|
|
$total = $query->count();
|
|
$messages = $query
|
|
->setFetchFlags(true)
|
|
->setFetchBody(false)
|
|
->limit($perPage, ($page - 1) * $perPage)
|
|
->get();
|
|
|
|
$rows = [];
|
|
foreach ($messages as $msg) {
|
|
$toAttr = $msg->getTo();
|
|
$toAddr = '';
|
|
foreach ($toAttr ?? [] as $r) { $toAddr = (string)($r->mail ?? ''); break; }
|
|
|
|
$rows[] = [
|
|
'uid' => $msg->getUid(),
|
|
'subject' => (string) $msg->getSubject(),
|
|
'from' => (string) ($msg->getFrom()[0]->mail ?? ''),
|
|
'from_name' => (string) ($msg->getFrom()[0]->personal ?? ''),
|
|
'to' => $toAddr,
|
|
'date' => $msg->getDate()?->toDate()?->toDateTimeString(),
|
|
'seen' => $msg->getFlags()->has('seen'),
|
|
'flagged' => $msg->getFlags()->has('flagged'),
|
|
'has_attachments' => count($msg->getAttachments()) > 0,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'messages' => array_reverse($rows),
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'perPage' => $perPage,
|
|
];
|
|
}
|
|
|
|
public function getMessage(Client $client, string $folderPath, int $uid): array
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$message = $folder->query()->getMessageByUid($uid);
|
|
|
|
if (! $message) {
|
|
return [];
|
|
}
|
|
|
|
$html = (string) $message->getHtmlBody();
|
|
$text = (string) $message->getTextBody();
|
|
|
|
$froms = $message->getFrom();
|
|
$from = $froms[0] ?? null;
|
|
|
|
$to = [];
|
|
foreach ($message->getTo() ?? [] as $r) {
|
|
$to[] = (string) ($r->mail ?? '');
|
|
}
|
|
|
|
$attachments = [];
|
|
foreach ($message->getAttachments() as $att) {
|
|
$attachments[] = [
|
|
'name' => $att->getName(),
|
|
'type' => $att->getMimeType(),
|
|
'size' => $att->getSize(),
|
|
];
|
|
}
|
|
|
|
return [
|
|
'uid' => $message->getUid(),
|
|
'subject' => (string) $message->getSubject(),
|
|
'from' => (string) ($from->mail ?? ''),
|
|
'from_name' => (string) ($from->personal ?? ''),
|
|
'to' => implode(', ', $to),
|
|
'date' => $message->getDate()?->toDate()?->toDateTimeString(),
|
|
'html' => $html,
|
|
'text' => $text,
|
|
'attachments' => $attachments,
|
|
'seen' => $message->getFlags()->has('seen'),
|
|
'flagged' => $message->getFlags()->has('flagged'),
|
|
];
|
|
}
|
|
|
|
public function sendMessage(string $from, string $password, string $to, string $subject, string $body, array $attachments = [], bool $isHtml = false): void
|
|
{
|
|
$attachFn = function ($msg) use ($attachments) {
|
|
foreach ($attachments as $att) {
|
|
$diskPath = \Illuminate\Support\Facades\Storage::path($att['path']);
|
|
if (! file_exists($diskPath)) continue;
|
|
$msg->attachData(
|
|
file_get_contents($diskPath),
|
|
$att['name'],
|
|
['mime' => $att['mime'] ?: 'application/octet-stream'],
|
|
);
|
|
}
|
|
};
|
|
|
|
if ($isHtml) {
|
|
\Illuminate\Support\Facades\Mail::html($body, function ($msg) use ($from, $to, $subject, $attachFn) {
|
|
$msg->from($from)->to($to)->subject($subject);
|
|
$attachFn($msg);
|
|
});
|
|
} else {
|
|
\Illuminate\Support\Facades\Mail::raw($body, function ($msg) use ($from, $to, $subject, $attachFn) {
|
|
$msg->from($from)->to($to)->subject($subject);
|
|
$attachFn($msg);
|
|
});
|
|
}
|
|
}
|
|
|
|
public function saveDraft(Client $client, string $from, string $to, string $subject, string $body): void
|
|
{
|
|
$date = now()->format('D, d M Y H:i:s O');
|
|
$raw = "Date: {$date}\r\n"
|
|
. "From: {$from}\r\n"
|
|
. "To: {$to}\r\n"
|
|
. "Subject: {$subject}\r\n"
|
|
. "MIME-Version: 1.0\r\n"
|
|
. "Content-Type: text/plain; charset=UTF-8\r\n"
|
|
. "Content-Transfer-Encoding: 8bit\r\n"
|
|
. "\r\n"
|
|
. $body;
|
|
|
|
$folder = $client->getFolder('Drafts');
|
|
$folder->appendMessage($raw, ['\\Draft', '\\Seen'], now());
|
|
}
|
|
|
|
public function moveMessage(Client $client, string $fromFolder, int $uid, string $toFolder): void
|
|
{
|
|
$folder = $client->getFolder($fromFolder);
|
|
$message = $folder->query()->getMessageByUid($uid);
|
|
$message?->move($toFolder, true);
|
|
}
|
|
|
|
public function toggleFlag(Client $client, string $folderPath, int $uid): bool
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$message = $folder->query()->getMessageByUid($uid);
|
|
if (! $message) return false;
|
|
|
|
$flagged = $message->getFlags()->has('flagged');
|
|
if ($flagged) {
|
|
$message->unsetFlag('Flagged');
|
|
return false;
|
|
}
|
|
$message->setFlag('Flagged');
|
|
return true;
|
|
}
|
|
|
|
public function deleteMessage(Client $client, string $folderPath, int $uid): void
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$message = $folder->query()->getMessageByUid($uid);
|
|
$message?->delete(true);
|
|
}
|
|
|
|
public function markSeen(Client $client, string $folderPath, int $uid): void
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$message = $folder->query()->getMessageByUid($uid);
|
|
$message?->setFlag('Seen');
|
|
}
|
|
|
|
public function markUnseen(Client $client, string $folderPath, int $uid): void
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$message = $folder->query()->getMessageByUid($uid);
|
|
$message?->unsetFlag('Seen');
|
|
}
|
|
|
|
public function searchMessages(Client $client, string $folderPath, string $query, int $limit = 15): array
|
|
{
|
|
$folder = $client->getFolder($folderPath);
|
|
$results = [];
|
|
$seen = [];
|
|
|
|
foreach (['SUBJECT', 'FROM'] as $criterion) {
|
|
try {
|
|
$msgs = $folder->query()
|
|
->where($criterion, $query)
|
|
->setFetchFlags(true)
|
|
->setFetchBody(false)
|
|
->get();
|
|
foreach ($msgs as $msg) {
|
|
$uid = $msg->getUid();
|
|
if (isset($seen[$uid])) continue;
|
|
$seen[$uid] = true;
|
|
$froms = $msg->getFrom();
|
|
$from = $froms[0] ?? null;
|
|
$to = [];
|
|
foreach ($msg->getTo() ?? [] as $r) { $to[] = (string)($r->mail ?? ''); }
|
|
$results[] = [
|
|
'uid' => $uid,
|
|
'subject' => (string) $msg->getSubject(),
|
|
'from' => (string) ($from->mail ?? ''),
|
|
'from_name' => (string) ($from->personal ?? ''),
|
|
'to' => implode(', ', $to),
|
|
'date' => $msg->getDate()?->toDate()?->toDateTimeString(),
|
|
'seen' => $msg->getFlags()->has('seen'),
|
|
'flagged' => $msg->getFlags()->has('flagged'),
|
|
];
|
|
if (count($results) >= $limit) break 2;
|
|
}
|
|
} catch (\Throwable) {}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
}
|