2026-05-26 08:14:29 +02:00
< ? php
namespace App\Http\Controllers ;
use App\Models\EmailFolder ;
use App\Models\EmailMessage ;
use App\Models\EmailAttachment ;
use App\Models\EmailSetting ;
2026-05-27 07:29:29 +02:00
use App\Models\SenderAccount ;
2026-05-26 08:14:29 +02:00
use App\Models\MailingList ;
use App\Models\Gruppo ;
use App\Models\Individuo ;
use App\Models\Documento ;
use Illuminate\Http\Request ;
use Illuminate\Support\Facades\Crypt ;
use Illuminate\Support\Facades\Storage ;
use Illuminate\Support\Str ;
class EmailController extends Controller
{
public function index ( Request $request , $folder = 'inbox' )
{
$this -> authorizeRead ( 'email' );
if ( $folder === 'compose' || $folder === 'nuovo' ) {
return $this -> compose ( $request );
}
$folderModel = EmailFolder :: where ( 'type' , $folder ) -> first ();
if ( ! $folderModel ) {
$folderModel = EmailFolder :: where ( 'type' , 'inbox' ) -> first ();
if ( ! $folderModel ) {
$this -> ensureFoldersExist ();
$folderModel = EmailFolder :: where ( 'type' , 'inbox' ) -> first ();
}
}
$query = EmailMessage :: where ( 'email_folder_id' , $folderModel -> id );
if ( $request -> search ) {
$query -> where ( function ( $q ) use ( $request ) {
$q -> where ( 'subject' , 'like' , '%' . $request -> search . '%' )
-> orWhere ( 'from_email' , 'like' , '%' . $request -> search . '%' )
-> orWhere ( 'to_email' , 'like' , '%' . $request -> search . '%' )
-> orWhere ( 'body_text' , 'like' , '%' . $request -> search . '%' );
});
}
$sortField = $request -> sort ? ? 'received_at' ;
$sortDir = $request -> direction ? ? 'desc' ;
$allowedSorts = [ 'subject' , 'received_at' , 'sent_at' , 'from_email' , 'to_email' ];
if ( ! in_array ( $sortField , $allowedSorts )) {
$sortField = 'received_at' ;
}
if ( ! in_array ( $sortDir , [ 'asc' , 'desc' ])) {
$sortDir = 'desc' ;
}
$query -> orderBy ( $sortField === 'sent_at' ? 'received_at' : $sortField , $sortDir );
2026-06-06 23:00:07 +02:00
$perPage = in_array (( int ) $request -> perPage , [ 10 , 20 , 25 , 50 , 100 ]) ? ( int ) $request -> perPage : 20 ;
$messages = $query -> paginate ( $perPage ) -> withQueryString ();
2026-05-26 08:14:29 +02:00
$folders = EmailFolder :: orderBy ( 'sort_order' ) -> get ();
$mailingLists = MailingList :: where ( 'attiva' , true ) -> get ();
$gruppi = Gruppo :: orderBy ( 'nome' ) -> get ();
$emailSettings = EmailSetting :: getActive ();
$syncInterval = $emailSettings ? $emailSettings -> sync_interval_minutes : 0 ;
return view ( 'email.index' , compact ( 'messages' , 'folders' , 'folder' , 'folderModel' , 'mailingLists' , 'gruppi' , 'syncInterval' ));
}
public function show ( $folder , $id )
{
$this -> authorizeRead ( 'email' );
$message = EmailMessage :: with ( 'attachments' ) -> findOrFail ( $id );
if ( ! $message -> is_read ) {
$message -> update ([ 'is_read' => true ]);
}
$folders = EmailFolder :: orderBy ( 'sort_order' ) -> get ();
return view ( 'email.show' , compact ( 'message' , 'folders' , 'folder' ));
}
public function compose ( Request $request )
{
$this -> authorizeWrite ( 'email' );
$folders = EmailFolder :: orderBy ( 'sort_order' ) -> get ();
$mailingLists = MailingList :: where ( 'attiva' , true ) -> get ();
$gruppi = Gruppo :: orderBy ( 'nome' ) -> get ();
$individui = Individuo :: orderBy ( 'cognome' ) -> orderBy ( 'nome' ) -> get ();
2026-05-27 07:29:29 +02:00
$senderAccounts = SenderAccount :: active () -> get ();
2026-05-26 08:14:29 +02:00
$prefill = [];
if ( $request -> reply_to ) {
$original = EmailMessage :: find ( $request -> reply_to );
if ( $original ) {
$prefill [ 'to' ] = $original -> from_email ;
$prefill [ 'subject' ] = 'Re: ' . $original -> subject ;
$prefill [ 'body' ] = " \n \n --- Messaggio originale --- \n " . ( $original -> body_text ? ? $original -> body_html );
}
}
if ( $request -> forward ) {
$original = EmailMessage :: find ( $request -> forward );
if ( $original ) {
$prefill [ 'subject' ] = 'Fwd: ' . $original -> subject ;
$prefill [ 'body' ] = " \n \n --- Messaggio inoltrato --- \n " . ( $original -> body_text ? ? $original -> body_html );
}
}
2026-05-27 07:29:29 +02:00
return view ( 'email.compose' , compact ( 'folders' , 'mailingLists' , 'gruppi' , 'individui' , 'prefill' , 'senderAccounts' ));
2026-05-26 08:14:29 +02:00
}
public function send ( Request $request )
{
$this -> authorizeWrite ( 'email' );
$rules = [
'subject' => 'required' ,
'body' => 'required' ,
];
if ( $request -> destinatario_tipo === 'manuale' || ! $request -> destinatario_tipo ) {
$rules [ 'to' ] = 'required' ;
}
$validated = $request -> validate ( $rules );
$recipients = $this -> resolveRecipients ( $request );
\Illuminate\Support\Facades\Log :: info ( 'Email send attempt' , [
'recipients' => $recipients ,
'recipients_count' => count ( $recipients ),
'subject' => $validated [ 'subject' ],
'destinatario_tipo' => $request -> destinatario_tipo ,
2026-05-27 07:29:29 +02:00
'mittente_id' => $request -> mittente_id ,
2026-05-26 08:14:29 +02:00
]);
if ( empty ( $recipients )) {
return back () -> with ( 'error' , 'Nessun destinatario trovato. Seleziona almeno un destinatario dal menu a tendina o inserisci un indirizzo email manuale.' ) -> withInput ();
}
2026-05-27 07:29:29 +02:00
$useSender = $request -> filled ( 'mittente_id' ) && $request -> mittente_id > 0 ;
if ( $useSender ) {
return $this -> sendWithSender ( $request , $validated , $recipients );
}
$settings = EmailSetting :: getActive ();
if ( ! $settings ) {
return back () -> with ( 'error' , 'Account email non configurato.' );
}
2026-05-26 08:14:29 +02:00
$imapSuccess = false ;
$imapError = null ;
2026-05-27 07:29:29 +02:00
$attachments = $this -> processAttachments ( $request );
2026-05-26 08:14:29 +02:00
foreach ( $recipients as $recipient ) {
try {
2026-05-27 07:29:29 +02:00
$bodyWithSig = $this -> appendSignature ( $validated [ 'body' ], $settings -> getSignature (), $settings -> signature_enabled ? ? false );
$this -> sendViaImap ( $settings , $recipient , $validated [ 'subject' ], $bodyWithSig , $request -> cc , $attachments );
2026-05-26 08:14:29 +02:00
$imapSuccess = true ;
} catch ( \Exception $e ) {
$imapError = $e -> getMessage ();
2026-05-27 07:29:29 +02:00
\Illuminate\Support\Facades\Log :: error ( 'SMTP send error' , [ 'error' => $e -> getMessage ()]);
2026-05-26 08:14:29 +02:00
}
}
try {
2026-05-27 07:29:29 +02:00
$bodyWithSig = $this -> appendSignature ( $validated [ 'body' ], $settings -> getSignature (), $settings -> signature_enabled ? ? false );
$this -> storeSentMessage ( $settings , $recipients , $validated [ 'subject' ], $bodyWithSig , $request -> cc );
2026-05-26 08:14:29 +02:00
} catch ( \Exception $e ) {
\Illuminate\Support\Facades\Log :: error ( 'Store sent message error' , [ 'error' => $e -> getMessage ()]);
return back () -> with ( 'error' , 'Errore nel salvataggio: ' . $e -> getMessage ()) -> withInput ();
}
if ( ! $imapSuccess && $imapError ) {
2026-05-27 07:29:29 +02:00
return redirect () -> route ( 'email.index' , 'sent' ) -> with ( 'warning' , 'Email salvata in Invio ma non inviata via SMTP: ' . $imapError );
2026-05-26 08:14:29 +02:00
}
return redirect () -> route ( 'email.index' , 'sent' ) -> with ( 'success' , 'Email inviata con successo.' );
}
2026-05-27 07:29:29 +02:00
private function sendWithSender ( Request $request , array $validated , array $recipients )
{
$sender = SenderAccount :: findOrFail ( $request -> mittente_id );
$attachmentPaths = $this -> resolveAttachmentPaths ( $request );
$ok = 0 ;
$errors = [];
foreach ( $recipients as $recipient ) {
try {
$sender -> sendEmail ( $recipient , $validated [ 'subject' ], $validated [ 'body' ], $attachmentPaths );
$ok ++ ;
} catch ( \Exception $e ) {
$errors [] = $recipient . ': ' . $e -> getMessage ();
}
}
if ( $sender -> verify_email ) {
$reportSubject = '✅ Invio: ' . $validated [ 'subject' ] . ' - ' . $sender -> email_address ;
$reportBody = " Report invio mittente: { $sender -> email_address } \n "
. " Destinatari totali: " . count ( $recipients ) . " \n "
. " Inviate con successo: { $ok } \n "
. " Fallite: " . count ( $errors ) . " \n " ;
if ( ! empty ( $errors )) {
$reportBody .= " \n Dettaglio errori: \n " . implode ( " \n " , $errors );
}
$sender -> sendReport ( $reportSubject , $reportBody );
}
if ( $ok === 0 && ! empty ( $errors )) {
return redirect () -> route ( 'email.index' , 'sent' ) -> with ( 'error' , 'Invio fallito: ' . implode ( '; ' , $errors ));
}
$message = $ok . ' email inviate con successo tramite "' . $sender -> email_address . '".' ;
if ( ! empty ( $errors )) {
$message .= ' ' . count ( $errors ) . ' fallite.' ;
}
return redirect () -> route ( 'email.index' , 'sent' ) -> with ( 'success' , $message );
}
2026-05-26 08:14:29 +02:00
public function storeDraft ( Request $request )
{
$this -> authorizeWrite ( 'email' );
$settings = EmailSetting :: getActive ();
$folder = EmailFolder :: where ( 'type' , 'drafts' ) -> first ();
$message = EmailMessage :: create ([
'email_folder_id' => $folder -> id ,
'subject' => $request -> subject ? ? '(senza oggetto)' ,
'body_text' => $request -> body ,
'to_email' => $request -> to ,
'is_draft' => true ,
'from_email' => $settings -> email_address ? ? '' ,
'from_name' => $settings -> email_name ? ? '' ,
]);
return response () -> json ([ 'success' => true , 'draft_id' => $message -> id ]);
}
public function bulkAction ( Request $request )
{
try {
$this -> authorizeWrite ( 'email' );
} catch ( \Symfony\Component\HttpKernel\Exception\HttpException $e ) {
return response () -> json ([
'success' => false ,
'message' => 'Non hai i permessi per questa azione'
], 403 );
}
$ids = $request -> input ( 'ids' , []);
$action = $request -> input ( 'action' );
if ( $action === 'empty_trash' ) {
$trashFolder = EmailFolder :: where ( 'type' , 'trash' ) -> first ();
if ( $trashFolder ) {
EmailMessage :: where ( 'email_folder_id' , $trashFolder -> id ) -> delete ();
}
return response () -> json ([ 'success' => true ]);
}
if ( empty ( $ids )) {
return response () -> json ([ 'success' => false , 'message' => 'Nessuna email selezionata' ]);
}
$messages = EmailMessage :: whereIn ( 'id' , $ids ) -> get ();
switch ( $action ) {
case 'mark_read' :
$messages -> each -> update ([ 'is_read' => true ]);
break ;
case 'mark_unread' :
$messages -> each -> update ([ 'is_read' => false ]);
break ;
case 'star' :
$messages -> each -> update ([ 'is_starred' => true ]);
break ;
case 'unstar' :
$messages -> each -> update ([ 'is_starred' => false ]);
break ;
case 'move_trash' :
$trashFolder = EmailFolder :: where ( 'type' , 'trash' ) -> first ();
if ( $trashFolder ) {
$messages -> each -> update ([ 'email_folder_id' => $trashFolder -> id ]);
}
break ;
case 'delete_permanent' :
$messages -> each -> delete ();
break ;
default :
return response () -> json ([ 'success' => false , 'message' => 'Azione non valida' ]);
}
return response () -> json ([ 'success' => true ]);
}
public function markRead ( $id )
{
try {
$this -> authorizeWrite ( 'email' );
} catch ( \Symfony\Component\HttpKernel\Exception\HttpException $e ) {
return response () -> json ([
'success' => false ,
'message' => 'Non hai i permessi per questa azione'
], 403 );
}
$message = EmailMessage :: findOrFail ( $id );
$message -> update ([ 'is_read' => true ]);
return response () -> json ([ 'success' => true ]);
}
public function toggleStar ( $id )
{
try {
$this -> authorizeWrite ( 'email' );
} catch ( \Symfony\Component\HttpKernel\Exception\HttpException $e ) {
return response () -> json ([
'success' => false ,
'message' => 'Non hai i permessi per questa azione'
], 403 );
}
$message = EmailMessage :: findOrFail ( $id );
$message -> update ([ 'is_starred' => ! $message -> is_starred ]);
return response () -> json ([ 'success' => true , 'is_starred' => $message -> is_starred ]);
}
public function destroy ( $id )
{
$this -> authorizeDelete ( 'email' );
$message = EmailMessage :: findOrFail ( $id );
$trashFolder = EmailFolder :: where ( 'type' , 'trash' ) -> first ();
if ( $trashFolder ) {
$message -> update ([ 'is_trash' => true , 'email_folder_id' => $trashFolder -> id ]);
} else {
$message -> delete ();
}
return back () -> with ( 'success' , 'Email spostata nel cestino.' );
}
public function downloadAttachment ( $id )
{
$this -> authorizeRead ( 'email' );
$attachment = EmailAttachment :: findOrFail ( $id );
if ( ! Storage :: exists ( $attachment -> file_path )) {
abort ( 404 );
}
return Storage :: download ( $attachment -> file_path , $attachment -> filename );
}
public function saveAttachmentToDocumenti ( Request $request , $id , $attachmentId )
{
$this -> authorizeWrite ( 'email' );
$this -> authorizeWrite ( 'documenti' );
$attachment = EmailAttachment :: findOrFail ( $attachmentId );
$documento = Documento :: create ([
'nome_file' => $attachment -> filename ,
'file_path' => $attachment -> file_path ,
'tipo' => 'upload' ,
'tipologia' => 'email_attachment' ,
'mime_type' => $attachment -> mime_type ,
'dimensione' => $attachment -> size ,
'visibilita' => null ,
'note' => 'Allegato email: ' . ( $attachment -> message -> subject ? ? '' ),
]);
$attachment -> update ([ 'is_saved_to_documenti' => true , 'documenti_id' => $documento -> id ]);
return back () -> with ( 'success' , 'Allegato salvato nei documenti.' );
}
public function quickSync ()
{
try {
$this -> syncEmails ();
return response () -> json ([ 'success' => true , 'message' => 'Sincronizzazione completata' ]);
} catch ( \Exception $e ) {
return response () -> json ([ 'success' => false , 'message' => $e -> getMessage ()]);
}
}
public function syncEmails ()
{
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: info ( 'Email sync started' );
2026-05-26 08:14:29 +02:00
$settings = EmailSetting :: getActive ();
if ( ! $settings ) {
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: warning ( 'Email sync aborted: no active email configuration' );
2026-05-26 08:14:29 +02:00
throw new \Exception ( 'Nessuna configurazione email attiva' );
}
$this -> ensureFoldersExist ();
$mailbox = $this -> connectImap ( $settings );
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: info ( 'IMAP connection established' , [ 'host' => $settings -> imap_host ]);
2026-05-26 08:14:29 +02:00
$folderMappings = [
'INBOX' => 'inbox' ,
'Sent' => 'sent' ,
'[Gmail]/Sent Mail' => 'sent' ,
'Sent Mail' => 'sent' ,
'Drafts' => 'drafts' ,
'[Gmail]/Drafts' => 'drafts' ,
'Trash' => 'trash' ,
'[Gmail]/Trash' => 'trash' ,
];
2026-05-26 11:47:36 +02:00
$totalProcessed = 0 ;
$totalNew = 0 ;
2026-05-26 08:14:29 +02:00
foreach ( array_keys ( $folderMappings ) as $imapFolderName ) {
try {
$folder = $mailbox -> folders () -> find ( $imapFolderName );
2026-05-26 11:47:36 +02:00
if ( ! $folder ) {
\Illuminate\Support\Facades\Log :: debug ( 'IMAP folder not found' , [ 'folder' => $imapFolderName ]);
continue ;
}
2026-05-26 08:14:29 +02:00
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: info ( 'Processing IMAP folder' , [ 'folder' => $imapFolderName ]);
2026-05-26 08:14:29 +02:00
$messages = $folder -> messages ()
-> withHeaders ()
-> withBody ()
-> withBodyStructure ()
-> limit ( 100 )
-> get ();
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: info ( 'Messages fetched from folder' , [ 'folder' => $imapFolderName , 'count' => count ( $messages )]);
2026-05-26 08:14:29 +02:00
foreach ( $messages as $imapMessage ) {
2026-05-26 11:47:36 +02:00
$totalProcessed ++ ;
$messageId = $imapMessage -> messageId () ? ? uniqid ( 'msg_' );
$existing = EmailMessage :: where ( 'message_id' , $messageId ) -> first ();
if ( $existing ) {
continue ;
}
2026-05-26 08:14:29 +02:00
$this -> processImapMessage ( $imapMessage , $imapFolderName );
2026-05-26 11:47:36 +02:00
$totalNew ++ ;
2026-05-26 08:14:29 +02:00
}
} catch ( \Exception $e ) {
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: error ( 'Error processing IMAP folder' , [ 'folder' => $imapFolderName , 'error' => $e -> getMessage ()]);
2026-05-26 08:14:29 +02:00
continue ;
}
}
2026-05-26 11:47:36 +02:00
\Illuminate\Support\Facades\Log :: info ( 'Email sync completed' , [
'processed' => $totalProcessed ,
'new' => $totalNew ,
]);
2026-05-26 08:14:29 +02:00
return true ;
}
private function resolveRecipients ( Request $request )
{
$recipients = [];
if ( $request -> destinatario_tipo === 'individui' && $request -> individui ) {
$individui = Individuo :: whereIn ( 'id' , $request -> individui ) -> get ();
foreach ( $individui as $ind ) {
$emails = $ind -> contatti () -> where ( 'tipo' , 'email' ) -> pluck ( 'valore' ) -> toArray ();
$recipients = array_merge ( $recipients , $emails );
}
} elseif ( $request -> destinatario_tipo === 'gruppo' && $request -> gruppo_id ) {
$gruppo = Gruppo :: with ( 'individui.contatti' ) -> find ( $request -> gruppo_id );
foreach ( $gruppo -> individui as $ind ) {
$emails = $ind -> contatti () -> where ( 'tipo' , 'email' ) -> pluck ( 'valore' ) -> toArray ();
$recipients = array_merge ( $recipients , $emails );
}
} elseif ( $request -> destinatario_tipo === 'mailing_list' && $request -> mailing_list_id ) {
$list = MailingList :: with ( 'contatti.individuo' ) -> find ( $request -> mailing_list_id );
foreach ( $list -> contatti as $contact ) {
if ( $contact -> opt_in && $contact -> individuo ) {
$emails = $contact -> individuo -> contatti () -> where ( 'tipo' , 'email' ) -> pluck ( 'valore' ) -> toArray ();
$recipients = array_merge ( $recipients , $emails );
}
}
} elseif ( $request -> to ) {
$recipients = is_array ( $request -> to ) ? $request -> to : [ $request -> to ];
}
return array_filter ( array_unique ( $recipients ));
}
private function appendSignature ( string $body , ? string $signature , bool $enabled ) : string
{
if ( empty ( $signature ) || ! $enabled ) {
return $body ;
}
return $body . " \n \n --- \n " . $signature ;
}
2026-05-27 07:29:29 +02:00
private function sendViaImap ( $settings , $to , $subject , $body , $cc = null , array $attachmentPaths = [])
2026-05-26 08:14:29 +02:00
{
\Illuminate\Support\Facades\Log :: info ( 'sendViaSMTP start' , [ 'to' => $to , 'subject' => $subject ]);
$smtpPassword = $settings -> getDecryptedSmtpPassword () ? ? $settings -> getDecryptedPassword ();
$encryption = $settings -> smtp_encryption ? ? 'tls' ;
$scheme = ( $encryption === 'ssl' ) ? 'smtps' : 'smtp' ;
$dsn = sprintf (
'%s://%s:%s@%s:%d?encryption=%s' ,
$scheme ,
rawurlencode ( $settings -> smtp_username ? ? $settings -> email_address ),
rawurlencode ( $smtpPassword ),
$settings -> smtp_host ? ? 'smtp.gmail.com' ,
$settings -> smtp_port ? ? 587 ,
$encryption
);
try {
$transport = \Symfony\Component\Mailer\Transport :: fromDsn ( $dsn );
$mailer = new \Symfony\Component\Mailer\Mailer ( $transport );
$fromName = $settings -> email_name ? ? '' ;
if ( filter_var ( $fromName , FILTER_VALIDATE_EMAIL )) {
$fromName = 'Glastree' ;
} else {
$fromName = preg_replace ( '/[^\p{L}\p{N}\s]/u' , '' , $fromName );
$fromName = trim ( $fromName );
if ( empty ( $fromName )) {
$fromName = 'Glastree' ;
}
}
$fromAddress = \Symfony\Component\Mime\Address :: create ( $settings -> email_address , $fromName );
$email = ( new \Symfony\Component\Mime\Email ())
-> from ( $fromAddress )
-> to ( $to )
-> subject ( $subject )
2026-05-27 07:29:29 +02:00
-> text ( $body );
2026-05-26 08:14:29 +02:00
if ( $cc ) {
$email -> cc ( $cc );
}
2026-05-27 07:29:29 +02:00
foreach ( $attachmentPaths as $attachmentPath ) {
$email -> attachFromPath ( $attachmentPath );
}
2026-05-26 08:14:29 +02:00
$mailer -> send ( $email );
\Illuminate\Support\Facades\Log :: info ( 'Email sent via SMTP successfully' );
return true ;
} catch ( \Exception $e ) {
\Illuminate\Support\Facades\Log :: error ( 'SMTP send error' , [ 'error' => $e -> getMessage ()]);
throw new \Exception ( 'Errore invio SMTP: ' . $e -> getMessage ());
}
}
2026-05-27 07:29:29 +02:00
private function processAttachments ( Request $request ) : array
{
$paths = [];
if ( $request -> hasFile ( 'allegati' )) {
foreach ( $request -> file ( 'allegati' ) as $file ) {
if ( $file -> isValid ()) {
$paths [] = $file -> getRealPath ();
}
}
}
return $paths ;
}
private function resolveAttachmentPaths ( Request $request ) : array
{
$paths = [];
if ( $request -> hasFile ( 'allegati' )) {
foreach ( $request -> file ( 'allegati' ) as $file ) {
if ( $file -> isValid ()) {
$paths [] = $file -> getRealPath ();
}
}
}
$documentIds = $request -> input ( 'documenti_selezionati' );
if ( ! empty ( $documentIds )) {
$ids = is_array ( $documentIds ) ? $documentIds : explode ( ',' , $documentIds );
$docs = Documento :: whereIn ( 'id' , $ids ) -> get ();
foreach ( $docs as $doc ) {
if ( $doc -> file_path && \Illuminate\Support\Facades\Storage :: exists ( $doc -> file_path )) {
$paths [] = \Illuminate\Support\Facades\Storage :: path ( $doc -> file_path );
}
}
}
return $paths ;
}
2026-05-26 08:14:29 +02:00
private function storeSentMessage ( $settings , $recipients , $subject , $body , $cc = null )
{
$sentFolder = EmailFolder :: where ( 'type' , 'sent' ) -> first ();
$bodyWithSignature = $this -> appendSignature ( $body , $settings -> getSignature (), $settings -> signature_enabled ? ? false );
$message = EmailMessage :: create ([
'email_folder_id' => $sentFolder -> id ,
'subject' => $subject ,
'body_text' => $bodyWithSignature ,
'from_email' => $settings -> email_address ,
'from_name' => $settings -> email_name ,
'to_email' => implode ( ', ' , $recipients ),
'cc' => $cc ,
'is_sent' => true ,
'sent_at' => now (),
'is_read' => true ,
]);
return $message ;
}
private function connectImap ( $settings )
{
if ( ! $settings -> imap_host || ! $settings -> imap_username ) {
throw new \Exception ( 'Configurazione IMAP non valida' );
}
$encryption = match ( $settings -> imap_encryption ) {
'ssl' => 'ssl' ,
'tls' => 'tls' ,
default => 'none' ,
};
return new \DirectoryTree\ImapEngine\Mailbox ([
'host' => $settings -> imap_host ,
'port' => $settings -> imap_port ? ? 993 ,
'encryption' => $encryption ,
'validate_cert' => false ,
'username' => $settings -> imap_username ,
'password' => $settings -> getDecryptedPassword (),
]);
}
private function processImapMessage ( $imapMessage , $imapFolder )
{
$folderType = match ( $imapFolder ) {
'INBOX' , 'inbox' => 'inbox' ,
'Sent' , 'sent' , '[Gmail]/Sent Mail' => 'sent' ,
'Drafts' , 'drafts' , '[Gmail]/Drafts' => 'drafts' ,
'Trash' , 'trash' , '[Gmail]/Trash' => 'trash' ,
'Archive' , 'archive' , '[Gmail]/All Mail' => 'archive' ,
'Starred' , 'starred' , '[Gmail]/Starred' => 'starred' ,
default => 'inbox' ,
};
$folder = EmailFolder :: where ( 'type' , $folderType ) -> first ();
if ( ! $folder ) return ;
$messageId = $imapMessage -> messageId () ? ? uniqid ( 'msg_' );
$existing = EmailMessage :: where ( 'message_id' , $messageId ) -> first ();
if ( $existing ) return ;
$from = $imapMessage -> from ();
$to = $imapMessage -> to ();
$message = EmailMessage :: create ([
'email_folder_id' => $folder -> id ,
'message_id' => $messageId ,
'subject' => $imapMessage -> subject () ? ? '(senza oggetto)' ,
'from_name' => $from ? -> name () ? ? '' ,
'from_email' => $from ? -> email () ? ? '' ,
'to_email' => count ( $to ) > 0 ? $to [ 0 ] -> email () : '' ,
'body_text' => $imapMessage -> text () ? ? '' ,
'body_html' => $imapMessage -> html () ? ? '' ,
'is_read' => $imapMessage -> isSeen () ? ? false ,
'is_starred' => $imapMessage -> isFlagged () ? ? false ,
'is_sent' => $folderType === 'sent' ,
'received_at' => $imapMessage -> date () ? ? now (),
'imap_uid' => $imapMessage -> uid () ? ? null ,
]);
$attachments = $imapMessage -> attachments ();
if ( $attachments && count ( $attachments ) > 0 ) {
foreach ( $attachments as $attachment ) {
$filename = $attachment -> filename () ? ? 'attachment' ;
$contents = $attachment -> contents ();
$path = 'email-attachments/' . Str :: uuid () . '_' . $filename ;
Storage :: put ( $path , $contents );
EmailAttachment :: create ([
'email_message_id' => $message -> id ,
'filename' => $filename ,
'mime_type' => $attachment -> contentType () ? ? 'application/octet-stream' ,
'size' => strlen ( $contents ),
'file_path' => $path ,
]);
}
}
}
private function ensureFoldersExist ()
{
$systemFolders = EmailFolder :: getSystemFolders ();
foreach ( $systemFolders as $folder ) {
EmailFolder :: updateOrCreate (
[ 'type' => $folder [ 'type' ]],
$folder
);
}
}
}