vendor/uvdesk/mailbox-component/Services/MailboxService.php line 319

Open in your IDE?
  1. <?php
  2. namespace Webkul\UVDesk\MailboxBundle\Services;
  3. use PhpMimeMailParser\Parser;
  4. use Symfony\Component\Yaml\Yaml;
  5. use Doctrine\ORM\EntityManagerInterface;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\HttpFoundation\RequestStack;
  9. use Webkul\UVDesk\CoreFrameworkBundle\Entity\User;
  10. use Symfony\Component\EventDispatcher\GenericEvent;
  11. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Ticket;
  12. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Thread;
  13. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Website;
  14. use Webkul\UVDesk\MailboxBundle\Utils\Mailbox\Mailbox;
  15. use Webkul\UVDesk\CoreFrameworkBundle\Utils\HTMLFilter;
  16. use Webkul\UVDesk\CoreFrameworkBundle\Entity\SupportRole;
  17. use Webkul\UVDesk\CoreFrameworkBundle\Utils\TokenGenerator;
  18. use Webkul\UVDesk\MailboxBundle\Utils\MailboxConfiguration;
  19. use Symfony\Component\DependencyInjection\ContainerInterface;
  20. use Webkul\UVDesk\CoreFrameworkBundle\Workflow\Events as CoreWorkflowEvents;
  21. use Webkul\UVDesk\MailboxBundle\Utils\IMAP;
  22. use Webkul\UVDesk\MailboxBundle\Utils\SMTP;
  23. class MailboxService
  24. {
  25.     const PATH_TO_CONFIG '/config/packages/uvdesk_mailbox.yaml';
  26.     private $parser;
  27.     private $container;
  28.     private $requestStack;
  29.     private $entityManager;
  30.     private $mailboxCollection = [];
  31.     public function __construct(ContainerInterface $containerRequestStack $requestStackEntityManagerInterface $entityManager)
  32.     {
  33.         $this->container $container;
  34.         $this->requestStack $requestStack;
  35.         $this->entityManager $entityManager;
  36.     }
  37.     public function getPathToConfigurationFile()
  38.     {
  39.         return $this->container->get('kernel')->getProjectDir() . self::PATH_TO_CONFIG;
  40.     }
  41.     public function createConfiguration($params)
  42.     {
  43.         $configuration = new MailboxConfigurations\MailboxConfiguration($params);
  44.         return $configuration ?? null;
  45.     }
  46.     public function parseMailboxConfigurations(bool $ignoreInvalidAttributes false
  47.     {
  48.         $path $this->getPathToConfigurationFile();
  49.         if (!file_exists($path)) {
  50.             throw new \Exception("File '$path' not found.");
  51.         }
  52.         // Read configurations from package config.
  53.         $mailboxes $this->container->getParameter('uvdesk.mailboxes');
  54.         $defaultMailbox $this->container->getParameter('uvdesk.default_mailbox');
  55.         
  56.         $mailboxConfiguration = new MailboxConfiguration($defaultMailbox);
  57.         foreach ($mailboxes as $id) {
  58.             $params $this->container->getParameter("uvdesk.mailboxes.$id");
  59.             // IMAP Configuration
  60.             $imapConfiguration null;
  61.             if (!empty($params['imap_server'])) {
  62.                 $imapConfiguration IMAP\Configuration::guessTransportDefinition($params['imap_server']);
  63.     
  64.                 if ($imapConfiguration instanceof IMAP\Transport\AppTransportConfigurationInterface) {
  65.                     $imapConfiguration
  66.                         ->setClient($params['imap_server']['client'])
  67.                         ->setUsername($params['imap_server']['username'])
  68.                     ;
  69.                 } else if ($imapConfiguration instanceof IMAP\Transport\SimpleTransportConfigurationInterface) {
  70.                     $imapConfiguration
  71.                         ->setUsername($params['imap_server']['username'])
  72.                     ;
  73.                 } else {
  74.                     $imapConfiguration
  75.                         ->setUsername($params['imap_server']['username'])
  76.                         ->setPassword($params['imap_server']['password'])
  77.                     ;
  78.                 }
  79.             }
  80.             // SMTP Configuration
  81.             $smtpConfiguration null;
  82.             if (!empty($params['smtp_server'])) {
  83.                 $smtpConfiguration SMTP\Configuration::guessTransportDefinition($params['smtp_server']);
  84.     
  85.                 if ($smtpConfiguration instanceof SMTP\Transport\AppTransportConfigurationInterface) {
  86.                     $smtpConfiguration
  87.                         ->setClient($params['smtp_server']['client'])
  88.                         ->setUsername($params['smtp_server']['username'])
  89.                     ;
  90.                 } else if ($smtpConfiguration instanceof SMTP\Transport\ResolvedTransportConfigurationInterface) {
  91.                     $smtpConfiguration
  92.                         ->setUsername($params['smtp_server']['username'])
  93.                         ->setPassword($params['smtp_server']['password'])
  94.                     ;
  95.                 }  else {
  96.                     $smtpConfiguration
  97.                         ->setHost($params['smtp_server']['host'])
  98.                         ->setPort($params['smtp_server']['port'])
  99.                         ->setUsername($params['smtp_server']['username'])
  100.                         ->setPassword($params['smtp_server']['password'])
  101.                     ;
  102.                     if (!empty($params['smtp_server']['sender_address'])) {
  103.                         $smtpConfiguration
  104.                             ->setSenderAddress($params['smtp_server']['sender_address'])
  105.                         ;
  106.                     }
  107.                 }
  108.             }
  109.             // Mailbox Configuration
  110.             $mailbox = new Mailbox($id);
  111.             $mailbox
  112.                 ->setName($params['name'])
  113.                 ->setIsEnabled($params['enabled'])
  114.                 ->setIsEmailDeliveryDisabled(empty($params['disable_outbound_emails']) ? false $params['disable_outbound_emails'])
  115.                 ->setIsStrictModeEnabled(empty($params['use_strict_mode']) ? false $params['use_strict_mode'])
  116.             ;
  117.             if (!empty($imapConfiguration)) {
  118.                 $mailbox
  119.                     ->setImapConfiguration($imapConfiguration)
  120.                 ;
  121.             }
  122.             if (!empty($smtpConfiguration)) {
  123.                 $mailbox
  124.                     ->setSmtpConfiguration($smtpConfiguration)
  125.                 ;
  126.             }
  127.             $mailboxConfiguration->addMailbox($mailbox);
  128.         }
  129.         return $mailboxConfiguration;
  130.     }
  131.     private function getParser()
  132.     {
  133.         if (empty($this->parser)) {
  134.             $this->parser = new Parser();
  135.         }
  136.         return $this->parser;
  137.     }
  138.     private function getRegisteredMailboxes()
  139.     {
  140.         if (empty($this->mailboxCollection)) {
  141.             $this->mailboxCollection array_map(function ($mailboxId) {
  142.                 return $this->container->getParameter("uvdesk.mailboxes.$mailboxId");
  143.             }, $this->container->getParameter('uvdesk.mailboxes'));
  144.         }
  145.         return $this->mailboxCollection;
  146.     }
  147.     public function parseAddress($type)
  148.     {
  149.         $addresses mailparse_rfc822_parse_addresses($this->getParser()->getHeader($type));
  150.         return $addresses ?: false;
  151.     }
  152.     public function getEmailAddress($addresses)
  153.     {
  154.         foreach ((array) $addresses as $address) {
  155.             if (filter_var($address['address'], FILTER_VALIDATE_EMAIL)) {
  156.                 return $address['address'];
  157.             }
  158.         }
  159.         return null;
  160.     }
  161.     public function getMailboxByEmail($email)
  162.     {
  163.         foreach ($this->getRegisteredMailboxes() as $registeredMailbox) {
  164.             if (strtolower($email) === strtolower($registeredMailbox['imap_server']['username'])) {
  165.                 return $registeredMailbox;
  166.             }
  167.         }
  168.         throw new \Exception("No mailbox found for email '$email'");
  169.     }
  170.     
  171.     public function getMailboxByToEmail($email)
  172.     {
  173.         foreach ($this->getRegisteredMailboxes() as $registeredMailbox) {
  174.             if (strtolower($email) === strtolower($registeredMailbox['imap_server']['username'])) {
  175.                 return true;
  176.             }
  177.         }
  178.         return false;
  179.     }
  180.     private function searchticketSubjectRefrence($senderEmail$messageSubject) {
  181.         
  182.         // Search Criteria: Find ticket based on subject
  183.         if (!empty($senderEmail) && !empty($messageSubject)) {
  184.             $threadRepository $this->entityManager->getRepository(Thread::class);
  185.             $ticket $threadRepository->findTicketBySubject($senderEmail$messageSubject);
  186.             if ($ticket  != null) {
  187.                 return $ticket;
  188.             }
  189.         }
  190.         return null;
  191.     }
  192.     private function searchExistingTickets(array $criterias = [])
  193.     {
  194.         if (empty($criterias)) {
  195.             return null;
  196.         }
  197.         $ticketRepository $this->entityManager->getRepository(Ticket::class);
  198.         $threadRepository $this->entityManager->getRepository(Thread::class);
  199.         foreach ($criterias as $criteria => $criteriaValue) {
  200.             if (empty($criteriaValue)) {
  201.                 continue;
  202.             }
  203.             switch ($criteria) {
  204.                 case 'messageId':
  205.                     // Search Criteria 1: Find ticket by unique message id
  206.                     $ticket $ticketRepository->findOneByReferenceIds($criteriaValue);
  207.                     if (!empty($ticket)) {
  208.                         return $ticket;
  209.                     } else {
  210.                         $thread $threadRepository->findOneByMessageId($criteriaValue);
  211.         
  212.                         if (!empty($thread)) {
  213.                             return $thread->getTicket();
  214.                         }
  215.                     }
  216.                     
  217.                     break;
  218.                 case 'outlookConversationId':
  219.                     // Search Criteria 1: Find ticket by unique message id
  220.                     $ticket $ticketRepository->findOneByOutlookConversationId($criteriaValue);
  221.                     if (!empty($ticket)) {
  222.                         return $ticket;
  223.                     }
  224.                     
  225.                     break;
  226.                 case 'inReplyTo':
  227.                     // Search Criteria 2: Find ticket based on in-reply-to reference id
  228.                     $ticket $this->entityManager->getRepository(Thread::class)->findThreadByRefrenceId($criteriaValue);
  229.                     if (!empty($ticket)) {
  230.                         return $ticket;
  231.                     } else {
  232.                         $thread $threadRepository->findOneByMessageId($criteriaValue);
  233.         
  234.                         if (!empty($thread)) {
  235.                             return $thread->getTicket();
  236.                         }
  237.                     }
  238.                     
  239.                     break;
  240.                 case 'referenceIds':
  241.                     // Search Criteria 3: Find ticket based on reference id
  242.                     // Break references into ind. message id collection, and iteratively 
  243.                     // search for existing threads for these message ids.
  244.                     $referenceIds explode(' '$criteriaValue);
  245.                     foreach ($referenceIds as $messageId) {
  246.                         $thread $threadRepository->findOneByMessageId($messageId);
  247.                         if (!empty($thread)) {
  248.                             return $thread->getTicket();
  249.                         }
  250.                     }
  251.                     break;
  252.                 default:
  253.                     break;
  254.             }
  255.         }
  256.         // // Search Criteria 4: Find ticket based on subject
  257.         // if (!empty($messageSubject)) {
  258.         //     $ticket = $threadRepository->findTicketBySubject($senderEmail, $subject);
  259.         //     if (!empty($ticket)) {
  260.         //         return $ticket;
  261.         //     }
  262.         // }
  263.         
  264.         return null;
  265.     }
  266.     
  267.     public function processMail($rawEmail)
  268.     {
  269.         $mailData = [];
  270.         $parser $this->getParser();
  271.         $parser->setText($rawEmail);
  272.         $from $this->parseAddress('from') ?: $this->parseAddress('sender');
  273.         $addresses = [
  274.             'from' => $this->getEmailAddress($from),
  275.             'to' => empty($this->parseAddress('X-Forwarded-To')) ? $this->parseAddress('to') : $this->parseAddress('X-Forwarded-To'),
  276.             'cc' => $this->parseAddress('cc'),
  277.             'delivered-to' => $this->parseAddress('delivered-to'),
  278.         ];
  279.         if (empty($addresses['from'])) {
  280.             return [
  281.                 'message' => "No 'from' email address was found while processing contents of email."
  282.                 'content' => [], 
  283.             ];
  284.         } else {
  285.             if (!empty($addresses['delivered-to'])) {
  286.                 $addresses['to'] = array_map(function($address) {
  287.                     return $address['address'];
  288.                 }, $addresses['delivered-to']);
  289.             } else if (!empty($addresses['to'])) {
  290.                 $addresses['to'] = array_map(function($address) {
  291.                     return $address['address'];
  292.                 }, $addresses['to']);
  293.             } else if (!empty($addresses['cc'])) {
  294.                 $addresses['to'] = array_map(function($address) {
  295.                     return $address['address'];
  296.                 }, $addresses['cc']);
  297.             }
  298.             
  299.             // Skip email processing if no to-emails are specified
  300.             if (empty($addresses['to'])) {
  301.                 return [
  302.                     'message' => "No 'to' email addresses were found in the email."
  303.                     'content' => [
  304.                         'from' => !empty($addresses['from']) ? $addresses['from'] : null
  305.                     ], 
  306.                 ];
  307.             }
  308.             // Skip email processing if email is an auto-forwarded message to prevent infinite loop.
  309.             if ($parser->getHeader('precedence') || $parser->getHeader('x-autoreply') || $parser->getHeader('x-autorespond') || 'auto-replied' == $parser->getHeader('auto-submitted')) {
  310.                 return [
  311.                     'message' => "Received an auto-forwarded email which can lead to possible infinite loop of email exchanges. Skipping email from further processing."
  312.                     'content' => [
  313.                         'from' => !empty($addresses['from']) ? $addresses['from'] : null
  314.                     ], 
  315.                 ];
  316.             }
  317.             // Check for self-referencing. Skip email processing if a mailbox is configured by the sender's address.
  318.             try {
  319.                 $this->getMailboxByEmail($addresses['from']);
  320.                 return [
  321.                     'message' => "Received a self-referencing email where the sender email address matches one of the configured mailbox address. Skipping email from further processing."
  322.                     'content' => [
  323.                         'from' => !empty($addresses['from']) ? $addresses['from'] : null
  324.                     ], 
  325.                 ];
  326.             } catch (\Exception $e) {
  327.                 // An exception being thrown means no mailboxes were found from the recipient's address. Continue processing.
  328.             }
  329.         }
  330.         $mailData['replyTo'] = '';
  331.         
  332.         foreach($addresses['to'] as $mailboxEmail){
  333.             if($this->getMailboxByToEmail(strtolower($mailboxEmail))){
  334.                 $mailData['replyTo'] = $mailboxEmail;
  335.             }
  336.         }
  337.         // Process Mail - References
  338.         $addresses['to'][0] = isset($mailData['replyTo']) ? strtolower($mailData['replyTo']) : strtolower($addresses['to'][0]);
  339.         $mailData['replyTo'] = $addresses['to'];
  340.         $mailData['messageId'] = $parser->getHeader('message-id') ?: null;
  341.         $mailData['inReplyTo'] = htmlspecialchars_decode($parser->getHeader('in-reply-to'));
  342.         $mailData['referenceIds'] = htmlspecialchars_decode($parser->getHeader('references'));
  343.         $mailData['cc'] = array_filter(explode(','$parser->getHeader('cc'))) ?: [];
  344.         $mailData['bcc'] = array_filter(explode(','$parser->getHeader('bcc'))) ?: [];
  345.         // Process Mail - User Details
  346.         $mailData['source'] = 'email';
  347.         $mailData['createdBy'] = 'customer';
  348.         $mailData['role'] = 'ROLE_CUSTOMER';
  349.         $mailData['from'] = $addresses['from'];
  350.         $mailData['name'] = trim(current(explode('@'$from[0]['display'])));
  351.         // Process Mail - Content
  352.         $htmlFilter = new HTMLFilter();
  353.         $mailData['subject'] = $parser->getHeader('subject');
  354.         $mailData['message'] = autolink($htmlFilter->addClassEmailReplyQuote($parser->getMessageBody('htmlEmbedded')));
  355.         $mailData['attachments'] = $parser->getAttachments();
  356.         
  357.         if (!$mailData['message']) {
  358.             $mailData['message'] = autolink($htmlFilter->addClassEmailReplyQuote($parser->getMessageBody('text')));
  359.         }
  360.         $website $this->entityManager->getRepository(Website::class)->findOneByCode('knowledgebase');
  361.         
  362.         if (!empty($mailData['from']) && $this->container->get('ticket.service')->isEmailBlocked($mailData['from'], $website)) {
  363.             return [
  364.                 'message' => "Received email where the sender email address is present in the block list. Skipping this email from further processing."
  365.                 'content' => [
  366.                     'from' => !empty($mailData['from']) ? $mailData['from'] : null
  367.                 ], 
  368.             ];
  369.         }
  370.         // Search for any existing tickets
  371.         $ticket $this->searchExistingTickets([
  372.             'messageId' => $mailData['messageId'],
  373.             'inReplyTo' => $mailData['inReplyTo'],
  374.             'referenceIds' => $mailData['referenceIds'],
  375.             'from' => $mailData['from'],
  376.             'subject' => $mailData['subject'],
  377.         ]);
  378.         if (empty($ticket)) {
  379.             $mailData['threadType'] = 'create';
  380.             $mailData['referenceIds'] = $mailData['messageId'];
  381.             // @Todo For same subject with same customer check
  382.             // $ticketSubjectRefrenceExist = $this->searchticketSubjectRefrence($mailData['from'], $mailData['subject']);
  383.             // if(!empty($ticketSubjectRefrenceExist)) {
  384.             //     return;
  385.             // }
  386.             $thread $this->container->get('ticket.service')->createTicket($mailData);
  387.             // Trigger ticket created event
  388.             $event = new GenericEvent(CoreWorkflowEvents\Ticket\Create::getId(), [
  389.                 'entity' =>  $thread->getTicket(),
  390.             ]);
  391.             $this->container->get('event_dispatcher')->dispatch($event'uvdesk.automation.workflow.execute');
  392.         } else if (false === $ticket->getIsTrashed() && strtolower($ticket->getStatus()->getCode()) != 'spam' && !empty($mailData['inReplyTo'])) {
  393.             $mailData['threadType'] = 'reply';
  394.             $thread $this->entityManager->getRepository(Thread::class)->findOneByMessageId($mailData['messageId']);
  395.             $ticketRef $this->entityManager->getRepository(Ticket::class)->findById($ticket->getId());
  396.             $referenceIds explode(' '$ticketRef[0]->getReferenceIds());
  397.             if (!empty($thread)) {
  398.                 // Thread with the same message id exists skip process.
  399.                 return [
  400.                     'message' => "The contents of this email has already been processed."
  401.                     'content' => [
  402.                         'from' => !empty($mailData['from']) ? $mailData['from'] : null
  403.                         'thread' => $thread->getId(), 
  404.                         'ticket' => $ticket->getId(), 
  405.                     ], 
  406.                 ];
  407.             }
  408.             if (in_array($mailData['messageId'], $referenceIds)) {
  409.                 // Thread with the same message id exists skip process.
  410.                 return [
  411.                     'message' => "The contents of this email has already been processed."
  412.                     'content' => [
  413.                         'from' => !empty($mailData['from']) ? $mailData['from'] : null
  414.                     ], 
  415.                 ];
  416.             }
  417.             if ($ticket->getCustomer() && $ticket->getCustomer()->getEmail() == $mailData['from']) {
  418.                 // Reply from customer
  419.                 $user $ticket->getCustomer();
  420.                 $mailData['user'] = $user;
  421.                 $userDetails $user->getCustomerInstance()->getPartialDetails();
  422.             } else if ($this->entityManager->getRepository(Ticket::class)->isTicketCollaborator($ticket$mailData['from'])){
  423.                 // Reply from collaborator
  424.                 $user $this->entityManager->getRepository(User::class)->findOneByEmail($mailData['from']);
  425.                 $mailData['user'] = $user;
  426.                 $mailData['createdBy'] = 'collaborator';
  427.                 $userDetails $user->getCustomerInstance()->getPartialDetails();
  428.             } else {
  429.                 $user $this->entityManager->getRepository(User::class)->findOneByEmail($mailData['from']);
  430.                 
  431.                 if (!empty($user) && null != $user->getAgentInstance()) {
  432.                     $mailData['user'] = $user;
  433.                     $mailData['createdBy'] = 'agent';
  434.                     $userDetails $user->getAgentInstance()->getPartialDetails();
  435.                 } else {
  436.                     // Add user as a ticket collaborator
  437.                     if (empty($user)) {
  438.                         // Create a new user instance with customer support role
  439.                         $role $this->entityManager->getRepository(SupportRole::class)->findOneByCode('ROLE_CUSTOMER');
  440.                         $user $this->container->get('user.service')->createUserInstance($mailData['from'], $mailData['name'], $role, [
  441.                             'source' => 'email',
  442.                             'active' => true
  443.                         ]);
  444.                     }
  445.                     $mailData['user'] = $user;
  446.                     $userDetails $user->getCustomerInstance()->getPartialDetails();
  447.                     if (false == $this->entityManager->getRepository(Ticket::class)->isTicketCollaborator($ticket$mailData['from'])) {
  448.                         $ticket->addCollaborator($user);
  449.                         $this->entityManager->persist($ticket);
  450.                         $this->entityManager->flush();
  451.                         $ticket->lastCollaborator $user;
  452.                         
  453.                         $event = new GenericEvent(CoreWorkflowEvents\Ticket\Collaborator::getId(), [
  454.                             'entity' => $ticket,
  455.                         ]);
  456.                         $this->container->get('event_dispatcher')->dispatch($event'uvdesk.automation.workflow.execute');
  457.                     }
  458.                 }
  459.             }
  460.             $mailData['fullname'] = $userDetails['name'];
  461.             
  462.             $thread $this->container->get('ticket.service')->createThread($ticket$mailData);
  463.             
  464.             if($thread->getThreadType() == 'reply') {
  465.                 if ($thread->getCreatedBy() == 'customer') {
  466.                     $event = new GenericEvent(CoreWorkflowEvents\Ticket\CustomerReply::getId(), [
  467.                         'entity' =>  $ticket,
  468.                     ]);
  469.                 }  else if ($thread->getCreatedBy() == 'collaborator') {
  470.                     $event = new GenericEvent(CoreWorkflowEvents\Ticket\CollaboratorReply::getId(), [
  471.                         'entity' =>  $ticket,
  472.                     ]);
  473.                 } else {
  474.                     $event = new GenericEvent(CoreWorkflowEvents\Ticket\AgentReply::getId(), [
  475.                         'entity' =>  $ticket,
  476.                     ]);
  477.                 }
  478.             }
  479.             // Trigger thread reply event
  480.             $this->container->get('event_dispatcher')->dispatch($event'uvdesk.automation.workflow.execute');
  481.         } else if (false === $ticket->getIsTrashed() && strtolower($ticket->getStatus()->getCode()) != 'spam' && empty($mailData['inReplyTo'])) {
  482.             return [
  483.                 'message' => "The contents of this email has already been processed."
  484.                 'content' => [
  485.                     'from' => !empty($mailData['from']) ? $mailData['from'] : null
  486.                     'thread' => !empty($thread) ? $thread->getId() : null
  487.                     'ticket' => !empty($ticket) ? $ticket->getId() : null
  488.                 ], 
  489.             ];
  490.         }
  491.         return [
  492.             'message' => "Inbound email processsed successfully."
  493.             'content' => [
  494.                 'from' => !empty($mailData['from']) ? $mailData['from'] : null
  495.                 'thread' => !empty($thread) ? $thread->getId() : null
  496.                 'ticket' => !empty($ticket) ? $ticket->getId() : null
  497.             ], 
  498.         ];
  499.     }
  500.     public function processOutlookMail(array $outlookEmail)
  501.     {
  502.         $mailData = [];
  503.         $senderName null;
  504.         $senderAddress null;
  505.         if (!empty($outlookEmail['from']['emailAddress']['address'])) {
  506.             $senderName $outlookEmail['from']['emailAddress']['name'];
  507.             $senderAddress $outlookEmail['from']['emailAddress']['address'];
  508.         } else if (!empty($outlookEmail['sender']['emailAddress']['address'])) {
  509.             $senderName $outlookEmail['sender']['emailAddress']['name'];
  510.             $senderAddress $outlookEmail['sender']['emailAddress']['address'];
  511.         } else {
  512.             return [
  513.                 'message' => "No 'from' email address was found while processing contents of email."
  514.                 'content' => [], 
  515.             ];
  516.         }
  517.         $toRecipients array_map(function ($recipient) { return $recipient['emailAddress']['address']; }, $outlookEmail['toRecipients']);
  518.         $ccRecipients array_map(function ($recipient) { return $recipient['emailAddress']['address']; }, $outlookEmail['ccRecipients'] ?? []);
  519.         $bccRecipients array_map(function ($recipient) { return $recipient['emailAddress']['address']; }, $outlookEmail['bccRecipients'] ?? []);
  520.         $addresses = [
  521.             'from' => $senderAddress
  522.             'to' => $toRecipients
  523.             'cc' => $ccRecipients
  524.         ];
  525.         
  526.         // Skip email processing if no to-emails are specified
  527.         if (empty($addresses['to'])) {
  528.             return [
  529.                 'message' => "No 'to' email addresses were found in the email."
  530.                 'content' => [
  531.                     'from' => $senderAddress ?? null
  532.                 ], 
  533.             ];
  534.         }
  535.         // Check for self-referencing. Skip email processing if a mailbox is configured by the sender's address.
  536.         try {
  537.             $this->getMailboxByEmail($senderAddress);
  538.             return [
  539.                 'message' => "Received a self-referencing email where the sender email address matches one of the configured mailbox address. Skipping email from further processing."
  540.                 'content' => [
  541.                     'from' => $senderAddress ?? null
  542.                 ], 
  543.             ];
  544.         } catch (\Exception $e) {
  545.             // An exception being thrown means no mailboxes were found from the recipient's address. Continue processing.
  546.         }
  547.         // Process Mail - References
  548.         // $addresses['to'][0] = isset($mailData['replyTo']) ? strtolower($mailData['replyTo']) : strtolower($addresses['to'][0]);
  549.         $mailData['replyTo'] = $addresses['to'];
  550.         $mailData['messageId'] = $outlookEmail['internetMessageId'];
  551.         $mailData['outlookConversationId'] = $outlookEmail['conversationId'];
  552.         $mailData['inReplyTo'] = $outlookEmail['conversationId'];
  553.         // $mailData['inReplyTo'] = htmlspecialchars_decode($parser->getHeader('in-reply-to'));
  554.         $mailData['referenceIds'] = '';
  555.         // $mailData['referenceIds'] = htmlspecialchars_decode($parser->getHeader('references'));
  556.         $mailData['cc'] = $ccRecipients;
  557.         $mailData['bcc'] = $bccRecipients;
  558.         // Process Mail - User Details
  559.         $mailData['source'] = 'email';
  560.         $mailData['createdBy'] = 'customer';
  561.         $mailData['role'] = 'ROLE_CUSTOMER';
  562.         $mailData['from'] = $senderAddress;
  563.         $mailData['name'] = trim($senderName);
  564.         // Process Mail - Content
  565.         $htmlFilter = new HTMLFilter();
  566.         $mailData['subject'] = $outlookEmail['subject'];
  567.         $mailData['message'] = autolink($htmlFilter->addClassEmailReplyQuote($outlookEmail['body']['content']));
  568.         $mailData['attachments'] = [];
  569.         // $mailData['attachments'] = $parser->getAttachments();
  570.         $website $this->entityManager->getRepository(Website::class)->findOneByCode('knowledgebase');
  571.         
  572.         if (!empty($mailData['from']) && $this->container->get('ticket.service')->isEmailBlocked($mailData['from'], $website)) {
  573.             return [
  574.                 'message' => "Received email where the sender email address is present in the block list. Skipping this email from further processing."
  575.                 'content' => [
  576.                     'from' => !empty($mailData['from']) ? $mailData['from'] : null
  577.                 ], 
  578.             ];
  579.         }
  580.         // return [
  581.         //     'outlookConversationId' => $mailData['outlookConversationId'],
  582.         //     'message' => "No 'to' email addresses were found in the email.", 
  583.         //     'content' => [
  584.         //         'outlookConversationId' => $mailData['outlookConversationId'],
  585.         //     ], 
  586.         // ];
  587.         // Search for any existing tickets
  588.         $ticket $this->searchExistingTickets([
  589.             'messageId' => $mailData['messageId'],
  590.             'inReplyTo' => $mailData['inReplyTo'],
  591.             'referenceIds' => $mailData['referenceIds'],
  592.             'from' => $mailData['from'],
  593.             'subject' => $mailData['subject'], 
  594.             'outlookConversationId' => $mailData['outlookConversationId'],
  595.         ]);
  596.         if (empty($ticket)) {
  597.             $mailData['threadType'] = 'create';
  598.             $mailData['referenceIds'] = $mailData['messageId'];
  599.             // @Todo For same subject with same customer check
  600.             // $ticketSubjectRefrenceExist = $this->searchticketSubjectRefrence($mailData['from'], $mailData['subject']);
  601.             // if(!empty($ticketSubjectRefrenceExist)) {
  602.             //     return;
  603.             // }
  604.             $thread $this->container->get('ticket.service')->createTicket($mailData);
  605.             // Trigger ticket created event
  606.             $event = new GenericEvent(CoreWorkflowEvents\Ticket\Create::getId(), [
  607.                 'entity' =>  $thread->getTicket(),
  608.             ]);
  609.             $this->container->get('event_dispatcher')->dispatch($event'uvdesk.automation.workflow.execute');
  610.         } else if (false === $ticket->getIsTrashed() && strtolower($ticket->getStatus()->getCode()) != 'spam' && !empty($mailData['inReplyTo'])) {
  611.             $mailData['threadType'] = 'reply';
  612.             $thread $this->entityManager->getRepository(Thread::class)->findOneByMessageId($mailData['messageId']);
  613.             $ticketRef $this->entityManager->getRepository(Ticket::class)->findById($ticket->getId());
  614.             $referenceIds explode(' '$ticketRef[0]->getReferenceIds());
  615.             if (!empty($thread)) {
  616.                 // Thread with the same message id exists skip process.
  617.                 return [
  618.                     'message' => "The contents of this email has already been processed 1."
  619.                     'content' => [
  620.                         'from' => !empty($mailData['from']) ? $mailData['from'] : null
  621.                         'thread' => $thread->getId(), 
  622.                         'ticket' => $ticket->getId(), 
  623.                     ], 
  624.                 ];
  625.             }
  626.             if (in_array($mailData['messageId'], $referenceIds)) {
  627.                 // Thread with the same message id exists skip process.
  628.                 return [
  629.                     'message' => "The contents of this email has already been processed 2."
  630.                     'content' => [
  631.                         'from' => !empty($mailData['from']) ? $mailData['from'] : null
  632.                     ], 
  633.                 ];
  634.             }
  635.             if ($ticket->getCustomer() && $ticket->getCustomer()->getEmail() == $mailData['from']) {
  636.                 // Reply from customer
  637.                 $user $ticket->getCustomer();
  638.                 $mailData['user'] = $user;
  639.                 $userDetails $user->getCustomerInstance()->getPartialDetails();
  640.             } else if ($this->entityManager->getRepository(Ticket::class)->isTicketCollaborator($ticket$mailData['from'])){
  641.                 // Reply from collaborator
  642.                 $user $this->entityManager->getRepository(User::class)->findOneByEmail($mailData['from']);
  643.                 $mailData['user'] = $user;
  644.                 $mailData['createdBy'] = 'collaborator';
  645.                 $userDetails $user->getCustomerInstance()->getPartialDetails();
  646.             } else {
  647.                 $user $this->entityManager->getRepository(User::class)->findOneByEmail($mailData['from']);
  648.                 
  649.                 if (!empty($user) && null != $user->getAgentInstance()) {
  650.                     $mailData['user'] = $user;
  651.                     $mailData['createdBy'] = 'agent';
  652.                     $userDetails $user->getAgentInstance()->getPartialDetails();
  653.                 } else {
  654.                     // Add user as a ticket collaborator
  655.                     if (empty($user)) {
  656.                         // Create a new user instance with customer support role
  657.                         $role $this->entityManager->getRepository(SupportRole::class)->findOneByCode('ROLE_CUSTOMER');
  658.                         $user $this->container->get('user.service')->createUserInstance($mailData['from'], $mailData['name'], $role, [
  659.                             'source' => 'email',
  660.                             'active' => true
  661.                         ]);
  662.                     }
  663.                     $mailData['user'] = $user;
  664.                     $userDetails $user->getCustomerInstance()->getPartialDetails();
  665.                     if (false == $this->entityManager->getRepository(Ticket::class)->isTicketCollaborator($ticket$mailData['from'])) {
  666.                         $ticket->addCollaborator($user);
  667.                         $this->entityManager->persist($ticket);
  668.                         $this->entityManager->flush();
  669.                         $ticket->lastCollaborator $user;
  670.                         
  671.                         $event = new GenericEvent(CoreWorkflowEvents\Ticket\Collaborator::getId(), [
  672.                             'entity' => $ticket,
  673.                         ]);
  674.                         $this->container->get('event_dispatcher')->dispatch($event'uvdesk.automation.workflow.execute');
  675.                     }
  676.                 }
  677.             }
  678.             $mailData['fullname'] = $userDetails['name'];
  679.             
  680.             $thread $this->container->get('ticket.service')->createThread($ticket$mailData);
  681.             
  682.             if($thread->getThreadType() == 'reply') {
  683.                 if ($thread->getCreatedBy() == 'customer') {
  684.                     $event = new GenericEvent(CoreWorkflowEvents\Ticket\CustomerReply::getId(), [
  685.                         'entity' =>  $ticket,
  686.                     ]);
  687.                 }  else if ($thread->getCreatedBy() == 'collaborator') {
  688.                     $event = new GenericEvent(CoreWorkflowEvents\Ticket\CollaboratorReply::getId(), [
  689.                         'entity' =>  $ticket,
  690.                     ]);
  691.                 } else {
  692.                     $event = new GenericEvent(CoreWorkflowEvents\Ticket\AgentReply::getId(), [
  693.                         'entity' =>  $ticket,
  694.                     ]);
  695.                 }
  696.             }
  697.             // Trigger thread reply event
  698.             $this->container->get('event_dispatcher')->dispatch($event'uvdesk.automation.workflow.execute');
  699.         } else if (false === $ticket->getIsTrashed() && strtolower($ticket->getStatus()->getCode()) != 'spam' && empty($mailData['inReplyTo'])) {
  700.             return [
  701.                 'message' => "The contents of this email has already been processed 3."
  702.                 'content' => [
  703.                     'from' => !empty($mailData['from']) ? $mailData['from'] : null
  704.                     'thread' => !empty($thread) ? $thread->getId() : null
  705.                     'ticket' => !empty($ticket) ? $ticket->getId() : null
  706.                 ], 
  707.             ];
  708.         }
  709.         return [
  710.             'message' => "Inbound email processsed successfully."
  711.             'content' => [
  712.                 'from' => !empty($mailData['from']) ? $mailData['from'] : null
  713.                 'thread' => !empty($thread) ? $thread->getId() : null
  714.                 'ticket' => !empty($ticket) ? $ticket->getId() : null
  715.             ], 
  716.         ];
  717.     }
  718. }