config = $appConfig['email']; $this->enabled = $this->config['enabled'] ?? false; // Load email templates require_once __DIR__ . '/EmailTemplates.php'; } /** * Send email via Mailgun API or SMTP */ public function send($to, $subject, $text, $html = null) { if (!$this->enabled) { error_log('Email not sent (service disabled): ' . $subject); return ['success' => true, 'message' => 'Email disabled']; } $service = $this->config['service'] ?? 'mailgun'; if ($service === 'smtp') { return $this->sendViaSMTP($to, $subject, $text, $html); } else { return $this->sendViaMailgunAPI($to, $subject, $text, $html); } } /** * Send templated email */ public function sendTemplate($to, $templateName, $data) { $html = EmailTemplates::getTemplate($templateName, $data); // Extract subject from HTML title preg_match('/]*>(.*?)<\/h1>/', $html, $matches); $subject = $matches[1] ?? 'L\'Oréal OMG Assistant Notification'; // Create plain text version from data $text = strip_tags($subject) . "\n\n" . json_encode($data, JSON_PRETTY_PRINT); return $this->send($to, $subject, $text, $html); } /** * Send email via Mailgun API */ private function sendViaMailgunAPI($to, $subject, $text) { try { $url = 'https://api.mailgun.net/v3/' . $this->config['domain'] . '/messages'; $postData = [ 'from' => $this->config['from'], 'to' => $to, 'subject' => $subject, 'text' => $text ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_RETURNTRANSFER => true, CURLOPT_USERPWD => 'api:' . $this->config['mailgun_api_key'], CURLOPT_HTTPAUTH => CURLAUTH_BASIC, CURLOPT_TIMEOUT => 10 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($curlError) { error_log('Email send failed: ' . $curlError); return [ 'success' => false, 'error' => 'Email delivery failed', 'details' => $curlError ]; } if ($httpCode !== 200) { error_log('Email send failed (HTTP ' . $httpCode . '): ' . $response); return [ 'success' => false, 'error' => 'Email service error', 'details' => 'HTTP ' . $httpCode, 'httpCode' => $httpCode ]; } error_log('Email sent successfully to ' . $to); return [ 'success' => true, 'httpCode' => $httpCode ]; } catch (Exception $e) { error_log('Email exception: ' . $e->getMessage()); return [ 'success' => false, 'error' => 'Email exception', 'details' => $e->getMessage() ]; } } /** * Send email via SMTP */ private function sendViaSMTP($to, $subject, $text, $html = null) { try { error_log('===== EMAIL SEND START ====='); error_log('To: ' . $to); error_log('Subject: ' . $subject); error_log('Service: SMTP'); $from = $this->config['from']; $host = $this->config['smtp_host']; $port = $this->config['smtp_port']; $username = $this->config['smtp_username']; $password = $this->config['smtp_password']; error_log('SMTP Host: ' . $host . ':' . $port); error_log('SMTP User: ' . $username); error_log('From: ' . $from); // Build email message with multipart if HTML provided $boundary = md5(time()); $headers = "From: {$from}\r\n"; $headers .= "Reply-To: {$from}\r\n"; $headers .= "X-Mailer: PHP/" . phpversion() . "\r\n"; $headers .= "MIME-Version: 1.0\r\n"; if ($html) { $headers .= "Content-Type: multipart/alternative; boundary=\"{$boundary}\"\r\n"; } else { $headers .= "Content-Type: text/plain; charset=UTF-8\r\n"; } // Create SMTP connection error_log('Connecting to SMTP server...'); $socket = fsockopen($host, $port, $errno, $errstr, 10); if (!$socket) { error_log('SMTP connection failed: ' . $errstr . ' (errno: ' . $errno . ')'); return [ 'success' => false, 'error' => 'SMTP connection failed', 'details' => $errstr ]; } error_log('SMTP connection established'); // SMTP conversation $this->smtpCommand($socket, null, 220); // Wait for greeting $this->smtpCommand($socket, "EHLO " . $host, 250); $this->smtpCommand($socket, "AUTH LOGIN", 334); $this->smtpCommand($socket, base64_encode($username), 334); $this->smtpCommand($socket, base64_encode($password), 235); $this->smtpCommand($socket, "MAIL FROM: <{$from}>", 250); $this->smtpCommand($socket, "RCPT TO: <{$to}>", 250); $this->smtpCommand($socket, "DATA", 354); // Send email data $message = "Subject: {$subject}\r\n"; $message .= $headers; $message .= "\r\n"; if ($html) { // Multipart message $message .= "--{$boundary}\r\n"; $message .= "Content-Type: text/plain; charset=UTF-8\r\n\r\n"; $message .= $text . "\r\n\r\n"; $message .= "--{$boundary}\r\n"; $message .= "Content-Type: text/html; charset=UTF-8\r\n\r\n"; $message .= $html . "\r\n\r\n"; $message .= "--{$boundary}--\r\n"; } else { // Plain text only $message .= $text . "\r\n"; } $message .= ".\r\n"; error_log('Sending email data...'); $this->smtpCommand($socket, $message, 250); error_log('Sending QUIT command...'); $this->smtpCommand($socket, "QUIT", 221); fclose($socket); error_log('✅ Email sent successfully via SMTP to ' . $to); error_log('===== EMAIL SEND END (SUCCESS) ====='); return [ 'success' => true, 'method' => 'smtp' ]; } catch (Exception $e) { error_log('❌ SMTP exception: ' . $e->getMessage()); error_log('Stack trace: ' . $e->getTraceAsString()); error_log('===== EMAIL SEND END (FAILED) ====='); return [ 'success' => false, 'error' => 'SMTP exception', 'details' => $e->getMessage() ]; } } /** * Send SMTP command and check response */ private function smtpCommand($socket, $command, $expectedCode) { if ($command !== null) { fwrite($socket, $command . "\r\n"); } $response = ''; while ($line = fgets($socket, 515)) { $response .= $line; if (substr($line, 3, 1) === ' ') { break; } } $code = intval(substr($response, 0, 3)); if ($code !== $expectedCode) { throw new Exception("SMTP Error: Expected {$expectedCode}, got {$code}. Response: {$response}"); } return $response; } /** * Send process started notification */ public function notifyStarted($userEmail, $data) { return $this->sendTemplate($userEmail, 'global_to_local_started', $data); } /** * Send process completed notification */ public function notifyCompleted($userEmail, $data) { return $this->sendTemplate($userEmail, 'global_to_local_complete', $data); } /** * Send error notification */ public function notifyError($userEmail, $data) { return $this->sendTemplate($userEmail, 'global_to_local_failed', $data); } /** * Send asset submission success notification */ public function notifyAssetSubmissionSuccess($userEmail, $data) { return $this->sendTemplate($userEmail, 'asset_submission_success', $data); } /** * Send asset submission failed notification */ public function notifyAssetSubmissionFailed($userEmail, $data) { return $this->sendTemplate($userEmail, 'asset_submission_failed', $data); } }