mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 15:07:24 +00:00
513 lines
21 KiB
PHP
513 lines
21 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Admin > Settings > Mail (admin/settings_mail.php). Each tab's form saves on
|
|
* its own and comes back to its tab; what changed is in the audit log, from
|
|
* what to what - passwords, the OAuth secret and the refresh token only as
|
|
* changed. Those are never sent to the page: left empty, they stay as saved.
|
|
* A server that isn't a name, a port that isn't one, an address that isn't an
|
|
* email address comes back to the tab saying so, and nothing is saved. Then
|
|
* the tests, the previews, and connecting Microsoft 365
|
|
* (admin/oauth_microsoft_mail_callback.php finishes that).
|
|
*/
|
|
|
|
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
|
|
|
// Saves and tests return to the tab they were submitted from (see settings_mail.php)
|
|
$mail_tabs = ['sending', 'receiving', 'oauth', 'from', 'tests', 'previews'];
|
|
$mail_tab_redirect = 'settings_mail.php';
|
|
if (isset($_POST['tab']) && in_array($_POST['tab'], $mail_tabs, true)) {
|
|
$mail_tab_redirect .= '?tab=' . $_POST['tab'];
|
|
}
|
|
|
|
if (!defined('MICROSOFT_OAUTH_BASE_URL')) {
|
|
define('MICROSOFT_OAUTH_BASE_URL', 'https://login.microsoftonline.com/');
|
|
}
|
|
|
|
if (isset($_POST['edit_mail_smtp_settings']) || isset($_POST['edit_mail_imap_settings'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$server = isset($_POST['edit_mail_smtp_settings']) ? 'smtp' : 'imap';
|
|
$before = settingsCurrent(array_map(function ($column) use ($server) {
|
|
return "config_{$server}_$column";
|
|
}, ['provider', 'host', 'port', 'encryption', 'username', 'password']));
|
|
|
|
[$after, $errors] = settingsMailServerCheck($server, $before, $_POST);
|
|
if ($errors) {
|
|
settingsRefuse('mail', $errors, $mail_tab_redirect);
|
|
}
|
|
|
|
settingsSave('mail', $before, $after);
|
|
|
|
redirect($mail_tab_redirect);
|
|
|
|
}
|
|
|
|
/*
|
|
* The OAuth app - its ID, secret and tenant (Microsoft only; kept as saved
|
|
* while Microsoft isn't picked), and the refresh token. No ID forgets the app:
|
|
* its secret and token go too. A changed app or token makes what ITFlow last
|
|
* signed in with useless, so that is fetched afresh when next needed.
|
|
* Connect Microsoft 365 saves the same way, then goes to Microsoft to sign in.
|
|
*/
|
|
if (isset($_POST['edit_mail_oauth_settings']) || isset($_POST['oauth_connect_microsoft_mail'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$before = settingsCurrent(['config_mail_oauth_client_id', 'config_mail_oauth_client_secret', 'config_mail_oauth_tenant_id', 'config_mail_oauth_refresh_token',
|
|
'config_mail_oauth_access_token', 'config_mail_oauth_access_token_expires_at']);
|
|
|
|
$client_id = trim((string) ($_POST['config_mail_oauth_client_id'] ?? $before['config_mail_oauth_client_id']));
|
|
$secret = trim((string) ($_POST['config_mail_oauth_client_secret'] ?? ''));
|
|
$tenant = trim((string) ($_POST['config_mail_oauth_tenant_id'] ?? $before['config_mail_oauth_tenant_id']));
|
|
// A token pasted in with line breaks is still one token
|
|
$refresh_token = preg_replace('/\s+/', '', (string) ($_POST['config_mail_oauth_refresh_token'] ?? ''));
|
|
|
|
$errors = [];
|
|
if (preg_match('/\s/', $client_id)) {
|
|
$errors['config_mail_oauth_client_id'] = 'No spaces in it - copy it again from the app.';
|
|
} elseif (mb_strlen($client_id) > 255) {
|
|
$errors['config_mail_oauth_client_id'] = 'Up to 255 characters.';
|
|
}
|
|
if (mb_strlen($secret) > 255) {
|
|
$errors['config_mail_oauth_client_secret'] = 'Up to 255 characters.';
|
|
}
|
|
if ($tenant !== '' && (mb_strlen($tenant) > 255 || !preg_match('/^[A-Za-z0-9.-]+$/', $tenant))) {
|
|
$errors['config_mail_oauth_tenant_id'] = 'The Directory (tenant) ID, as 00000000-0000-0000-0000-000000000000 - or your domain, as example.onmicrosoft.com.';
|
|
} elseif ($tenant === '' && $client_id !== '' && isset($_POST['config_mail_oauth_tenant_id'])) {
|
|
// Only asked while Sending or Receiving is on Microsoft 365
|
|
$errors['config_mail_oauth_tenant_id'] = 'Microsoft 365 needs it - the Directory (tenant) ID, from the app registration\'s Overview.';
|
|
}
|
|
if ($errors) {
|
|
settingsRefuse('mail', $errors, $mail_tab_redirect);
|
|
}
|
|
|
|
$after = [
|
|
'config_mail_oauth_client_id' => $client_id,
|
|
'config_mail_oauth_client_secret' => $client_id === '' ? '' : ($secret !== '' ? $secret : (string) $before['config_mail_oauth_client_secret']),
|
|
'config_mail_oauth_tenant_id' => $tenant,
|
|
'config_mail_oauth_refresh_token' => $client_id === '' ? '' : ($refresh_token !== '' ? $refresh_token : (string) $before['config_mail_oauth_refresh_token']),
|
|
];
|
|
foreach ($after as $column => $value) {
|
|
if (!settingsSame($before[$column], $value)) {
|
|
$after['config_mail_oauth_access_token'] = '';
|
|
$after['config_mail_oauth_access_token_expires_at'] = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$changed = settingsWrite('settings', $before, $after);
|
|
// What it signed in with is only ever fetched - not something anyone changed
|
|
unset($changed['config_mail_oauth_access_token'], $changed['config_mail_oauth_access_token_expires_at']);
|
|
$lines = settingsChangeLines($before, $changed);
|
|
|
|
if (isset($_POST['edit_mail_oauth_settings'])) {
|
|
settingsSaid('mail', $lines);
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
// ---- Connect Microsoft 365 ----
|
|
if ($lines) {
|
|
settingsSaid('mail', $lines);
|
|
}
|
|
|
|
// The providers as saved - they are on the other tabs' forms
|
|
if ($config_imap_provider !== 'microsoft_oauth' && $config_smtp_provider !== 'microsoft_oauth') {
|
|
flashAlert("Set Sending or Receiving to Microsoft 365 and save it, then connect.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$config_mail_oauth_client_id = $after['config_mail_oauth_client_id'];
|
|
$config_mail_oauth_client_secret = $after['config_mail_oauth_client_secret'];
|
|
$config_mail_oauth_tenant_id = $after['config_mail_oauth_tenant_id'];
|
|
if ($config_mail_oauth_client_id === '' || $config_mail_oauth_client_secret === '' || $config_mail_oauth_tenant_id === '') {
|
|
flashAlert("The app's client ID, secret and tenant ID first - then connect.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
if (defined('BASE_URL') && !empty(BASE_URL)) {
|
|
$base_url = rtrim((string) BASE_URL, '/');
|
|
} else {
|
|
$base_url = 'https://' . rtrim((string) $config_base_url, '/');
|
|
}
|
|
|
|
$redirect_uri = $base_url . '/admin/oauth_microsoft_mail_callback.php';
|
|
|
|
try {
|
|
$state = bin2hex(random_bytes(32));
|
|
} catch (Throwable $e) {
|
|
$state = sha1(uniqid((string) mt_rand(), true));
|
|
}
|
|
|
|
$_SESSION['mail_oauth_state'] = $state;
|
|
$_SESSION['mail_oauth_state_expires_at'] = time() + 600;
|
|
|
|
$scope = 'offline_access openid profile https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send';
|
|
|
|
$authorize_url = MICROSOFT_OAUTH_BASE_URL . rawurlencode($config_mail_oauth_tenant_id) . '/oauth2/v2.0/authorize?'
|
|
. http_build_query([
|
|
'client_id' => $config_mail_oauth_client_id,
|
|
'response_type' => 'code',
|
|
'redirect_uri' => $redirect_uri,
|
|
'response_mode' => 'query',
|
|
'scope' => $scope,
|
|
'state' => $state,
|
|
'prompt' => 'consent',
|
|
], '', '&', PHP_QUERY_RFC3986);
|
|
|
|
logAudit("Settings", "Edit", "$session_name started connecting Microsoft 365 for mail");
|
|
|
|
redirect($authorize_url);
|
|
}
|
|
|
|
/*
|
|
* The addresses and names emails come from. An address that isn't one comes
|
|
* back saying so; a name is kept as typed, bar line breaks and the like.
|
|
*/
|
|
if (isset($_POST['edit_mail_from_settings'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$boxes = ['mail', 'invoice', 'quote', 'ticket'];
|
|
$columns = [];
|
|
foreach ($boxes as $box) {
|
|
$columns[] = "config_{$box}_from_email";
|
|
$columns[] = "config_{$box}_from_name";
|
|
}
|
|
$before = settingsCurrent($columns);
|
|
|
|
$after = [];
|
|
$errors = [];
|
|
foreach ($boxes as $box) {
|
|
$email_key = "config_{$box}_from_email";
|
|
$name_key = "config_{$box}_from_name";
|
|
$email = trim((string) ($_POST[$email_key] ?? $before[$email_key]));
|
|
$name = trim(preg_replace('/\s+/u', ' ', preg_replace('/[\x00-\x1F\x7F]/u', ' ', (string) ($_POST[$name_key] ?? $before[$name_key]))));
|
|
if (($problem = settingsEmailProblem($email)) !== '') {
|
|
$errors[$email_key] = $problem;
|
|
} elseif (mb_strlen($email) > 200) {
|
|
$errors[$email_key] = 'Up to 200 characters.';
|
|
}
|
|
if (mb_strlen($name) > 200) {
|
|
$errors[$name_key] = 'Up to 200 characters.';
|
|
}
|
|
$after[$email_key] = $email;
|
|
$after[$name_key] = $name;
|
|
}
|
|
if ($errors) {
|
|
settingsRefuse('mail', $errors, $mail_tab_redirect);
|
|
}
|
|
|
|
settingsSave('mail', $before, $after);
|
|
|
|
redirect($mail_tab_redirect);
|
|
|
|
}
|
|
|
|
// A copy of one of the previews (Admin > Mail > Previews), to the admin asking
|
|
if (isset($_POST['send_email_preview'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$key = (string) ($_POST['email'] ?? '');
|
|
$catalog = emailPreviewCatalog();
|
|
if (!isset($catalog[$key])) {
|
|
flashAlert('There is no such email', 'error');
|
|
redirect('settings_mail.php?tab=previews');
|
|
}
|
|
if (!emailReady()) {
|
|
flashAlert('Set up how mail is sent first, then send yourself a copy', 'error');
|
|
redirect('settings_mail.php?tab=previews&email=' . rawurlencode($key));
|
|
}
|
|
if (emailPreviewSendCopy($key, $session_email, stripslashes($session_name))) {
|
|
logAudit("Settings", "Edit", "$session_name sent themselves a preview of the " . escapeSql($catalog[$key]['group'] . ' - ' . $catalog[$key]['label']) . " email");
|
|
flashAlert('A copy of <strong>' . escapeHtml($catalog[$key]['label']) . '</strong> is queued to ' . escapeHtml($session_email));
|
|
} else {
|
|
flashAlert('The copy could not be sent - check your own email address in Your account', 'error');
|
|
}
|
|
redirect('settings_mail.php?tab=previews&email=' . rawurlencode($key));
|
|
|
|
}
|
|
|
|
if (isset($_POST['test_email_smtp'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$test_email = intval($_POST['test_email']);
|
|
|
|
// From the mailbox picked - the system's, invoices', quotes' or tickets' (emailTestSend(), functions/email.php)
|
|
$mailbox = [1 => 'system', 2 => 'invoice', 3 => 'quote'][$test_email] ?? 'ticket';
|
|
$to = trim((string) ($_POST['email_to'] ?? ''));
|
|
|
|
if (!emailReady()) {
|
|
flashAlert('Set up how mail is sent first, then send the test', 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$sent = emailTestSend($to, $mailbox);
|
|
|
|
if ($sent) {
|
|
flashAlert('Test email queued - it goes within a minute. Watch it go in <strong>Maintenance > Mail Queue</strong>');
|
|
} else {
|
|
flashAlert(escapeHtml($to) . " isn't an email address - nothing was queued", 'error');
|
|
}
|
|
|
|
redirect($mail_tab_redirect);
|
|
|
|
}
|
|
|
|
if (isset($_POST['test_email_imap'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$provider = escapeSql($config_imap_provider ?? '');
|
|
|
|
$host = $config_imap_host;
|
|
$port = (int) $config_imap_port;
|
|
$encryption = strtolower(trim($config_imap_encryption)); // e.g. "ssl", "tls", "none"
|
|
$username = $config_imap_username;
|
|
$password = $config_imap_password;
|
|
|
|
// Shared OAuth fields
|
|
$config_mail_oauth_client_id = $config_mail_oauth_client_id ?? '';
|
|
$config_mail_oauth_client_secret = $config_mail_oauth_client_secret ?? '';
|
|
$config_mail_oauth_tenant_id = $config_mail_oauth_tenant_id ?? '';
|
|
$config_mail_oauth_refresh_token = $config_mail_oauth_refresh_token ?? '';
|
|
$config_mail_oauth_access_token = $config_mail_oauth_access_token ?? '';
|
|
$config_mail_oauth_access_token_expires_at = $config_mail_oauth_access_token_expires_at ?? '';
|
|
|
|
$is_oauth = ($provider === 'google_oauth' || $provider === 'microsoft_oauth');
|
|
|
|
// Override, don't default - a leftover standard-IMAP host from before the
|
|
// switch to OAuth would otherwise make this test fail against the old server
|
|
// while the cron parser (which overrides unconditionally) connects fine.
|
|
if ($provider === 'google_oauth') {
|
|
$host = 'imap.gmail.com';
|
|
$port = 993;
|
|
$encryption = 'ssl';
|
|
} elseif ($provider === 'microsoft_oauth') {
|
|
$host = 'outlook.office365.com';
|
|
$port = 993;
|
|
$encryption = 'ssl';
|
|
}
|
|
|
|
if (empty($host) || empty($port) || empty($username)) {
|
|
flashAlert("<strong>IMAP connection failed:</strong> Missing host, port, or username.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$token_is_expired = function (?string $expires_at): bool {
|
|
if (empty($expires_at)) {
|
|
return true;
|
|
}
|
|
|
|
$ts = strtotime($expires_at);
|
|
|
|
if ($ts === false) {
|
|
return true;
|
|
}
|
|
|
|
return ($ts - 60) <= time();
|
|
};
|
|
|
|
$http_form_post = function (string $url, array $fields): array {
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields, '', '&'));
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
|
|
|
|
$raw = curl_exec($ch);
|
|
$err = curl_error($ch);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
curl_close($ch);
|
|
|
|
return [
|
|
'ok' => ($raw !== false && $code >= 200 && $code < 300),
|
|
'body' => $raw,
|
|
'code' => $code,
|
|
'err' => $err,
|
|
];
|
|
};
|
|
|
|
if ($is_oauth) {
|
|
if (!empty($config_mail_oauth_access_token) && !$token_is_expired($config_mail_oauth_access_token_expires_at)) {
|
|
$password = $config_mail_oauth_access_token;
|
|
} else {
|
|
if (empty($config_mail_oauth_client_id) || empty($config_mail_oauth_client_secret) || empty($config_mail_oauth_refresh_token)) {
|
|
flashAlert("<strong>IMAP OAuth failed:</strong> Missing OAuth client credentials or refresh token.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
if ($provider === 'google_oauth') {
|
|
$response = $http_form_post('https://oauth2.googleapis.com/token', [
|
|
'client_id' => $config_mail_oauth_client_id,
|
|
'client_secret' => $config_mail_oauth_client_secret,
|
|
'refresh_token' => $config_mail_oauth_refresh_token,
|
|
'grant_type' => 'refresh_token',
|
|
]);
|
|
} else {
|
|
if (empty($config_mail_oauth_tenant_id)) {
|
|
flashAlert("<strong>IMAP OAuth failed:</strong> Microsoft tenant ID is required.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$token_url = MICROSOFT_OAUTH_BASE_URL . rawurlencode($config_mail_oauth_tenant_id) . "/oauth2/v2.0/token";
|
|
$response = $http_form_post($token_url, [
|
|
'client_id' => $config_mail_oauth_client_id,
|
|
'client_secret' => $config_mail_oauth_client_secret,
|
|
'refresh_token' => $config_mail_oauth_refresh_token,
|
|
'grant_type' => 'refresh_token',
|
|
]);
|
|
}
|
|
|
|
if (!$response['ok']) {
|
|
flashAlert("<strong>IMAP OAuth failed:</strong> Could not refresh access token.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$json = json_decode($response['body'], true);
|
|
if (!is_array($json) || empty($json['access_token'])) {
|
|
flashAlert("<strong>IMAP OAuth failed:</strong> Token response did not include an access token.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$password = $json['access_token'];
|
|
$expires_at = date('Y-m-d H:i:s', time() + (int)($json['expires_in'] ?? 3600));
|
|
$refresh_token_to_save = $json['refresh_token'] ?? null;
|
|
|
|
$token_esc = mysqli_real_escape_string($mysqli, $password);
|
|
$expires_at_esc = mysqli_real_escape_string($mysqli, $expires_at);
|
|
|
|
$refresh_sql = '';
|
|
if (!empty($refresh_token_to_save)) {
|
|
$refresh_token_esc = mysqli_real_escape_string($mysqli, $refresh_token_to_save);
|
|
$refresh_sql = ", config_mail_oauth_refresh_token = '{$refresh_token_esc}'";
|
|
}
|
|
|
|
mysqli_query($mysqli, "UPDATE settings SET config_mail_oauth_access_token = '{$token_esc}', config_mail_oauth_access_token_expires_at = '{$expires_at_esc}'{$refresh_sql} WHERE company_id = 1");
|
|
}
|
|
}
|
|
|
|
// Build remote socket (implicit SSL vs plain TCP)
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/libs/vendor/autoload.php'; // ImapEngine (composer)
|
|
|
|
// Map the stored encryption value to an ImapEngine transport (matches the cron sync)
|
|
$imap_transport = match ($encryption) {
|
|
'ssl' => 'ssl', // implicit TLS (993)
|
|
'tls' => 'starttls', // STARTTLS upgrade (143) - Webklex semantics
|
|
'starttls' => 'starttls',
|
|
default => '', // 'none' / plain TCP
|
|
};
|
|
|
|
try {
|
|
// Same ImapEngine client the cron sync uses, so a passing test predicts a
|
|
// working sync. Typed errors instead of raw banners; host validated at save.
|
|
$mailbox = new \DirectoryTree\ImapEngine\Mailbox([
|
|
'host' => $host,
|
|
'port' => $port,
|
|
'encryption' => $imap_transport,
|
|
'validate_cert' => true,
|
|
'username' => $username,
|
|
'password' => $password, // access token when OAuth
|
|
'authentication' => $is_oauth ? 'oauth' : 'plain',
|
|
]);
|
|
|
|
$mailbox->connect();
|
|
$mailbox->inbox(); // confirm auth + mailbox access, like the sync does
|
|
|
|
flashAlert($is_oauth ? "Connected successfully using OAuth" : "Connected successfully");
|
|
} catch (\Throwable $e) {
|
|
flashAlert("<strong>IMAP connection failed.</strong> Check the host, port, encryption, and credentials.", 'error');
|
|
}
|
|
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
|
|
if (isset($_POST['test_oauth_token_refresh'])) {
|
|
|
|
validateCSRFToken();
|
|
|
|
$provider = escapeSql($_POST['oauth_provider'] ?? '');
|
|
|
|
if ($provider !== 'google_oauth' && $provider !== 'microsoft_oauth') {
|
|
flashAlert("OAuth token test failed: unsupported provider.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$oauth_client_id = escapeSql($config_mail_oauth_client_id ?? '');
|
|
$oauth_client_secret = escapeSql($config_mail_oauth_client_secret ?? '');
|
|
$oauth_tenant_id = escapeSql($config_mail_oauth_tenant_id ?? '');
|
|
$oauth_refresh_token = escapeSql($config_mail_oauth_refresh_token ?? '');
|
|
|
|
if (empty($oauth_client_id) || empty($oauth_client_secret) || empty($oauth_refresh_token)) {
|
|
flashAlert("OAuth token test failed: missing client ID, client secret, or refresh token.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
if ($provider === 'microsoft_oauth' && empty($oauth_tenant_id)) {
|
|
flashAlert("OAuth token test failed: Microsoft tenant ID is required.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$token_url = 'https://oauth2.googleapis.com/token';
|
|
if ($provider === 'microsoft_oauth') {
|
|
$token_url = MICROSOFT_OAUTH_BASE_URL . rawurlencode($oauth_tenant_id) . "/oauth2/v2.0/token";
|
|
}
|
|
|
|
$post_fields = http_build_query([
|
|
'client_id' => $oauth_client_id,
|
|
'client_secret' => $oauth_client_secret,
|
|
'refresh_token' => $oauth_refresh_token,
|
|
'grant_type' => 'refresh_token',
|
|
]);
|
|
|
|
$ch = curl_init($token_url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
|
|
|
|
$raw_body = curl_exec($ch);
|
|
$curl_err = curl_error($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($raw_body === false || $http_code < 200 || $http_code >= 300) {
|
|
$err_msg = !empty($curl_err) ? $curl_err : "HTTP $http_code";
|
|
flashAlert("OAuth token test failed: $err_msg", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$json = json_decode($raw_body, true);
|
|
|
|
if (!is_array($json) || empty($json['access_token'])) {
|
|
flashAlert("OAuth token test failed: access token missing in provider response.", 'error');
|
|
redirect($mail_tab_redirect);
|
|
}
|
|
|
|
$new_access_token = escapeSql($json['access_token']);
|
|
$new_expires_at = date('Y-m-d H:i:s', time() + (int)($json['expires_in'] ?? 3600));
|
|
$new_refresh_token = !empty($json['refresh_token']) ? escapeSql($json['refresh_token']) : '';
|
|
|
|
$new_access_token_esc = mysqli_real_escape_string($mysqli, $new_access_token);
|
|
$new_expires_at_esc = mysqli_real_escape_string($mysqli, $new_expires_at);
|
|
|
|
$refresh_sql = '';
|
|
if (!empty($new_refresh_token)) {
|
|
$new_refresh_token_esc = mysqli_real_escape_string($mysqli, $new_refresh_token);
|
|
$refresh_sql = ", config_mail_oauth_refresh_token = '$new_refresh_token_esc'";
|
|
}
|
|
|
|
mysqli_query($mysqli, "UPDATE settings SET config_mail_oauth_access_token = '$new_access_token_esc', config_mail_oauth_access_token_expires_at = '$new_expires_at_esc'$refresh_sql WHERE company_id = 1");
|
|
|
|
$provider_label = $provider === 'microsoft_oauth' ? 'Microsoft 365' : 'Google Workspace';
|
|
logAudit("Settings", "Edit", "$session_name tested OAuth token refresh for $provider_label mail settings");
|
|
|
|
flashAlert("OAuth token refresh successful for $provider_label. Access token expires at $new_expires_at.");
|
|
redirect($mail_tab_redirect);
|
|
}
|