PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.13
Yatra – Travel Booking & Tour Operator Software v3.0.13
3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 All 82 releases
yatra / app / Services / EmailService.php

EmailService.php in Yatra – Travel Booking & Tour Operator Software 3.0.13, at app/Services/EmailService.php

301 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Email Service
4 *
5 * Handles email sending with SMTP support
6 *
7 * @package Yatra\Services
8 * @since 3.0.0
9 */
10
11 declare(strict_types=1);
12
13 namespace Yatra\Services;
14
15 use PHPMailer\PHPMailer\PHPMailer;
16 use PHPMailer\PHPMailer\Exception;
17
18 class EmailService
19 {
20 /**
21 * Send email using WordPress wp_mail or SMTP
22 *
23 * @param string|array $to Recipient email address(es)
24 * @param string $subject Email subject
25 * @param string $message Email message
26 * @param array $headers Optional headers
27 * @param array $attachments Optional attachments
28 * @return bool True on success, false on failure
29 */
30 public static function send($to, string $subject, string $message, array $headers = [], array $attachments = []): bool
31 {
32 // Archive copy of every Yatra email, if the operator configured one.
33 // Applied here rather than at each call site so it covers all senders —
34 // core transactional mail, Pro automation templates and sequences — and
35 // cannot be forgotten by a sender added later.
36 $headers = self::withAlwaysBcc($headers);
37
38 // Check if SMTP is enabled
39 $smtp_enabled = SettingsService::isEnabled('smtp_enabled');
40
41 if ($smtp_enabled) {
42 return self::sendViaSMTP($to, $subject, $message, $headers, $attachments);
43 }
44
45 // Use default WordPress wp_mail
46 return self::sendViaWpMail($to, $subject, $message, $headers, $attachments);
47 }
48
49 /**
50 * Merge the configured always-BCC address into a header list.
51 *
52 * Opt-in: with the setting empty nothing is added and the headers are handed
53 * back untouched, so an operator who never configures it sees no change at
54 * all. When a template already carries its own Bcc header the two are merged
55 * and de-duplicated, so an address listed in both places is only copied once.
56 *
57 * @param string[] $headers
58 * @return string[]
59 */
60 private static function withAlwaysBcc(array $headers): array
61 {
62 // Cc/Bcc configured on the template being sent. Read here rather than at
63 // the call site because Yatra Pro can take the send over and mail it
64 // through its own service — both routes end up here.
65 foreach (TransactionalEmailTemplateService::headersForCurrentDispatch() as $templateHeader) {
66 $headers[] = $templateHeader;
67 }
68
69 $configured = TransactionalEmailTemplateService::sanitizeAddressList(
70 (string) SettingsService::get('email_always_bcc', '')
71 );
72
73 /**
74 * Filter the always-BCC recipients for a single send. Return an empty
75 * array to skip archiving this particular email.
76 *
77 * @param string[] $configured
78 * @param string[] $headers
79 */
80 $configured = (array) apply_filters('yatra_email_always_bcc', $configured, $headers);
81
82 if ($configured === []) {
83 return $headers;
84 }
85
86 // Fold any Bcc headers already present into the same list so the address
87 // cannot be added twice, then re-emit a single combined header.
88 $existing = [];
89 $kept = [];
90
91 foreach ($headers as $header) {
92 if (is_string($header) && stripos(trim($header), 'bcc:') === 0) {
93 $existing = array_merge(
94 $existing,
95 TransactionalEmailTemplateService::sanitizeAddressList(trim(substr(trim($header), 4)))
96 );
97 continue;
98 }
99
100 $kept[] = $header;
101 }
102
103 $all = [];
104
105 foreach (array_merge($existing, $configured) as $address) {
106 $all[strtolower($address)] = $address;
107 }
108
109 if ($all === []) {
110 return $headers;
111 }
112
113 $kept[] = 'Bcc: ' . implode(', ', array_values($all));
114
115 return $kept;
116 }
117
118 /**
119 * Send email via WordPress wp_mail with custom from address
120 */
121 private static function sendViaWpMail($to, string $subject, string $message, array $headers, array $attachments): bool
122 {
123 // Add custom from address if configured
124 add_filter('wp_mail_from', [self::class, 'customFromEmail']);
125 add_filter('wp_mail_from_name', [self::class, 'customFromName']);
126
127 $result = wp_mail($to, $subject, $message, $headers, $attachments);
128
129 // Remove filters after sending
130 remove_filter('wp_mail_from', [self::class, 'customFromEmail']);
131 remove_filter('wp_mail_from_name', [self::class, 'customFromName']);
132
133 return $result;
134 }
135
136 /**
137 * Send email via SMTP
138 */
139 private static function sendViaSMTP($to, string $subject, string $message, array $headers, array $attachments): bool
140 {
141 // Get SMTP settings
142 $smtp_host = SettingsService::getString('smtp_host', 'smtp.gmail.com');
143 $smtp_port = SettingsService::getInt('smtp_port', 587);
144 $smtp_username = SettingsService::getString('smtp_username', '');
145 $smtp_password = SettingsService::getString('smtp_password', '');
146 $smtp_encryption = SettingsService::getString('smtp_encryption', 'tls');
147 $from_email = SettingsService::getString('from_email', get_option('admin_email'));
148 $from_name = SettingsService::getString('from_name', get_bloginfo('name'));
149
150 // Validate required settings
151 if (empty($smtp_host) || empty($smtp_username) || empty($smtp_password)) {
152 return false;
153 }
154
155 try {
156 $mail = new PHPMailer(true);
157
158 // Server settings
159 $mail->isSMTP();
160 $mail->Host = $smtp_host;
161 $mail->SMTPAuth = true;
162 $mail->Username = $smtp_username;
163 $mail->Password = $smtp_password;
164 self::configureSmtpEncryption($mail, $smtp_encryption);
165 $mail->Port = $smtp_port;
166
167 // Recipients
168 $mail->setFrom($from_email, $from_name);
169
170 if (is_array($to)) {
171 foreach ($to as $recipient) {
172 $mail->addAddress($recipient);
173 }
174 } else {
175 $mail->addAddress($to);
176 }
177
178 // Process headers
179 foreach ($headers as $header) {
180 if (stripos($header, 'content-type:') === 0) {
181 if (stripos($header, 'text/html') !== false) {
182 $mail->isHTML(true);
183 }
184 } elseif (stripos($header, 'cc:') === 0) {
185 $cc = trim(substr($header, 3));
186 $mail->addCC($cc);
187 } elseif (stripos($header, 'bcc:') === 0) {
188 $bcc = trim(substr($header, 4));
189 $mail->addBCC($bcc);
190 } elseif (stripos($header, 'reply-to:') === 0) {
191 $reply_to = trim(substr($header, 9));
192 $mail->addReplyTo($reply_to);
193 }
194 }
195
196 // Content
197 $mail->Subject = $subject;
198 $mail->Body = $message;
199
200 // Attachments
201 foreach ($attachments as $attachment) {
202 $mail->addAttachment($attachment);
203 }
204
205 $mail->send();
206 return true;
207
208 } catch (Exception $e) {
209 return false;
210 }
211 }
212
213 private static function configureSmtpEncryption(PHPMailer $mail, string $smtp_encryption): void
214 {
215 if ($smtp_encryption === 'ssl') {
216 $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
217 $mail->SMTPAutoTLS = true;
218
219 return;
220 }
221 if ($smtp_encryption === 'tls') {
222 $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
223 $mail->SMTPAutoTLS = true;
224
225 return;
226 }
227 $mail->SMTPSecure = '';
228 $mail->SMTPAutoTLS = false;
229 }
230
231 /**
232 * Custom from email filter
233 */
234 public static function customFromEmail($email)
235 {
236 $from_email = SettingsService::getString('from_email', '');
237 return !empty($from_email) ? $from_email : $email;
238 }
239
240 /**
241 * Custom from name filter
242 */
243 public static function customFromName($name)
244 {
245 $from_name = SettingsService::getString('from_name', '');
246 return !empty($from_name) ? $from_name : $name;
247 }
248
249 /**
250 * Test SMTP connection
251 *
252 * @return array Result with success status and message
253 */
254 public static function testSMTPConnection(): array
255 {
256 $smtp_host = SettingsService::getString('smtp_host', '');
257 $smtp_port = SettingsService::getInt('smtp_port', 587);
258 $smtp_username = SettingsService::getString('smtp_username', '');
259 $smtp_password = SettingsService::getString('smtp_password', '');
260 $smtp_encryption = SettingsService::getString('smtp_encryption', 'tls');
261
262 if (empty($smtp_host) || empty($smtp_username) || empty($smtp_password)) {
263 return [
264 'success' => false,
265 'message' => __('Please configure all SMTP settings first.', 'yatra')
266 ];
267 }
268
269 try {
270 $mail = new PHPMailer(true);
271 $mail->isSMTP();
272 $mail->Host = $smtp_host;
273 $mail->SMTPAuth = true;
274 $mail->Username = $smtp_username;
275 $mail->Password = $smtp_password;
276 self::configureSmtpEncryption($mail, $smtp_encryption);
277 $mail->Port = $smtp_port;
278 $mail->Timeout = 10;
279
280 // Test connection
281 $mail->smtpConnect();
282 $mail->smtpClose();
283
284 return [
285 'success' => true,
286 'message' => __('SMTP connection successful!', 'yatra')
287 ];
288
289 } catch (Exception $e) {
290 return [
291 'success' => false,
292 'message' => sprintf(
293 /* translators: %s: SMTP error message. */
294 __('SMTP connection failed: %s', 'yatra'),
295 $e->getMessage()
296 )
297 ];
298 }
299 }
300 }
301