Added Beta support for Microsoft IMAP OAUTH2 must use new mail parser for it to work cron/ticket_email_parser.php

This commit is contained in:
johnnyq
2025-09-12 15:56:44 -04:00
parent ce7d84aa2f
commit 3a5b18f3dd
7 changed files with 485 additions and 59 deletions

View File

@@ -3968,11 +3968,26 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.2'");
}
if (CURRENT_DATABASE_VERSION == '2.3.2') {
mysqli_query($mysqli, "ALTER TABLE settings
ADD `config_imap_provider` ENUM('standard_imap','google_oauth','microsoft_oauth') NULL DEFAULT NULL AFTER `config_mail_from_name`,
ADD `config_mail_oauth_client_id` VARCHAR(255) NULL AFTER `config_imap_provider`,
ADD `config_mail_oauth_client_secret` VARCHAR(255) NULL AFTER `config_mail_oauth_client_id`,
ADD `config_mail_oauth_tenant_id` VARCHAR(255) NULL AFTER `config_mail_oauth_client_secret`,
ADD `config_mail_oauth_refresh_token` TEXT NULL AFTER `config_mail_oauth_tenant_id`,
ADD `config_mail_oauth_access_token` TEXT NULL AFTER `config_mail_oauth_refresh_token`,
ADD `config_mail_oauth_access_token_expires_at` DATETIME NULL AFTER `config_mail_oauth_access_token`
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.3'");
}
// if (CURRENT_DATABASE_VERSION == '2.3.2') {
// if (CURRENT_DATABASE_VERSION == '2.3.3') {
// // Insert queries here required to update to DB version 2.3.3
// // Then, update the database to the next sequential version
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.3'");
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.4'");
// }
} else {

View File

@@ -26,20 +26,91 @@ if (isset($_POST['edit_mail_imap_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_imap_host = sanitizeInput($_POST['config_imap_host']);
$config_imap_username = sanitizeInput($_POST['config_imap_username']);
$config_imap_password = sanitizeInput($_POST['config_imap_password']);
$config_imap_port = intval($_POST['config_imap_port']);
$config_imap_encryption = sanitizeInput($_POST['config_imap_encryption']);
// Provider ('' -> NULL allowed)
$config_imap_provider = sanitizeInput($_POST['config_imap_provider']);
$allowed_providers = ['standard_imap','google_oauth','microsoft_oauth'];
if ($config_imap_provider !== '' && !in_array($config_imap_provider, $allowed_providers, true)) {
$config_imap_provider = 'standard_imap'; // fallback
}
mysqli_query($mysqli,"UPDATE settings SET config_imap_host = '$config_imap_host', config_imap_port = $config_imap_port, config_imap_encryption = '$config_imap_encryption', config_imap_username = '$config_imap_username', config_imap_password = '$config_imap_password' WHERE company_id = 1");
// Standard IMAP fields (kept for all providers; OAuth still needs these endpoints)
$config_imap_host = sanitizeInput($_POST['config_imap_host']);
$config_imap_port = (int) sanitizeInput($_POST['config_imap_port']);
$config_imap_encryption = sanitizeInput($_POST['config_imap_encryption']); // '', 'tls', 'ssl'
$config_imap_username = sanitizeInput($_POST['config_imap_username']);
$config_imap_password = sanitizeInput($_POST['config_imap_password']); // ignored if OAuth selected
logAction("Settings", "Edit", "$session_name edited IMAP mail settings");
// Shared OAuth fields (may or may not be present in your form yet)
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']); // M365 only; harmless to keep when Google
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']); // optional manual paste
$config_mail_oauth_access_token_expires_at = sanitizeInput($_POST['config_mail_oauth_access_token_expires_at']); // 'YYYY-mm-dd HH:ii:ss' optional
flash_alert("IMAP Mail Settings updated");
// If provider is not OAuth, purge OAuth values on save
$is_oauth = ($config_imap_provider === 'google_oauth' || $config_imap_provider === 'microsoft_oauth');
// Detect refresh token change to invalidate access token cache
// (Relies on $config_mail_oauth_refresh_token loaded earlier with settings)
$refresh_changed = false;
if ($is_oauth) {
$prev_refresh = isset($config_mail_oauth_refresh_token_current) ? $config_mail_oauth_refresh_token_current : ($config_mail_oauth_refresh_token ?? '');
// If you already load settings into $config_mail_oauth_refresh_token, use that:
if (isset($config_mail_oauth_refresh_token)) {
$prev_refresh = $config_mail_oauth_refresh_token;
}
$refresh_changed = ($config_mail_oauth_refresh_token !== '' && $config_mail_oauth_refresh_token !== $prev_refresh)
|| ($config_mail_oauth_refresh_token === '' && $prev_refresh !== '');
}
// If OAuth refresh changed or provider just switched to non-OAuth, clear access token values
if (!$is_oauth || $refresh_changed) {
$config_mail_oauth_access_token = '';
$config_mail_oauth_access_token_expires_at = '';
}
// Helper for NULL / quoted values
$q = fn($v) => ($v !== '' ? "'" . mysqli_real_escape_string($mysqli, $v) . "'" : "NULL");
// Build UPDATE with correct NULL handling
$sql = "
UPDATE settings SET
config_imap_provider = " . ($config_imap_provider !== '' ? $q($config_imap_provider) : "NULL") . ",
config_imap_host = " . $q($config_imap_host) . ",
config_imap_port = " . (int)$config_imap_port . ",
config_imap_encryption = " . $q($config_imap_encryption) . ",
config_imap_username = " . $q($config_imap_username) . ",
config_imap_password = " . ($is_oauth ? "NULL" : $q($config_imap_password)) . ",
-- Shared OAuth fields (kept even if provider is Google or Microsoft; NULL if not used)
config_mail_oauth_client_id = " . ($is_oauth ? $q($config_mail_oauth_client_id) : "NULL") . ",
config_mail_oauth_client_secret = " . ($is_oauth ? $q($config_mail_oauth_client_secret) : "NULL") . ",
config_mail_oauth_tenant_id = " . ($is_oauth ? $q($config_mail_oauth_tenant_id) : "NULL") . ",
config_mail_oauth_refresh_token = " . ($is_oauth ? $q($config_mail_oauth_refresh_token) : "NULL") . ",
config_mail_oauth_access_token = " . ($is_oauth ? $q($config_mail_oauth_access_token) : "NULL") . ",
config_mail_oauth_access_token_expires_at = " . ($is_oauth ? $q($config_mail_oauth_access_token_expires_at) : "NULL") . "
WHERE company_id = 1
";
mysqli_query($mysqli, $sql);
logAction("Settings", "Edit", "$session_name edited IMAP/OAuth mail settings");
// Friendly hint about what was saved
if ($config_imap_provider === '') {
flash_alert("IMAP monitoring disabled (provider not configured).");
} elseif ($config_imap_provider === 'standard_imap') {
flash_alert("IMAP settings updated (standard username/password).");
} elseif ($config_imap_provider === 'google_oauth') {
flash_alert("IMAP settings updated for Google Workspace (OAuth).");
} elseif ($config_imap_provider === 'microsoft_oauth') {
flash_alert("IMAP settings updated for Microsoft 365 (OAuth).");
} else {
flash_alert("IMAP settings updated.");
}
redirect();
}
if (isset($_POST['edit_mail_from_settings'])) {

View File

@@ -84,37 +84,56 @@ require_once "includes/inc_all_admin.php";
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="form-group">
<label>IMAP Host</label>
<label>IMAP Provider</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-server"></i></span>
<span class="input-group-text"><i class="fa fa-fw fa-cloud"></i></span>
</div>
<input type="text" class="form-control" name="config_imap_host" placeholder="Incoming Mail Server Address (for email to ticket parsing)" value="<?php echo nullable_htmlentities($config_imap_host); ?>">
</div>
</div>
<div class="form-group">
<label>IMAP Port</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-plug"></i></span>
</div>
<input type="number" min="0" class="form-control" name="config_imap_port" placeholder="Incoming Mail Server Port Number (993)" value="<?php echo intval($config_imap_port); ?>">
</div>
</div>
<div class="form-group">
<label>IMAP Encryption</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
</div>
<select class="form-control" name="config_imap_encryption">
<option value=''>None</option>
<option <?php if ($config_imap_encryption == 'tls') { echo "selected"; } ?> value="tls">TLS</option>
<option <?php if ($config_imap_encryption == 'ssl') { echo "selected"; } ?> value="ssl">SSL</option>
<select class="form-control" name="config_imap_provider" id="config_imap_provider">
<option value="none" <?php if($config_imap_provider ==='') echo 'selected'; ?>>None (Disabled)</option>
<option value="standard_imap" <?php if(($config_imap_provider ?? 'standard_imap')==='standard_imap') echo 'selected'; ?>>Standard IMAP (Username/Password)</option>
<option value="google_oauth" <?php if(($config_imap_provider ?? '')==='google_oauth') echo 'selected'; ?>>Google Workspace (OAuth)</option>
<option value="microsoft_oauth" <?php if(($config_imap_provider ?? '')==='microsoft_oauth') echo 'selected'; ?>>Microsoft 365 (OAuth)</option>
</select>
</div>
<small class="text-secondary d-block mt-1" id="imap_provider_hint">
Select your mailbox provider. OAuth options ignore the IMAP password here.
</small>
</div>
<div id="standard_fields" style="display:none;">
<div class="form-group">
<label>IMAP Host</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-server"></i></span>
</div>
<input type="text" class="form-control" name="config_imap_host" placeholder="Incoming Mail Server Address (for email to ticket parsing)" value="<?php echo nullable_htmlentities($config_imap_host); ?>">
</div>
</div>
<div class="form-group">
<label>IMAP Port</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-plug"></i></span>
</div>
<input type="number" min="0" class="form-control" name="config_imap_port" placeholder="Incoming Mail Server Port Number (993)" value="<?php echo intval($config_imap_port); ?>">
</div>
</div>
<div class="form-group">
<label>IMAP Encryption</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
</div>
<select class="form-control" name="config_imap_encryption">
<option value=''>None</option>
<option <?php if ($config_imap_encryption == 'tls') { echo "selected"; } ?> value="tls">TLS</option>
<option <?php if ($config_imap_encryption == 'ssl') { echo "selected"; } ?> value="ssl">SSL</option>
</select>
</div>
</div>
</div>
<div class='form-group'>
@@ -123,25 +142,80 @@ require_once "includes/inc_all_admin.php";
<div class='input-group-prepend'>
<span class='input-group-text'><i class='fa fa-fw fa-user'></i></span>
</div>
<input type='text' class='form-control' name='config_imap_username' placeholder='Username' value="<?php
echo nullable_htmlentities($config_imap_username); ?>" required>
<input type='text' class='form-control' name='config_imap_username' placeholder='Username (email address)' value="<?php echo nullable_htmlentities($config_imap_username); ?>" required>
</div>
</div>
<div class='form-group'>
<div class='form-group' id="imap_password_group">
<label>IMAP Password</label>
<div class='input-group'>
<div class='input-group-prepend'>
<span class='input-group-text'><i class='fa fa-fw fa-key'></i></span>
</div>
<input type='password' class='form-control' data-toggle='password' name='config_imap_password' placeholder='Password' value="<?php
echo nullable_htmlentities($config_imap_password); ?>" autocomplete='new-password' required>
<input type='password' class='form-control' data-toggle='password' name='config_imap_password' placeholder='Password (not used for OAuth)' value="<?php echo nullable_htmlentities($config_imap_password); ?>" autocomplete='new-password'>
<div class='input-group-append'>
<span class='input-group-text'><i class='fa fa-fw fa-eye'></i></span>
</div>
</div>
</div>
<div id="oauth_fields" style="display:none;">
<hr>
<h5 class="mb-2">OAuth Settings</h5>
<p class="text-secondary" id="oauth_hint">
Configure OAuth credentials for the selected provider.
</p>
<div class="form-group">
<label>OAuth Client ID</label>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span></div>
<input type="text" class="form-control" name="config_mail_oauth_client_id"
value="<?php echo nullable_htmlentities($config_mail_oauth_client_id ?? ''); ?>">
</div>
</div>
<div class="form-group">
<label>OAuth Client Secret</label>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span></div>
<input type="password" class="form-control" data-toggle="password" name="config_mail_oauth_client_secret"
value="<?php echo nullable_htmlentities($config_mail_oauth_client_secret ?? ''); ?>" autocomplete="new-password">
<div class="input-group-append"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span></div>
</div>
</div>
<div class="form-group" id="tenant_row" style="display:none;">
<label>Tenant ID (Microsoft 365 only)</label>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-building"></i></span></div>
<input type="text" class="form-control" name="config_mail_oauth_tenant_id"
value="<?php echo nullable_htmlentities($config_mail_oauth_tenant_id ?? ''); ?>">
</div>
</div>
<div class="form-group">
<label>Refresh Token</label>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-sync-alt"></i></span></div>
<textarea class="form-control" name="config_mail_oauth_refresh_token" rows="2"
placeholder="Paste refresh token"><?php echo nullable_htmlentities($config_mail_oauth_refresh_token ?? ''); ?></textarea>
</div>
</div>
<div class="form-group">
<label>Access Token (optional will refresh if expired)</label>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-shield-alt"></i></span></div>
<textarea class="form-control" name="config_mail_oauth_access_token" rows="2"
placeholder="Can be left blank; system refreshes using the refresh token"><?php echo nullable_htmlentities($config_mail_oauth_access_token ?? ''); ?></textarea>
</div>
<small class="text-secondary">
Expires at: <?php echo !empty($config_mail_oauth_access_token_expires_at) ? htmlspecialchars($config_mail_oauth_access_token_expires_at) : 'n/a'; ?>
</small>
</div>
</div>
<hr>
<button type="submit" name="edit_mail_imap_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
@@ -327,5 +401,67 @@ require_once "includes/inc_all_admin.php";
<?php } ?>
<?php require_once "../includes/footer.php";
<script>
(function(){
const sel = document.getElementById('config_imap_provider');
const pwdGrp = document.getElementById('imap_password_group');
const oauthWrap = document.getElementById('oauth_fields');
const standardWrap = document.getElementById('standard_fields');
const tenantRow = document.getElementById('tenant_row');
const oauthHint = document.getElementById('oauth_hint');
const providerHint = document.getElementById('imap_provider_hint');
function setDisabled(container, disabled){
if(!container) return;
container.querySelectorAll('input, select, textarea').forEach(el => {
el.disabled = !!disabled;
});
}
function toggleFields(){
if(!sel) return;
const v = sel.value || '';
const isNone = v === '';
const isStd = v === 'standard_imap';
const isG = v === 'google_oauth';
const isM = v === 'microsoft_oauth';
const isOAuth = isG || isM;
// Show/hide containers
if (pwdGrp) pwdGrp.style.display = isStd ? '' : 'none';
if (oauthWrap) oauthWrap.style.display = isOAuth ? '' : 'none';
if (standardWrap) standardWrap.style.display = isStd ? '' : 'none';
if (tenantRow) tenantRow.style.display = isM ? '' : 'none';
// Disable inputs inside hidden sections to avoid accidental submission
setDisabled(pwdGrp, !isStd);
setDisabled(standardWrap, !isStd);
setDisabled(oauthWrap, !isOAuth);
// Update hints
if (providerHint) {
providerHint.textContent = isNone
? 'Choose a provider to reveal the relevant settings.'
: isStd
? 'Standard IMAP: provide host, port, encryption, username, and password.'
: isG
? 'Google Workspace OAuth: provide Client ID & Secret; paste the refresh token; username should be the mailbox address.'
: 'Microsoft 365 OAuth: provide Client ID, Secret & Tenant ID; paste the refresh token; username should be the mailbox address.';
}
if (oauthHint) {
oauthHint.textContent = isG
? 'Google Workspace OAuth: Client ID & Secret from Google Cloud; Refresh token generated via OAuth consent.'
: isM
? 'Microsoft 365 OAuth: Client ID, Secret & Tenant ID from Entra ID; Refresh token generated via OAuth consent.'
: 'Configure OAuth credentials for the selected provider.';
}
}
if (sel) {
sel.addEventListener('change', toggleFields);
toggleFields();
}
})();
</script>
<?php require_once "../includes/footer.php";