1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2026-07-30 08:33:51 +00:00

[Imapsync] Use dropdown for custom params

This commit is contained in:
FreddleSpl0it
2026-07-17 20:45:46 +02:00
parent 496c3a51ac
commit 069b89629e
12 changed files with 237 additions and 125 deletions
+33 -60
View File
@@ -360,6 +360,21 @@ function imapsync_get_settings() {
return $settings;
}
function imapsync_normalize_custom_params($input) {
$pairs = json_decode((string)$input, true);
if (!is_array($pairs)) return '[]';
$allow = $GLOBALS['IMAPSYNC_OPTIONS']['whitelist'];
$out = array();
foreach ($pairs as $p) {
if (!is_array($p)) continue;
$o = ltrim(strtolower(trim((string)($p['o'] ?? ''))), '-');
if ($o === '') continue;
if (!in_array($o, $allow, true)) return false; // disallowed option -> reject the whole set
$out[] = array('o' => $o, 'v' => str_replace("\0", '', (string)($p['v'] ?? '')));
}
return json_encode($out);
}
function imapsync_set_setting($name, $value) {
// Admin-only writer for a global syncjob setting.
global $pdo;
@@ -508,36 +523,15 @@ function syncjob($_action, $_type, $_data = null, $_extra = null) {
return false;
}
// validate custom params
foreach (explode('-', $custom_params) as $param){
if(empty($param)) continue;
// extract option
if (str_contains($param, '=')) $param = explode('=', $param)[0];
else $param = rtrim($param, ' ');
// remove first char if first char is -
if ($param[0] == '-') $param = ltrim($param, $param[0]);
if (str_contains($param, ' ')) {
// bad char
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'bad character SPACE'
);
return false;
}
// check if param is whitelisted
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
// bad option
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'bad option '. $param
);
return false;
}
// Structured custom params: validate option names against the allowlist, store as JSON pairs
$custom_params = imapsync_normalize_custom_params($custom_params);
if ($custom_params === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'imapsync_custom_param_invalid'
);
return false;
}
if (empty($subfolder2)) {
$subfolder2 = "";
@@ -879,36 +873,15 @@ function syncjob($_action, $_type, $_data = null, $_extra = null) {
$password1 = '';
}
// validate custom params
foreach (explode('-', $custom_params) as $param){
if(empty($param)) continue;
// extract option
if (str_contains($param, '=')) $param = explode('=', $param)[0];
else $param = rtrim($param, ' ');
// remove first char if first char is -
if ($param[0] == '-') $param = ltrim($param, $param[0]);
if (str_contains($param, ' ')) {
// bad char
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'bad character SPACE'
);
return false;
}
// check if param is whitelisted
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
// bad option
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'bad option '. $param
);
return false;
}
// Structured custom params: validate option names against the allowlist, store as JSON pairs
$custom_params = imapsync_normalize_custom_params($custom_params);
if ($custom_params === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'imapsync_custom_param_invalid'
);
continue;
}
if (empty($subfolder2)) {
$subfolder2 = "";
+24 -2
View File
@@ -4,7 +4,7 @@ function init_db_schema()
try {
global $pdo;
$db_version = "16072026_1000";
$db_version = "17072026_1000";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -886,7 +886,7 @@ function init_db_schema()
"delete2" => "TINYINT(1) NOT NULL DEFAULT '0'",
"automap" => "TINYINT(1) NOT NULL DEFAULT '0'",
"skipcrossduplicates" => "TINYINT(1) NOT NULL DEFAULT '0'",
"custom_params" => "VARCHAR(512) NOT NULL DEFAULT ''",
"custom_params" => "TEXT",
"timeout1" => "SMALLINT NOT NULL DEFAULT '600'",
"timeout2" => "SMALLINT NOT NULL DEFAULT '600'",
"subscribeall" => "TINYINT(1) NOT NULL DEFAULT '1'",
@@ -1533,6 +1533,28 @@ function init_db_schema()
$pdo->query("UPDATE `imapsync` SET `order_id` = (@r := @r + 1) ORDER BY `id`");
}
// Syncjobs: migrate custom_params from raw imapsync string to structured JSON pairs.
// Old values could not contain spaces (they were rejected), so a whitespace split is safe.
$allow = $GLOBALS['IMAPSYNC_OPTIONS']['whitelist'];
$cp_upd = $pdo->prepare("UPDATE `imapsync` SET `custom_params` = :cp WHERE `id` = :id");
foreach ($pdo->query("SELECT `id`, `custom_params` FROM `imapsync`")->fetchAll(PDO::FETCH_ASSOC) as $r) {
$cp = trim((string)$r['custom_params']);
if ($cp !== '' && $cp[0] === '[') continue; // already migrated (JSON)
$pairs = array();
if ($cp !== '') {
$tokens = preg_split('/\s+/', $cp, -1, PREG_SPLIT_NO_EMPTY);
for ($i = 0; $i < count($tokens); $i++) {
if (strpos($tokens[$i], '--') !== 0) continue;
$opt = ltrim($tokens[$i], '-'); $val = '';
if (strpos($opt, '=') !== false) { list($opt, $val) = explode('=', $opt, 2); }
elseif ($i + 1 < count($tokens) && strpos($tokens[$i + 1], '--') !== 0) { $val = $tokens[++$i]; }
if (!in_array(strtolower($opt), $allow)) continue; // drop options no longer allowed
$pairs[] = array('o' => strtolower($opt), 'v' => $val);
}
}
$cp_upd->execute(array(':cp' => json_encode($pairs), ':id' => $r['id']));
}
// Inject admin if not exists
$stmt = $pdo->query("SELECT NULL FROM `admin`");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-32
View File
@@ -285,10 +285,7 @@ $IMAPSYNC_OPTIONS = array(
'whitelist' => array(
'abort',
'authmd51',
'authmd52',
'authmech2',
'authuser1',
'authuser2',
'debug',
'debugcontent',
'debugcrossduplicates',
@@ -296,20 +293,15 @@ $IMAPSYNC_OPTIONS = array(
'debugfolders',
'debugimap',
'debugimap1',
'debugimap2',
'debugmemory',
'debugssl',
'delete1emptyfolders',
'delete2folders',
'disarmreadreceipts',
'domain1',
'domain2',
'domino1',
'domino2',
'dry',
'errorsmax',
'exchange1',
'exchange2',
'exitwhenover',
'expunge1',
'f1f2',
@@ -319,7 +311,6 @@ $IMAPSYNC_OPTIONS = array(
'folderlast',
'folderrec',
'gmail1',
'gmail2',
'idatefromheader',
'include',
'inet4',
@@ -329,10 +320,7 @@ $IMAPSYNC_OPTIONS = array(
'justfoldersizes',
'justlogin',
'keepalive1',
'keepalive2',
'log',
'logdir',
'logfile',
'maxbytesafter',
'maxlinelength',
'maxmessagespersecond',
@@ -342,43 +330,24 @@ $IMAPSYNC_OPTIONS = array(
'minsize',
'noabletosearch',
'noabletosearch1',
'noabletosearch2',
'noexpunge1',
'noexpunge2',
'nofoldersizesatend',
'noid',
'nolog',
'nomixfolders',
'noresyncflags',
'nossl1',
'nossl2',
'nosyncacls',
'notls1',
'notls2',
'nouidexpunge2',
'nousecache',
'oauthaccesstoken2',
'oauthdirect2',
'office1',
'office2',
'pidfile',
'pidfilelocking',
'prefix1',
'prefix2',
'proxyauth1',
'proxyauth2',
'resyncflags',
'resynclabels',
'search',
'search1',
'search2',
'sep1',
'sep2',
'showpasswords',
'skipemptyfolders',
'ssl2',
'sslargs1',
'sslargs2',
'subfolder1',
'subscribe',
'subscribed',
@@ -389,7 +358,6 @@ $IMAPSYNC_OPTIONS = array(
'tests',
'testslive',
'testslive6',
'tls2',
'truncmess',
'usecache',
'useheader',