'array', 'cc' => 'array', 'bcc' => 'array', 'attachments' => 'array', // If attachments are stored as a JSON field 'timestamp' => 'datetime', // Cast timestamp to Carbon instance ]; public static function connectMailBox($imap = null): \Ddeboer\Imap\ConnectionInterface { if ($imap === null) { $imap = json_decode(config('app.settings.imap_settings'), true); } $flags = $imap['protocol'] . '/' . $imap['encryption']; if ($imap['validate_cert']) { $flags = $flags . '/validate-cert'; } else { $flags = $flags . '/novalidate-cert'; } $server = new Server($imap['host'], $imap['port'], $flags); return $server->authenticate($imap['username'], $imap['password']); } public static function fetchProcessStoreEmail() { try { $allowed = explode(',', 'doc,docx,xls,xlsx,ppt,pptx,xps,pdf,dxf,ai,psd,eps,ps,svg,ttf,zip,rar,tar,gzip,mp3,mpeg,wav,ogg,jpeg,jpg,png,gif,bmp,tif,webm,mpeg4,3gpp,mov,avi,mpegs,wmv,flx,txt'); $connection = \App\Models\Email::connectMailBox(); $mailbox = $connection->getMailbox('INBOX'); $messages = $mailbox->getMessages(); $result = ''; foreach ($messages as $message) { $sender = $message->getFrom(); $date = $message->getDate(); if (!$date) { $date = new \DateTime(); if ($message->getHeaders()->get('udate')) { $date->setTimestamp($message->getHeaders()->get('udate')); } } $content = ''; $contentText = ''; $html = $message->getBodyHtml(); $text = $message->getBodyText(); if ($text) { $contentText = str_replace('', $text)); } $obj = []; $to = $message->getHeaders()->get('To') ? array_map(function ($entry) { return $entry->mailbox . '@' . $entry->host; }, $message->getHeaders()->get('To')) : []; $cc = $message->getHeaders()->get('Cc') ? array_map(function ($entry) { return $entry->mailbox . '@' . $entry->host; }, $message->getHeaders()->get('Cc')) : []; $bcc = $message->getHeaders()->get('Bcc') ? array_map(function ($entry) { return $entry->mailbox . '@' . $entry->host; }, $message->getHeaders()->get('Bcc')) : []; $messageTime = $message->getDate(); $utcTime = CarbonImmutable::instance($messageTime)->setTimezone('UTC')->toDateTimeString(); $obj['id'] = $message->getNumber(); $obj['to'] = $to; $obj['cc'] = $cc; $obj['bcc'] = $bcc; $obj['subject'] = $message->getSubject(); $obj['sender_name'] = $sender->getName(); $obj['sender_email'] = $sender->getAddress(); $obj['timestamp'] = $utcTime; $obj['size'] = $message->getSize(); //$obj['date'] = $date->format(json_decode(config('app.settings.configuration_settings'))->date_format ?? 'd M Y h:i A'); $obj['content'] = $content; $obj['contentText'] = $contentText; $obj['attachments'] = []; //$obj['raw_headers'] = $message->getRawHeaders(); //$obj['raw_body'] = $message->getRawMessage(); if ($message->hasAttachments()) { $attachments = $message->getAttachments(); $directory = './tmp/attachments/' . $obj['id'] . '/'; is_dir($directory) || mkdir($directory, 0777, true); foreach ($attachments as $attachment) { $filenameArray = explode('.', $attachment->getFilename()); $extension = $filenameArray[count($filenameArray) - 1]; if (in_array($extension, $allowed)) { if (!file_exists($directory . $attachment->getFilename())) { try { file_put_contents( $directory . $attachment->getFilename(), $attachment->getDecodedContent() ); } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error($e->getMessage()); } } if ($attachment->getFilename() !== 'undefined') { $url = config('app.settings.app_base_url') . str_replace('./', '/', $directory . $attachment->getFilename()); $structure = $attachment->getStructure(); if (isset($structure->id) && str_contains($obj['content'], trim($structure->id, '<>'))) { $obj['content'] = str_replace('cid:' . trim($structure->id, '<>'), $url, $obj['content']); } $obj['attachments'][] = [ 'file' => $attachment->getFilename(), 'url' => $url ]; } } } } $response['data'][] = $obj; if (!$message->isSeen()) { $initialData = $obj; $data = [ 'message_id' => Carbon::parse($utcTime)->format('Ymd').$initialData['id'], 'subject' => $initialData['subject'], 'from_name' => $initialData['sender_name'], 'from_email' => $initialData['sender_email'], 'to' => $initialData['to'], 'cc' => $initialData['cc'], 'bcc' => $initialData['bcc'], 'timestamp' => $initialData['timestamp'], // store in UTC 'body_text' => $initialData['contentText'], 'body_html' => $initialData['content'], 'is_seen' => false, 'is_flagged' => false, 'size' => $initialData['size'], 'mailbox' => 'INBOX', 'raw_headers' => null, 'raw_body' => null, 'attachments' => $initialData['attachments'], ]; try { self::create($data); $checkAction = config('app.move_or_delete'); if ($checkAction != null) { if ($checkAction == 'delete') { $message->delete(); } else { $newMailBox = $connection->getMailbox($checkAction); $message->move($newMailBox); } } } catch (\Exception $e) { // \Log::error($e); } } else { $initialData = $obj; $data = [ 'message_id' => Carbon::parse($utcTime)->format('Ymd').$initialData['id'], 'subject' => $initialData['subject'], 'from_name' => $initialData['sender_name'], 'from_email' => $initialData['sender_email'], 'to' => $initialData['to'], 'cc' => $initialData['cc'], 'bcc' => $initialData['bcc'], 'timestamp' => $initialData['timestamp'], // store in UTC 'body_text' => $initialData['contentText'], 'body_html' => $initialData['content'], 'is_seen' => true, 'is_flagged' => false, 'size' => $initialData['size'], 'mailbox' => 'INBOX', 'raw_headers' => null, 'raw_body' => null, 'attachments' => $initialData['attachments'], ]; try { self::create($data); $checkAction = config('app.move_or_delete'); if ($checkAction != null) { if ($checkAction == 'delete') { $message->delete(); } else { $newMailBox = $connection->getMailbox($checkAction); $message->move($newMailBox); } } } catch (\Exception $e) { // \Log::error($e); } } } $connection->expunge(); } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error($e->getMessage()); } } public static function fetchEmailFromDB($email) { $validator = Validator::make(['email' => $email], [ 'email' => 'required|email' ]); if ($validator->fails()) { return []; } return self::whereJsonContains('to', $email)->orderBy('timestamp', 'desc')->get(); } public static function parseEmail($email, $deleted = []): array { $messages = self::fetchEmailFromDB($email); $limit = json_decode(config('app.settings.configuration_settings'))->fetch_messages_limit ?? 15; $count = 1; $response = [ 'data' => [], 'notifications' => [] ]; foreach ($messages as $message) { if (in_array($message['message_id'], $deleted)) { // If it exists, delete the matching record from the 'emails' table Email::where('message_id', $message['message_id'])->delete(); continue; } $blocked = false; $timestamp = $message['timestamp']; $carbonTimestamp = Carbon::parse($timestamp, 'UTC'); $obj = []; $obj['subject'] = $message['subject']; $obj['sender_name'] = $message['from_name']; $obj['sender_email'] = $message['from_email']; $obj['timestamp'] = $message['timestamp']; $obj['date'] = $carbonTimestamp->format('d M Y h:i A'); $obj['datediff'] = $carbonTimestamp->diffForHumans(Carbon::now('UTC')); $obj['id'] = $message['message_id']; $obj['content'] = $message['body_html']; $obj['contentText'] = $message['body_text']; $obj['attachments'] = []; $obj['is_seen'] = $message['is_seen']; $obj['sender_photo'] = self::chooseColor(strtoupper(substr($message['from_name'] ?: $message['from_email'], 0, 1) )); $domain = explode('@', $obj['sender_email'])[1]; $blocked = in_array($domain, json_decode(config('app.settings.configuration_settings'))->blocked_domains); if ($blocked) { $obj['subject'] = __('Blocked'); $obj['content'] = __('Emails from') . ' ' . $domain . ' ' . __('are blocked by Admin'); $obj['contentText'] = __('Emails from') . ' ' . $domain . ' ' . __('are blocked by Admin'); } if (count($message['attachments']) > 0 && !$blocked) { $obj['attachments'] = $message['attachments']; } $response['data'][] = $obj; if (!$message['is_seen']) { $response['notifications'][] = [ 'subject' => $obj['subject'], 'sender_name' => $obj['sender_name'], 'sender_email' => $obj['sender_email'] ]; if (config('app.zemail_log')) { file_put_contents(storage_path('logs/zemail.csv'), request()->ip() . "," . date("Y-m-d h:i:s a") . "," . $obj['sender_email'] . "," . $email . PHP_EOL, FILE_APPEND); } } Email::where('message_id', $message['message_id'])->update(['is_seen' => true]); if (++$count > $limit) { break; } } return $response; } public static function deleteBulkAttachments() { $dir = public_path('/tmp/attachments'); try { if (File::exists($dir)) { File::cleanDirectory($dir); } } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error($e->getMessage()); } } public static function deleteBulkMailboxes() { $foldersToClean = ['INBOX', 'ZDUMP', 'Trash']; $cutoff = (new \DateTime())->modify('-3 hours'); $totalDeleted = 0; $maxToDelete = 100; foreach ($foldersToClean as $folderName) { $connection = \App\Models\Email::connectMailBox(); if ($totalDeleted >= $maxToDelete) { $connection->expunge(); break; } if ($connection->hasMailbox($folderName)) { $mailbox = $connection->getMailbox($folderName); $messages = $mailbox->getMessages(new Since(new \DateTime('today'))); foreach ($messages as $message) { if ($totalDeleted >= $maxToDelete) { $connection->expunge(); break 2; // exit both loops } $messageDate = $message->getDate(); if ($messageDate < $cutoff) { $message->delete(); $totalDeleted++; } } } $connection->expunge(); } return "$totalDeleted message(s) deleted from Trash and ZDUMP."; } public static function deleteMessagesFromDB() { $cutoff = Carbon::now('UTC')->subHours(6)->toDateTimeString(); $count = count(self::where('timestamp', '<', $cutoff) ->orderBy('timestamp', 'desc') ->get()); if ($count > 0) { self::where('timestamp', '<', $cutoff)->delete(); return "$count old message(s) deleted from the database."; } return "No messages older than 6 hours found."; } public static function mailToDBStatus(): bool { $latestRecord = self::orderBy('timestamp', 'desc')->first(); if (!$latestRecord) { return false; } $currentTime = Carbon::now('UTC'); $lastRecordTime = Carbon::parse($latestRecord->timestamp); if ($lastRecordTime->diffInMinutes($currentTime) < 5) { return true; } return false; } public static function cleanMailbox(): string { $foldersToClean = ['INBOX']; $cutoff = (new \DateTime())->modify('-6 hours'); $totalDeleted = 0; $maxToDelete = 100; foreach ($foldersToClean as $folderName) { $connection = \App\Models\Email::connectMailBox(); if ($totalDeleted >= $maxToDelete) { $connection->expunge(); break; } if ($connection->hasMailbox($folderName)) { $mailbox = $connection->getMailbox($folderName); $messages = $mailbox->getMessages(new Since(new \DateTime('today'))); foreach ($messages as $message) { if ($totalDeleted >= $maxToDelete) { $connection->expunge(); break 2; // exit both loops } $messageDate = $message->getDate(); if ($messageDate < $cutoff) { $message->delete(); $totalDeleted++; } } } $connection->expunge(); } return "$totalDeleted message(s) deleted from Trash and ZDUMP."; } }