fix: firefox close dialog from smart dsi edit source (#7789)
[openemr.git] / library / classes / postmaster.php
blobad80be51fc782bf412ce7b95966a6edd8621f64a
1 <?php
3 /**
4 * MyMailer class
6 * @package OpenEMR
7 * @link https://www.open-emr.org
8 * @author Brady Miller <brady.g.miller@gmail.com>
9 * @copyright Copyright (c) 2010 Open Support LLC
10 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
11 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
14 use OpenEMR\Common\Crypto\CryptoGen;
15 use PHPMailer\PHPMailer\PHPMailer;
16 use OpenEMR\Common\Twig\TwigContainer;
17 use OpenEMR\Common\Logging\SystemLogger;
18 use OpenEMR\Common\Database\QueryUtils;
20 class MyMailer extends PHPMailer
22 var $Mailer;
23 var $SMTPAuth;
24 var $Host;
25 var $Username;
26 var $Password;
27 var $Port;
28 var $CharSet;
30 function __construct($throwExceptions = false)
32 // make sure we initiate our constructor here...
33 parent::__construct($throwExceptions);
35 $this->emailMethod();
38 /**
39 * Checks if the MyMailer service is configured for mail with all of the host parameters defined
40 * @return bool
42 public static function isConfigured()
44 switch ($GLOBALS['EMAIL_METHOD']) {
45 case "SMTP":
46 $requiredKeys = ['SMTP_HOST', 'SMTP_PORT', 'SMTP_SECURE'];
47 if ($GLOBALS['SMTP_Auth']) {
48 $requiredKeys[] = 'SMTP_USER';
49 $requiredKeys[] = 'SMTP_PASS';
51 break;
52 default:
53 $requiredKeys = [];
54 break;
57 foreach ($requiredKeys as $key) {
58 if (empty($GLOBALS[$key])) {
59 return false;
62 return true;
65 /**
66 * Adds an email to the email queue
67 * @param string $sender
68 * @param string $recipient
69 * @param string $template
70 * @param array $templateData
71 * @return bool
73 public static function emailServiceQueueTemplatedEmail(string $sender, string $recipient, string $subject, string $template, array $templateData)
75 if (empty($sender) || empty($recipient) || empty($subject) || empty($template) || empty($templateData)) {
76 return false;
78 try {
79 $body = json_encode($templateData);
80 QueryUtils::sqlInsert("INSERT into `email_queue` (`sender`, `recipient`, `subject`, `body`, `template_name`, `datetime_queued`) VALUES (?, ?, ?, ?, ?, NOW())", [$sender, $recipient, $subject, $body, $template]);
81 return true;
82 } catch (\Exception $e) {
83 (new SystemLogger())->errorLogCaller("Failed to add email to queue notification error " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
85 return false;
89 public static function emailServiceQueue(string $sender, string $recipient, string $subject, string $body): bool
91 if (empty($sender) || empty($recipient) || empty($subject) || empty($body)) {
92 return false;
95 sqlInsert("INSERT into `email_queue` (`sender`, `recipient`, `subject`, `body`, `datetime_queued`) VALUES (?, ?, ?, ?, NOW())", [$sender, $recipient, $subject, $body]);
96 return true;
99 public static function emailServiceRun(): void
101 // collect the queue
102 // TODO: @adunsulag is there a reason we don't use a transaction here to prevent race conditions?
103 $res = sqlStatement("SELECT `id`, `sender`, `recipient`, `subject`, `body`, `template_name` FROM `email_queue` WHERE `sent` = 0");
105 // send emails in the queue (to avoid race conditions, sent flag is rechecked before sending the email and then quickly set before proceeding to send the email)
106 // (first ensure the email method is properly configured)
107 $emailMethodConfigured = self::isConfigured();
108 while ($ret = sqlFetchArray($res)) {
109 $sql = sqlQuery("SELECT `sent` FROM `email_queue` WHERE `id` = ?", [$ret['id']]);
110 if ($sql['sent'] == 1) {
111 // Sent, so skip
112 } else {
113 // Not sent, so set the sent flag, and then send the email
114 sqlStatement("UPDATE `email_queue` SET `sent` = 1, `datetime_sent` = NOW() WHERE `id` = ?", [$ret['id']]);
116 if ($emailMethodConfigured) {
117 try {
118 $twigContainer = new TwigContainer(null, $GLOBALS['kernel']);
119 $twig = $twigContainer->getTwig();
120 if (!empty($ret['template_name'])) {
121 $templateData = json_decode($ret['body'], true);
122 // we make sure to prefix this so that people have to work inside the openemr namespace for email templates
123 $htmlBody = $twig->render($ret['template_name'] . ".html.twig", $templateData);
124 $textBody = $twig->render($ret['template_name'] . ".text.twig", $templateData);
125 } else {
126 $htmlBody = $twig->render("emails/system/system-notification.html.twig", ['message' => $ret['body']]);
127 $textBody = $twig->render("emails/system/system-notification.text.twig", ['message' => $ret['body']]);
130 $mail = new MyMailer();
131 $email_subject = $ret['subject'];
132 $email_sender = $ret['sender'];
133 $email_address = $ret['recipient'];
134 $mail->AddReplyTo($email_sender, $email_sender);
135 $mail->SetFrom($email_sender, $email_sender);
136 $mail->AddAddress($email_address);
137 $mail->Subject = $email_subject;
138 $mail->MsgHTML($htmlBody);
139 $mail->AltBody = $textBody;
140 $mail->IsHTML(true);
141 if (!$mail->Send()) {
142 sqlStatement("UPDATE `email_queue` SET `error` = 1, `error_message`= ?, , `datetime_error` = NOW() WHERE `id` = ?", [$mail->ErrorInfo, $ret['id']]);
143 error_log("Failed to send email notification through Mymailer emailServiceRun with error " . errorLogEscape($mail->ErrorInfo));
145 } catch (\Exception $e) {
146 (new SystemLogger())->errorLogCaller("Failed to generate email contents for queued email" . $e->getMessage(), ['trace' => $e->getTraceAsString(), 'id' => $ret['id']]);
147 sqlStatement("UPDATE `email_queue` SET `error` = 1, `error_message`= ?, `datetime_error` = NOW() WHERE `id` = ?", [$e->getMessage(), $ret['id']]);
149 } else {
150 sqlStatement("UPDATE `email_queue` SET `error` = 1, `error_message`= 'email method is not configured correctly', `datetime_error` = NOW() WHERE `id` = ?", [$ret['id']]);
151 error_log("Failed to send email notification through Mymailer since email method is not configured correctly");
157 function emailMethod()
159 global $HTML_CHARSET;
160 $this->CharSet = $HTML_CHARSET;
161 switch ($GLOBALS['EMAIL_METHOD']) {
162 case "PHPMAIL":
163 $this->Mailer = "mail";
164 break;
165 case "SMTP":
166 $this->Mailer = "smtp";
167 $this->SMTPAuth = $GLOBALS['SMTP_Auth'];
168 $this->Host = $GLOBALS['SMTP_HOST'];
169 $this->Username = $GLOBALS['SMTP_USER'];
170 $cryptoGen = new CryptoGen();
171 $this->Password = $cryptoGen->decryptStandard($GLOBALS['SMTP_PASS']);
172 $this->Port = $GLOBALS['SMTP_PORT'];
173 $this->SMTPSecure = $GLOBALS['SMTP_SECURE'];
174 break;
175 case "SENDMAIL":
176 $this->Mailer = "sendmail";
177 break;