Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/app/Console/Commands/RabbitMQEventConsumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ private function sendCallQualitySurveyEmail($email, $recipientName, $typePayload
$userId = array_key_exists('user_id', $typePayload) ? (int) $typePayload['user_id'] : 1;
$surveyBaseUrl = \App\Helpers\MainHelper::createUrl('survey/callquality');

$user = User::findOrFail($userId);
$surveyLinks = [];
for ($rating = 1; $rating <= 5; $rating++) {
$token = TokenHelper::createSurveyToken('call_quality', [
Expand All @@ -312,6 +313,7 @@ private function sendCallQualitySurveyEmail($email, $recipientName, $typePayload
'recipient_name' => $recipientName,
'workspace_id' => $workspaceId,
'user_id' => $userId,
'user' => $user,
'token' => '',
'survey_links' => $surveyLinks
]);
Expand Down Expand Up @@ -503,6 +505,7 @@ public function handleWorkspaceSuspended($msg)
$emailData = array(
'workspace' => $workspace,
'owner' => $owner,
'user' => $owner,
'event' => $payload,
'event_data' => $data,
'reference_id' => isset($data['id']) ? $data['id'] : null,
Expand Down
155 changes: 117 additions & 38 deletions app/app/Helpers/EmailHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,118 @@
use Swift_SmtpTransport;
use Swift_Message;
use Exception;
use Illuminate\Database\Eloquent\Model;

final class EmailHelper {

/**
* Categorized mappings matching the Eloquent model attributes / HTML form input names.
* Removed the 'array' type hint from the property definition for maximum PHP 7.4 compliance.
*
* @var array
*/
private static $categorizedEmails = [
'auditing' => [
'admin_email',
'app_feedback_request',
'call_activity_alert',
'call_quality_survey',
'contact',
'contact_confirm',
'support_ticket_created',
'support_ticket_updated',
'usage_trigger',
],
'account_changes' => [
'billing_agreement_cancelled',
'billing_failed',
'card_expiring',
'deactivated_account',
'did_purchased',
'failed_upgrade',
'free_trial_expiring',
'inactive_user',
'one_time_login_link',
'password',
'password_reset',
'password_was_reset',
'payment_receipt',
'plan_upgraded',
'quote',
'quote_confirm',
'reactivated_account',
'service_plan_being_migrated',
'two_factor_acknowledgement',
'unknown_device_login',
'verify_email',
'welcome_email',
],
'workspace_changes' => [
'extension_created',
'invited_to_workspace',
'phone_created',
'sip_credentials',
'workspace_account_suspended',
'workspace_invoices',
'workspace_suspended_admin',
],
'system_status_updates' => [
'alert_email',
'port_started',
'ports_status_completed',
'ports_status_confirmed',
'ports_status_needs_info',
'ports_status_received',
'ports_status_submitted',
'sys_update',
'test_email',
],
'debugger' => [
'bug_report',
'debugger_error',
],
];

/**
* Check if a template falls under a category the user has disabled (set to false) on their model.
* PHP 7.4 compliant nullable type hint syntax.
*
* @param Model|null $user
* @param string $templateName
* @return bool
*/
private static function isUnsubscribed($user, $templateName)
{
if (!$user) {
return false;
}

foreach (self::$categorizedEmails as $category => $templates) {
if (in_array($templateName, $templates, true)) {
return isset($user->$category) && (bool) $user->$category === false;
}
}
return false;
}

public static function sendWithPHPMailer($subject, $to, $template, $data) {
$apiCreds = ApiCredentialKVStore::getRecord();
$mail = new PHPMailer(true); // Enable exceptions
$mail = new PHPMailer(true);

try {
// Server settings
$mail->isSMTP();
$mail->Host = $apiCreds->smtp_host;
$mail->SMTPAuth = true;
$mail->Username = $apiCreds->smtp_user;
$mail->Password = $apiCreds->smtp_password;
$mail->Port = (int) $apiCreds->smtp_port;

// Dynamic SSL/TLS Configuration
// Note: 'ssl' (port 465) maps to ENCRYPTION_SMTPS
// Note: 'tls' (port 587/2525) maps to ENCRYPTION_STARTTLS
if ($apiCreds->smtp_tls === 'ssl') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
} else if ($apiCreds->smtp_tls === 'tls') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
}

// SSL Certificate Verification (Useful for internal relays,
// but use with caution on production public networks)
$mail->SMTPOptions = [
'ssl' => [
'verify_peer' => false,
Expand All @@ -48,39 +134,24 @@ public static function sendWithPHPMailer($subject, $to, $template, $data) {
]
];

// Recipients
$mail->setFrom($apiCreds->smtp_user, MainHelper::getSiteName());
$mail->addAddress($to);

// Content
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = view('emails.'.$template, $data)->render();

return $mail->send();
} catch (\Exception $e) {
// Log the error if necessary for debugging
return $mail->ErrorInfo;
}
}
/**
* Sends email using the native Swift Mailer library for more granular control.
*/

public static function sendEmailDirect($subject, $to, $template, $data) {
$apiCreds = ApiCredentialKVStore::getRecord();

Log::info("EmailHelper: Starting Direct Swift process for $to");

try {
// 1. Determine Encryption
$encryption = null;
if ($apiCreds->smtp_tls === 'tls') {
$encryption = 'tls';
} elseif ($apiCreds->smtp_tls === 'ssl') {
$encryption = 'ssl';
}

// 2. Create the Transport
$transport = Swift_SmtpTransport::newInstance(
$apiCreds->smtp_host,
2525,
Expand All @@ -89,7 +160,6 @@ public static function sendEmailDirect($subject, $to, $template, $data) {
->setUsername($apiCreds->smtp_user)
->setPassword($apiCreds->smtp_password);

// 3. Bypass SSL Verification (Direct Stream Options)
$transport->setStreamOptions([
'ssl' => [
'allow_self_signed' => true,
Expand All @@ -98,23 +168,16 @@ public static function sendEmailDirect($subject, $to, $template, $data) {
]
]);

// 4. Instantiate the Mailer
$mailer = Swift_Mailer::newInstance($transport);

// 5. Build the Body using Laravel's View factory
$siteName = MainHelper::getSiteName();

$htmlBody = view('emails.'.$template, $data)->render();

// 6. Construct the Message
$message = Swift_Message::newInstance($subject)
->setFrom([$apiCreds->smtp_user => $siteName])
->setTo([$to])
->setBody($htmlBody, 'text/html');

// 8. Send
$result = $mailer->send($message);

Log::info("EmailHelper: Direct Swift Dispatch successful. Units sent: $result");
return TRUE;

Expand All @@ -132,12 +195,10 @@ public static function sendWithSwift($subject, $to, $template, $data) {
$apiCreds = ApiCredentialKVStore::getRecord();
$siteName = MainHelper::getSiteName();


Log::info("EmailHelper: Starting process for $to using {$customizations->mail_provider}");

if ($customizations->mail_provider == 'smtp-gateway') {
try {
// 1. Dynamic Config Mapping
Config::set('mail.driver', 'smtp');
Config::set('mail.host', $apiCreds->smtp_host);
Config::set('mail.port', (int) $apiCreds->smtp_port);
Expand All @@ -150,10 +211,8 @@ public static function sendWithSwift($subject, $to, $template, $data) {
} elseif ($apiCreds->smtp_tls === 'ssl') {
$encryption = 'ssl';
}

Config::set('mail.encryption', $encryption);

// --- NEW: Bypass SSL Verification ---
Config::set('mail.stream', [
'ssl' => [
'allow_self_signed' => true,
Expand All @@ -162,11 +221,9 @@ public static function sendWithSwift($subject, $to, $template, $data) {
],
]);

// Set From address to match SMTP username
Config::set('mail.from.address', $apiCreds->smtp_user);
Config::set('mail.from.name', $siteName);

// 2. Refresh Mailer Service
(new MailServiceProvider(app()))->register();

Log::info("EmailHelper: Attempting SMTP dispatch.", [
Expand All @@ -176,7 +233,6 @@ public static function sendWithSwift($subject, $to, $template, $data) {
'username' => Config::get('mail.username'),
]);

// 3. Send and Log Payload
Mail::send('emails.'.$template, $data, function ($message) use ($subject, $to) {
$message->to($to);
$message->subject($subject);
Expand All @@ -193,7 +249,30 @@ public static function sendWithSwift($subject, $to, $template, $data) {
}
return FALSE;
}

/**
* Master send method. Intercepts unsubscribed users based on their Eloquent model boolean settings.
* Fully compatible with scalar and nullable type hints on PHP 7.4.
*
* @param string $subject
* @param string $to
* @param string $template
* @param array $data
* @param Model|null $user
* @param string $mailLib
* @return mixed
*/
public static function sendEmail($subject, $to, $template, $data, $mailLib='SWIFT') {
$user = NULL;
if (!empty($data['user']) && $data['user'] instanceof Model) {
$user = $data['user'];
}

if (self::isUnsubscribed($user, $template)) {
Log::info("EmailHelper: Dispatch skipped. User ID " . ($user->id ?? 'unknown') . " ($to) has disabled the category containing '$template'.");
return FALSE;
}

$data['site_name'] = MainHelper::getSiteName();
$data['customizations'] = CustomizationsKVStore::getRecord();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function saveExtension(Request $request)
$status = SIPRouterHelper::provision($user, $workspace, $extensions);
if ($status) {
$mail = Config::get("mail");
$data = compact('extension', 'workspace');
$data = compact('extension', 'workspace', 'user');
$subject = "Extension Created";
if ($workspaceUser->auditing) {
$result = EmailHelper::sendEmail($subject, $user->email, 'extension_created', $data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ public function saveNumber(Request $request)
]);
$mail = Config::get("mail");
$data = array(
"did" => $number
"did" => $number,
"user" => $user,
);
$subject = "DID Purchased";
$result = EmailHelper::sendEmail($subject, $user->email, 'did_purchased', $data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ private function startLogRoutine($level, $params, $workspace, $creator) {
$data = [
'params' => $params,
'workspace' => $workspace,
'creator' => $creator
'creator' => $creator,
'user' => $creator
];
$mail = Config::get('mail');
$log = DebuggerLog::create($params);
Expand Down
2 changes: 1 addition & 1 deletion app/app/Http/Controllers/Api/Phone/PhoneController.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public function savePhone(Request $request)
$phoneDef = PhoneDefinition::where('phone_type', $data['phone_type'])->first();
PhoneTag::updateModelTags($tags, $phone->id);
$mail = Config::get("mail");
$data = compact('phone', 'phoneDef');
$data = compact('phone', 'phoneDef', 'user');
$subject = "Phone Created";
$result = EmailHelper::sendEmail($subject, $user->email, 'phone_created', $data);
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ public function updateNumber(Request $request, $numberId)
return $this->errorInternal(sprintf("One of the documents could not be uploaded please be sure to upload a file size less than %s and use one of the following file formats: %s", self::$maxDocumentSizeReadable, implode(",", self::$acceptedDocumentFormats)));
}
$subject = "Port Number Request Updated";
$result = EmailHelper::sendEmail($subject, $user->email, 'port_updated', $data);
$emailData = $data;
$emailData['user'] = $user;
$result = EmailHelper::sendEmail($subject, $user->email, 'port_updated', $emailData);
/*
Mail::send('emails.port_updated', $data, function ($message) use ($user, $mail) {
$message->to($user->email);
Expand Down
7 changes: 4 additions & 3 deletions app/app/Http/Controllers/MergedController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1445,7 +1445,7 @@ public function emailSIPCredentials(Request $request) {
$sipHost = $workspace->sipURL();
$sipRouter = SIPRouter::getMainRouter();

$sipCredentials = [
$emailData = [
'username' => $sipUsername,
'password' => $sipPassword,
'host' => $sipHost,
Expand All @@ -1454,15 +1454,16 @@ public function emailSIPCredentials(Request $request) {
'websocket_settings' => [
'port' => $sipRouter['wss_port'] ?? 7443,
'gateway' => 'wss://' . $sipHost . ':' . ($sipRouter['wss_port'] ?? 7443)
]
],
'user' => $user
];


\App\Helpers\EmailHelper::sendEmail(
'Your SIP Credentials',
$data['to_email'],
'sip_credentials',
$sipCredentials
$emailData
);

return $this->response->noContent();
Expand Down
Loading