$class_name::getName(), 'required' => $class_name::getRequiredConfig(), 'api_types' => $class_name::getAPITypes(), 'spf_hint' => $class_name::getSPFHint(), 'dkim_hint' => $class_name::getDKIMHint(), ); } } foreach (self::$custom_drivers as $driver) { if ($driver->isSupported()) { $result[strtolower(class_basename($driver))] = array( 'name' => $driver->getName(), 'required' => $driver->getRequiredConfig(), 'api_types' => $driver->getAPITypes(), 'spf_hint' => $class_name::getSPFHint(), 'dkim_hint' => $class_name::getDKIMHint(), ); } } ksort($result); return $result; } /** * The constructor. */ public function __construct() { $this->message = new \Swift_Message; $this->driver = self::getDefaultDriver(); } /** * Set the sender (From:). * * @param string $email E-mail address * @param string $name Name (optional) * @return bool */ public function setFrom(string $email, ?string $name = null): bool { try { $this->message->setFrom($name === null ? $email : array($email => $name)); return true; } catch (\Exception $e) { $this->errors[] = 'setFrom: ' . $e->getMessage(); return false; } } /** * Get the sender (From:). * * @return string|null */ public function getFrom(): ?string { $list = $this->message->getFrom(); return $list ? array_first($this->formatAddresses($list)) : null; } /** * Add a recipient (To:). * * @param string $email E-mail address * @param string $name Name (optional) * @return bool */ public function addTo(string $email, ?string $name = null): bool { try { $this->message->addTo($email, $name); return true; } catch (\Exception $e) { $this->errors[] = 'addTo: ' . $e->getMessage(); return false; } } /** * Add a recipient (CC:). * * @param string $email E-mail address * @param string $name Name (optional) * @return bool */ public function addCc(string $email, ?string $name = null): bool { try { $this->message->addCc($email, $name); return true; } catch (\Exception $e) { $this->errors[] = 'addCc: ' . $e->getMessage(); return false; } } /** * Add a recipient (BCC:). * * @param string $email E-mail address * @param string $name Name (optional) * @return bool */ public function addBcc(string $email, ?string $name = null): bool { try { $this->message->addBcc($email, $name); return true; } catch (\Exception $e) { $this->errors[] = 'addBcc: ' . $e->getMessage(); return false; } } /** * Get the list of recipients. * * @return array */ public function getRecipients(): array { $result = array(); foreach ($this->formatAddresses($this->message->getTo() ?: []) as $address) { $result[] = $address; } foreach ($this->formatAddresses($this->message->getCc() ?: []) as $address) { $result[] = $address; } foreach ($this->formatAddresses($this->message->getBcc() ?: []) as $address) { $result[] = $address; } return array_unique($result); } /** * Set the Reply-To: address. * * @param string $replyTo * @return bool */ public function setReplyTo(string $replyTo): bool { try { $this->message->setReplyTo(array($replyTo)); return true; } catch (\Exception $e) { $this->errors[] = 'setReplyTo: ' . $e->getMessage(); return false; } } /** * Set the Return-Path: address. * * @param string $returnPath * @return bool */ public function setReturnPath(string $returnPath): bool { try { $this->message->setReturnPath($returnPath); return true; } catch (\Exception $e) { $this->errors[] = 'setReturnPath: ' . $e->getMessage(); return false; } } /** * Set the Message ID. * * @param string $message_id * @return bool */ public function setMessageID(string $message_id): bool { try { $headers = $this->message->getHeaders(); $headers->get('Message-ID')->setId($message_id); return true; } catch (\Exception $e) { $this->errors[] = 'setMessageID: ' . $e->getMessage(); return false; } } /** * Set the In-Reply-To: header. * * @param string $in_reply_to * @return bool */ public function setInReplyTo(string $in_reply_to): bool { try { $headers = $this->message->getHeaders(); $headers->addTextHeader('In-Reply-To', $in_reply_to); return true; } catch (\Exception $e) { $this->errors[] = 'setInReplyTo: ' . $e->getMessage(); return false; } } /** * Set the References: header. * * @param string $references * @return bool */ public function setReferences(string $references): bool { try { $headers = $this->message->getHeaders(); $headers->addTextHeader('References', $references); return true; } catch (\Exception $e) { $this->errors[] = 'setReferences: ' . $e->getMessage(); return false; } } /** * Set the subject. * * @param string $subject * @return bool */ public function setSubject(string $subject): bool { try { $this->message->setSubject($subject); return true; } catch (\Exception $e) { $this->errors[] = 'setSubject: ' . $e->getMessage(); return false; } } /** * Get the subject. * * @return string */ public function getSubject(): string { return $this->message->getSubject(); } /** * Set the subject (alias to setSubject). * * @param string $subject * @return bool */ public function setTitle(string $subject): bool { return $this->setSubject($subject); } /** * Get the subject (alias to getSubject). * * @return string */ public function getTitle(): string { return $this->getSubject(); } /** * Set the body content. * * @param string $content * @param string $content_type (optional) * @return void */ public function setBody(string $content, ?string $content_type = null): void { if ($content_type !== null) { $this->setContentType($content_type); } if (strpos($this->content_type, 'html') !== false) { $content = Filters\HTMLFilter::fixRelativeUrls($content); } $this->message->setBody($content, $this->content_type); } /** * Get the body content. * * @return string */ public function getBody(): string { return $this->message->getBody(); } /** * Set the body content (alias to setBody). * * @param string $content * @param string $content_type (optional) * @return void */ public function setContent(string $content, ?string $content_type = null): void { $this->setBody($content, $content_type); } /** * Get the body content (alias to getBody). * * @return string */ public function getContent(): string { return $this->getBody(); } /** * Set the content type. * * @param string $mode The type * @return void */ public function setContentType(string $type = 'text/html'): void { $this->content_type = (strpos($type, 'html') !== false) ? 'text/html' : ((strpos($type, '/') !== false) ? $type : 'text/plain'); } /** * Get the content type. * * @return string */ public function getContentType(): string { return $this->content_type; } /** * Attach a file. * * @param string $local_filename * @param string $display_filename (optional) * @return bool */ public function attach(string $local_filename, ?string $display_filename = null): bool { if ($display_filename === null) { $display_filename = basename($local_filename); } if (!Storage::exists($local_filename)) { return false; } $attachment = \Swift_Attachment::fromPath($local_filename); $attachment->setFilename($display_filename); $result = $this->message->attach($attachment); if ($result) { $this->attachments[] = (object)array( 'type' => 'attach', 'local_filename' => $local_filename, 'display_filename' => $display_filename, 'cid' => null, ); return true; } else { return false; } } /** * Embed a file. * * @param string $local_filename * @param string $cid (optional) * @return string|false */ public function embed(string $local_filename, ?string $cid = null) { if (!Storage::exists($local_filename)) { return false; } $embedded = \Swift_EmbeddedFile::fromPath($local_filename); if ($cid !== null) { $embedded->setId(preg_replace('/^cid:/i', '', $cid)); } $result = $this->message->embed($embedded); if ($result) { $this->attachments[] = (object)array( 'type' => 'embed', 'local_filename' => $local_filename, 'display_filename' => null, 'cid' => $result, ); return $result; } else { return false; } } /** * Get the list of attachments to this message. * * @return array */ public function getAttachments(): array { return $this->attachments; } /** * Send the email. * * @param bool $sync * @return bool */ public function send(bool $sync = false): bool { // If queue is enabled, send asynchronously. if (!$sync && config('queue.enabled') && !defined('RXQUEUE_CRON')) { Queue::addTask(self::class . '::' . 'sendAsync', $this); return true; } // Get caller information. $backtrace = debug_backtrace(0); if(count($backtrace) && isset($backtrace[0]['file'])) { $this->caller = $backtrace[0]['file'] . ($backtrace[0]['line'] ? (' line ' . $backtrace[0]['line']) : ''); } $output = \ModuleHandler::triggerCall('mail.send', 'before', $this); if(!$output->toBool()) { $this->errors[] = $output->getMessage(); return false; } // Reset Message-ID in case send() is called multiple times. $random = substr(hash('sha256', mt_rand() . microtime() . getmypid()), 0, 32); $sender = $this->message->getFrom(); $sender_email = strval(array_first_key($sender)); $id = $random . '@' . (preg_match('/^(.+)@([^@]+)$/', $sender_email, $matches) ? $matches[2] : 'swift.generated'); $this->message->getHeaders()->get('Message-ID')->setId($id); try { $this->sent = $this->driver->send($this) ? true : false; } catch(\Exception $e) { $this->errors[] = $e->getMessage(); $this->sent = false; } $output = \ModuleHandler::triggerCall('mail.send', 'after', $this); if(!$output->toBool()) { $this->errors[] = $output->getMessage(); } return $this->sent; } /** * Send an email asynchronously (for Queue integration). * * @param self $mail * @return void */ public static function sendAsync(self $mail): void { $mail->send(); } /** * Check if the message was sent. * * @return bool */ public function isSent(): bool { return $this->sent; } /** * Get caller information. * * @return string */ public function getCaller(): string { return $this->caller; } /** * Get errors. * * @return array */ public function getErrors(): array { return $this->errors; } /** * Convert image paths to absolute URLs. * * @see Mail::setContent() * @param array $matches Match info. * @return string */ protected function convertImageURLs(array $matches): string { return Filters\HTMLFilter::fixRelativeUrls($matches[0]); } /** * Format an array of addresses for display. * * @param array $addresses * @return array */ protected function formatAddresses(array $addresses): array { $result = array(); if (!$addresses) { return array(); } foreach($addresses as $email => $name) { if(strval($name) === '') { $result[] = $email; } else { $result[] = $name . ' <' . $email . '>'; } } return $result; } }