mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-09-21 18:27:12 +00:00
[Imapsync] fix custom params
This commit is contained in:
+27
-15
@@ -1520,7 +1520,9 @@ paths:
|
|||||||
timeout1: "600"
|
timeout1: "600"
|
||||||
timeout2: "600"
|
timeout2: "600"
|
||||||
exclude: "(?i)spam|(?i)junk"
|
exclude: "(?i)spam|(?i)junk"
|
||||||
custom_params: '[{"o":"dry","v":""}]'
|
custom_params:
|
||||||
|
- {o: dry, v: ""}
|
||||||
|
- {o: folder, v: INBOX}
|
||||||
delete2duplicates: "1"
|
delete2duplicates: "1"
|
||||||
delete1: "1"
|
delete1: "1"
|
||||||
delete2: "0"
|
delete2: "0"
|
||||||
@@ -1570,13 +1572,18 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
custom_params:
|
custom_params:
|
||||||
description: >-
|
description: >-
|
||||||
JSON array of allowlisted imapsync options as
|
Allowlisted imapsync options as option/value pairs. `o` is the
|
||||||
option/value pairs, e.g.
|
option name (must be one from `GET /get/syncjob_options`); `v`
|
||||||
`[{"o":"folder","v":"INBOX"},{"o":"dry","v":""}]`. Only
|
is the value (empty string for pure flags). Values may contain
|
||||||
option names from `GET /get/syncjob_options` are accepted;
|
any character and are passed to imapsync as a single argument.
|
||||||
values may contain any character and are passed as a single
|
type: array
|
||||||
argument.
|
items:
|
||||||
type: string
|
type: object
|
||||||
|
properties:
|
||||||
|
o:
|
||||||
|
type: string
|
||||||
|
v:
|
||||||
|
type: string
|
||||||
delete2duplicates:
|
delete2duplicates:
|
||||||
description: delete duplicates on destination (--delete2duplicates)
|
description: delete duplicates on destination (--delete2duplicates)
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -4070,13 +4077,18 @@ paths:
|
|||||||
type: boolean
|
type: boolean
|
||||||
custom_params:
|
custom_params:
|
||||||
description: >-
|
description: >-
|
||||||
JSON array of allowlisted imapsync options as
|
Allowlisted imapsync options as option/value pairs. `o` is
|
||||||
option/value pairs, e.g.
|
the option name (must be one from `GET /get/syncjob_options`);
|
||||||
`[{"o":"folder","v":"INBOX"},{"o":"dry","v":""}]`. Only
|
`v` is the value (empty string for flags). Values are passed to imapsync as a single
|
||||||
option names from `GET /get/syncjob_options` are
|
argument.
|
||||||
accepted; values may contain any character and are
|
type: array
|
||||||
passed as a single argument.
|
items:
|
||||||
type: string
|
type: object
|
||||||
|
properties:
|
||||||
|
o:
|
||||||
|
type: string
|
||||||
|
v:
|
||||||
|
type: string
|
||||||
delete1:
|
delete1:
|
||||||
description: Delete from source when completed
|
description: Delete from source when completed
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|||||||
@@ -361,15 +361,15 @@ function imapsync_get_settings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function imapsync_normalize_custom_params($input) {
|
function imapsync_normalize_custom_params($input) {
|
||||||
$pairs = json_decode((string)$input, true);
|
if (!is_array($input)) return '[]';
|
||||||
if (!is_array($pairs)) return '[]';
|
$pairs = $input;
|
||||||
$allow = $GLOBALS['IMAPSYNC_OPTIONS']['whitelist'];
|
$allow = $GLOBALS['IMAPSYNC_OPTIONS'];
|
||||||
$out = array();
|
$out = array();
|
||||||
foreach ($pairs as $p) {
|
foreach ($pairs as $p) {
|
||||||
if (!is_array($p)) continue;
|
if (!is_array($p)) continue;
|
||||||
$o = ltrim(strtolower(trim((string)($p['o'] ?? ''))), '-');
|
$o = ltrim(strtolower(trim((string)($p['o'] ?? ''))), '-');
|
||||||
if ($o === '') continue;
|
if ($o === '') continue;
|
||||||
if (!in_array($o, $allow, true)) return false; // disallowed option -> reject the whole set
|
if (!array_key_exists($o, $allow)) return false; // disallowed option -> reject the whole set
|
||||||
$out[] = array('o' => $o, 'v' => str_replace("\0", '', (string)($p['v'] ?? '')));
|
$out[] = array('o' => $o, 'v' => str_replace("\0", '', (string)($p['v'] ?? '')));
|
||||||
}
|
}
|
||||||
return json_encode($out);
|
return json_encode($out);
|
||||||
@@ -484,7 +484,7 @@ function syncjob($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$subfolder2 = $_data['subfolder2'];
|
$subfolder2 = $_data['subfolder2'];
|
||||||
$user1 = $_data['user1'];
|
$user1 = $_data['user1'];
|
||||||
$mins_interval = $_data['mins_interval'];
|
$mins_interval = $_data['mins_interval'];
|
||||||
$custom_params = (empty(trim($_data['custom_params']))) ? '' : trim($_data['custom_params']);
|
$custom_params = $_data['custom_params'] ?? '';
|
||||||
|
|
||||||
// Resolve and authorize the chosen sync source. Visibility is enforced by syncjob('get', 'source').
|
// Resolve and authorize the chosen sync source. Visibility is enforced by syncjob('get', 'source').
|
||||||
$source = syncjob('get', 'source', array('id' => $source_id));
|
$source = syncjob('get', 'source', array('id' => $source_id));
|
||||||
@@ -843,7 +843,8 @@ function syncjob($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$subfolder2 = (isset($_data['subfolder2'])) ? $_data['subfolder2'] : $is_now['subfolder2'];
|
$subfolder2 = (isset($_data['subfolder2'])) ? $_data['subfolder2'] : $is_now['subfolder2'];
|
||||||
$mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval'];
|
$mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval'];
|
||||||
$exclude = (isset($_data['exclude'])) ? $_data['exclude'] : $is_now['exclude'];
|
$exclude = (isset($_data['exclude'])) ? $_data['exclude'] : $is_now['exclude'];
|
||||||
$custom_params = (isset($_data['custom_params'])) ? $_data['custom_params'] : $is_now['custom_params'];
|
$custom_params_provided = isset($_data['custom_params']);
|
||||||
|
$custom_params = $custom_params_provided ? $_data['custom_params'] : $is_now['custom_params'];
|
||||||
$maxage = (isset($_data['maxage']) && $_data['maxage'] != "") ? intval($_data['maxage']) : $is_now['maxage'];
|
$maxage = (isset($_data['maxage']) && $_data['maxage'] != "") ? intval($_data['maxage']) : $is_now['maxage'];
|
||||||
$maxbytespersecond = (isset($_data['maxbytespersecond']) && $_data['maxbytespersecond'] != "") ? intval($_data['maxbytespersecond']) : $is_now['maxbytespersecond'];
|
$maxbytespersecond = (isset($_data['maxbytespersecond']) && $_data['maxbytespersecond'] != "") ? intval($_data['maxbytespersecond']) : $is_now['maxbytespersecond'];
|
||||||
$timeout1 = (isset($_data['timeout1']) && $_data['timeout1'] != "") ? intval($_data['timeout1']) : $is_now['timeout1'];
|
$timeout1 = (isset($_data['timeout1']) && $_data['timeout1'] != "") ? intval($_data['timeout1']) : $is_now['timeout1'];
|
||||||
@@ -873,8 +874,9 @@ function syncjob($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$password1 = '';
|
$password1 = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Structured custom params: validate option names against the allowlist, store as JSON pairs
|
if ($custom_params_provided) {
|
||||||
$custom_params = imapsync_normalize_custom_params($custom_params);
|
$custom_params = imapsync_normalize_custom_params($custom_params);
|
||||||
|
}
|
||||||
if ($custom_params === false) {
|
if ($custom_params === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
|
|||||||
@@ -1535,7 +1535,7 @@ function init_db_schema()
|
|||||||
|
|
||||||
// Syncjobs: migrate custom_params from raw imapsync string to structured JSON pairs.
|
// 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.
|
// Old values could not contain spaces (they were rejected), so a whitespace split is safe.
|
||||||
$allow = $GLOBALS['IMAPSYNC_OPTIONS']['whitelist'];
|
$allow = $GLOBALS['IMAPSYNC_OPTIONS'];
|
||||||
$cp_upd = $pdo->prepare("UPDATE `imapsync` SET `custom_params` = :cp WHERE `id` = :id");
|
$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) {
|
foreach ($pdo->query("SELECT `id`, `custom_params` FROM `imapsync`")->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
$cp = trim((string)$r['custom_params']);
|
$cp = trim((string)$r['custom_params']);
|
||||||
@@ -1548,7 +1548,7 @@ function init_db_schema()
|
|||||||
$opt = ltrim($tokens[$i], '-'); $val = '';
|
$opt = ltrim($tokens[$i], '-'); $val = '';
|
||||||
if (strpos($opt, '=') !== false) { list($opt, $val) = explode('=', $opt, 2); }
|
if (strpos($opt, '=') !== false) { list($opt, $val) = explode('=', $opt, 2); }
|
||||||
elseif ($i + 1 < count($tokens) && strpos($tokens[$i + 1], '--') !== 0) { $val = $tokens[++$i]; }
|
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
|
if (!array_key_exists(strtolower($opt), $allow)) continue; // drop options no longer allowed
|
||||||
$pairs[] = array('o' => strtolower($opt), 'v' => $val);
|
$pairs[] = array('o' => strtolower($opt), 'v' => $val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+84
-91
@@ -281,96 +281,89 @@ $RSPAMD_MAPS = array(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Allowed imapsync options for a sync job's custom_params, mapped to whether they take a value:
|
||||||
|
// true = option takes a value
|
||||||
|
// false = pure flag, no value
|
||||||
|
// Anything not listed here is rejected.
|
||||||
|
// NEVER add (RCE): 'delete2foldersonly', 'delete2foldersbutnot', 'regexflag', 'regexmess', 'pipemess', 'regextrans2', 'maxlinelengthcmd'
|
||||||
$IMAPSYNC_OPTIONS = array(
|
$IMAPSYNC_OPTIONS = array(
|
||||||
'whitelist' => array(
|
'abort' => false,
|
||||||
'abort',
|
'authmd51' => false,
|
||||||
'authmd51',
|
'authuser1' => true,
|
||||||
'authuser1',
|
'debug' => false,
|
||||||
'debug',
|
'debugcontent' => false,
|
||||||
'debugcontent',
|
'debugcrossduplicates' => false,
|
||||||
'debugcrossduplicates',
|
'debugflags' => false,
|
||||||
'debugflags',
|
'debugfolders' => false,
|
||||||
'debugfolders',
|
'debugimap' => false,
|
||||||
'debugimap',
|
'debugimap1' => false,
|
||||||
'debugimap1',
|
'debugmemory' => false,
|
||||||
'debugmemory',
|
'debugssl' => true,
|
||||||
'debugssl',
|
'delete1emptyfolders' => false,
|
||||||
'delete1emptyfolders',
|
'delete2folders' => false,
|
||||||
'delete2folders',
|
'disarmreadreceipts' => false,
|
||||||
'disarmreadreceipts',
|
'domain1' => true,
|
||||||
'domain1',
|
'domino1' => false,
|
||||||
'domino1',
|
'errorsmax' => true,
|
||||||
'errorsmax',
|
'exchange1' => false,
|
||||||
'exchange1',
|
'exitwhenover' => true,
|
||||||
'exitwhenover',
|
'expunge1' => false,
|
||||||
'expunge1',
|
'f1f2' => true,
|
||||||
'f1f2',
|
'filterbuggyflags' => false,
|
||||||
'filterbuggyflags',
|
'folder' => true,
|
||||||
'folder',
|
'folderfirst' => true,
|
||||||
'folderfirst',
|
'folderlast' => true,
|
||||||
'folderlast',
|
'folderrec' => true,
|
||||||
'folderrec',
|
'gmail1' => false,
|
||||||
'gmail1',
|
'idatefromheader' => false,
|
||||||
'idatefromheader',
|
'include' => true,
|
||||||
'include',
|
'inet4' => false,
|
||||||
'inet4',
|
'inet6' => false,
|
||||||
'inet6',
|
'justconnect' => false,
|
||||||
'justconnect',
|
'justfolders' => false,
|
||||||
'justfolders',
|
'justfoldersizes' => false,
|
||||||
'justfoldersizes',
|
'justlogin' => false,
|
||||||
'justlogin',
|
'keepalive1' => false,
|
||||||
'keepalive1',
|
'log' => false,
|
||||||
'log',
|
'maxbytesafter' => true,
|
||||||
'maxbytesafter',
|
'maxlinelength' => true,
|
||||||
'maxlinelength',
|
'maxmessagespersecond' => true,
|
||||||
'maxmessagespersecond',
|
'maxsize' => true,
|
||||||
'maxsize',
|
'maxsleep' => true,
|
||||||
'maxsleep',
|
'minage' => true,
|
||||||
'minage',
|
'minsize' => true,
|
||||||
'minsize',
|
'noabletosearch' => false,
|
||||||
'noabletosearch',
|
'noabletosearch1' => false,
|
||||||
'noabletosearch1',
|
'noexpunge1' => false,
|
||||||
'noexpunge1',
|
'nofoldersizesatend' => false,
|
||||||
'nofoldersizesatend',
|
'noid' => false,
|
||||||
'noid',
|
'nolog' => false,
|
||||||
'nolog',
|
'nomixfolders' => false,
|
||||||
'nomixfolders',
|
'noresyncflags' => false,
|
||||||
'noresyncflags',
|
'nossl1' => false,
|
||||||
'nossl1',
|
'nosyncacls' => false,
|
||||||
'nosyncacls',
|
'notls1' => false,
|
||||||
'notls1',
|
'nousecache' => false,
|
||||||
'nousecache',
|
'office1' => false,
|
||||||
'office1',
|
'prefix1' => true,
|
||||||
'prefix1',
|
'resyncflags' => false,
|
||||||
'resyncflags',
|
'resynclabels' => false,
|
||||||
'resynclabels',
|
'search' => true,
|
||||||
'search',
|
'search1' => true,
|
||||||
'search1',
|
'sep1' => true,
|
||||||
'sep1',
|
'skipemptyfolders' => false,
|
||||||
'skipemptyfolders',
|
'subfolder1' => true,
|
||||||
'subfolder1',
|
'subscribe' => false,
|
||||||
'subscribe',
|
'subscribed' => false,
|
||||||
'subscribed',
|
'syncacls' => false,
|
||||||
'syncacls',
|
'syncduplicates' => false,
|
||||||
'syncduplicates',
|
'syncinternaldates' => false,
|
||||||
'syncinternaldates',
|
'synclabels' => false,
|
||||||
'synclabels',
|
'tests' => false,
|
||||||
'tests',
|
'testslive' => false,
|
||||||
'testslive',
|
'testslive6' => false,
|
||||||
'testslive6',
|
'truncmess' => true,
|
||||||
'truncmess',
|
'usecache' => false,
|
||||||
'usecache',
|
'useheader' => true,
|
||||||
'useheader',
|
'useuid' => false,
|
||||||
'useuid'
|
|
||||||
),
|
|
||||||
'blacklist' => array(
|
|
||||||
'skipmess',
|
|
||||||
'delete2foldersonly',
|
|
||||||
'delete2foldersbutnot',
|
|
||||||
'regexflag',
|
|
||||||
'regexmess',
|
|
||||||
'pipemess',
|
|
||||||
'regextrans2',
|
|
||||||
'maxlinelengthcmd'
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -520,44 +520,62 @@ function imapsyncSyncSourceOauthToggle($form) {
|
|||||||
$form.find('.imapsync-source-redirect-uri').val(window.location.origin + '/syncjob-oauth');
|
$form.find('.imapsync-source-redirect-uri').val(window.location.origin + '/syncjob-oauth');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allowlisted imapsync option names for the custom-params selects. Loaded once and cached.
|
// Allowlisted imapsync options as a map { name: takesValue }. Loaded once and cached.
|
||||||
var imapsyncCpOptions = null;
|
var imapsyncCpOptions = null;
|
||||||
function imapsyncCpEnsureOptions(cb) {
|
function imapsyncCpEnsureOptions(cb) {
|
||||||
if (imapsyncCpOptions !== null) { cb(); return; }
|
if (imapsyncCpOptions !== null) { cb(); return; }
|
||||||
$.get("/api/v1/get/syncjob_options", function(opts) {
|
$.get("/api/v1/get/syncjob_options", function(opts) {
|
||||||
imapsyncCpOptions = Array.isArray(opts) ? opts : [];
|
imapsyncCpOptions = (opts && typeof opts === 'object') ? opts : {};
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append one row to an editor; option is a bootstrap-select (not free-text), value is arbitrary.
|
// A imapsync flag option takes no value
|
||||||
|
function imapsyncCpTakesValue(name) {
|
||||||
|
return !(imapsyncCpOptions && imapsyncCpOptions[name] === false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable/disable/require a row's value field based on the selected option.
|
||||||
|
function imapsyncCpApplyValueState($row) {
|
||||||
|
var name = $row.find('.imapsync-cp-opt').val() || '';
|
||||||
|
var isFlag = (name !== '') && !imapsyncCpTakesValue(name);
|
||||||
|
var needsValue = (name !== '') && !isFlag;
|
||||||
|
var $val = $row.find('.imapsync-cp-val');
|
||||||
|
$val.prop('disabled', isFlag);
|
||||||
|
if (isFlag) $val.val('');
|
||||||
|
$val.prop('required', needsValue);
|
||||||
|
$val.attr('placeholder', isFlag
|
||||||
|
? ((lang.syncjobs && lang.syncjobs.custom_param_no_value) || 'no value')
|
||||||
|
: ((lang.syncjobs && lang.syncjobs.custom_param_value) || 'value'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append one row to an editor; option is a native <select> (not free-text), value is arbitrary.
|
||||||
function imapsyncCpAddRow($editor, o, v) {
|
function imapsyncCpAddRow($editor, o, v) {
|
||||||
var $row = $(
|
var $row = $(
|
||||||
'<div class="input-group mb-2 imapsync-cp-row">' +
|
'<div class="input-group mb-2 imapsync-cp-row">' +
|
||||||
'<select class="form-control imapsync-cp-opt" data-width="35%"></select>' +
|
'<select class="form-control imapsync-cp-opt" style="flex:0 0 35%"></select>' +
|
||||||
'<input type="text" class="form-control imapsync-cp-val">' +
|
'<input type="text" class="form-control imapsync-cp-val">' +
|
||||||
'<button type="button" class="btn btn-secondary imapsync-cp-remove"><i class="bi bi-trash"></i></button>' +
|
'<button type="button" class="btn btn-secondary imapsync-cp-remove"><i class="bi bi-trash"></i></button>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
);
|
);
|
||||||
var $sel = $row.find('.imapsync-cp-opt')
|
var $sel = $row.find('.imapsync-cp-opt');
|
||||||
.attr('title', (lang.syncjobs && lang.syncjobs.custom_param_option) || 'option');
|
$sel.append($('<option></option>').attr('value', '').text('— ' + ((lang.syncjobs && lang.syncjobs.custom_param_option) || 'option') + ' —'));
|
||||||
(imapsyncCpOptions || []).forEach(function(name) {
|
Object.keys(imapsyncCpOptions || {}).forEach(function(name) {
|
||||||
$sel.append($('<option></option>').attr('value', name).text(name));
|
$sel.append($('<option></option>').attr('value', name).text(name));
|
||||||
});
|
});
|
||||||
// keep a stored value that is no longer allowlisted visible instead of silently dropping it
|
// keep a stored value that is no longer allowlisted visible instead of silently dropping it
|
||||||
if (o && (imapsyncCpOptions || []).indexOf(o) === -1) {
|
if (o && !(imapsyncCpOptions && (o in imapsyncCpOptions))) {
|
||||||
$sel.append($('<option></option>').attr('value', o).text(o));
|
$sel.append($('<option></option>').attr('value', o).text(o));
|
||||||
}
|
}
|
||||||
$sel.val(o || '');
|
$sel.val(o || '');
|
||||||
$row.find('.imapsync-cp-val').attr('placeholder', (lang.syncjobs && lang.syncjobs.custom_param_value) || 'value').val(v || '');
|
$row.find('.imapsync-cp-val').val(v || '');
|
||||||
$editor.find('.imapsync-cp-rows').append($row);
|
$editor.find('.imapsync-cp-rows').append($row);
|
||||||
$sel.selectpicker();
|
imapsyncCpApplyValueState($row);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the rows of an editor from its hidden JSON value.
|
// Build the rows of an editor from its hidden JSON value.
|
||||||
function imapsyncCpBuild($editor) {
|
function imapsyncCpBuild($editor) {
|
||||||
var $hidden = $editor.siblings('.imapsync-cp-value');
|
var $hidden = $editor.siblings('.imapsync-cp-value');
|
||||||
$editor.find('.imapsync-cp-opt').selectpicker('destroy');
|
|
||||||
$editor.find('.imapsync-cp-rows').empty();
|
$editor.find('.imapsync-cp-rows').empty();
|
||||||
var pairs = [];
|
var pairs = [];
|
||||||
try { pairs = JSON.parse($hidden.val() || '[]'); } catch (e) { pairs = []; }
|
try { pairs = JSON.parse($hidden.val() || '[]'); } catch (e) { pairs = []; }
|
||||||
@@ -581,6 +599,7 @@ function imapsyncCpSerialize($editor) {
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Custom-params editor: keep the hidden JSON in sync with the rows
|
// Custom-params editor: keep the hidden JSON in sync with the rows
|
||||||
$(document).on('change', '.imapsync-cp-opt', function() {
|
$(document).on('change', '.imapsync-cp-opt', function() {
|
||||||
|
imapsyncCpApplyValueState($(this).closest('.imapsync-cp-row'));
|
||||||
imapsyncCpSerialize($(this).closest('.imapsync-cp-editor'));
|
imapsyncCpSerialize($(this).closest('.imapsync-cp-editor'));
|
||||||
});
|
});
|
||||||
$(document).on('input', '.imapsync-cp-val', function() {
|
$(document).on('input', '.imapsync-cp-val', function() {
|
||||||
@@ -592,7 +611,6 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
$(document).on('click', '.imapsync-cp-remove', function() {
|
$(document).on('click', '.imapsync-cp-remove', function() {
|
||||||
var $editor = $(this).closest('.imapsync-cp-editor');
|
var $editor = $(this).closest('.imapsync-cp-editor');
|
||||||
$(this).closest('.imapsync-cp-row').find('.imapsync-cp-opt').selectpicker('destroy');
|
|
||||||
$(this).closest('.imapsync-cp-row').remove();
|
$(this).closest('.imapsync-cp-row').remove();
|
||||||
imapsyncCpSerialize($editor);
|
imapsyncCpSerialize($editor);
|
||||||
});
|
});
|
||||||
@@ -609,11 +627,12 @@ $(document).ready(function() {
|
|||||||
var $form = $sel.closest('form');
|
var $form = $sel.closest('form');
|
||||||
var isOauth = ($opt.data('auth-type') === 'XOAUTH2');
|
var isOauth = ($opt.data('auth-type') === 'XOAUTH2');
|
||||||
var isAuthCode = isOauth && ($opt.data('oauth-flow') === 'authorization_code');
|
var isAuthCode = isOauth && ($opt.data('oauth-flow') === 'authorization_code');
|
||||||
|
var isEdit = ($form.data('id') === 'editsyncjob');
|
||||||
$form.find('.password1-row').toggle(!isOauth);
|
$form.find('.password1-row').toggle(!isOauth);
|
||||||
$form.find('input[name="password1"]').prop('required', !isOauth);
|
$form.find('input[name="password1"]').prop('required', !isOauth && !isEdit);
|
||||||
$form.find('.imapsync-oauth-connect-row').toggle(isAuthCode);
|
$form.find('.imapsync-oauth-connect-row').toggle(isAuthCode);
|
||||||
$form.find('.imapsync-user1-row').toggle(!isAuthCode);
|
$form.find('.imapsync-user1-row').toggle(!isAuthCode);
|
||||||
$form.find('input[name="user1"]').prop('required', !isAuthCode);
|
$form.find('input[name="user1"]').prop('required', !isAuthCode && !isEdit);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Source editor: OAuth block + authorization_code fields
|
// Source editor: OAuth block + authorization_code fields
|
||||||
|
|||||||
@@ -1050,8 +1050,7 @@ if (isset($_GET['query'])) {
|
|||||||
process_get_return($data);
|
process_get_return($data);
|
||||||
break;
|
break;
|
||||||
case "syncjob_options":
|
case "syncjob_options":
|
||||||
// allowlisted imapsync option names, for the custom-params datalist in the UI
|
process_get_return($GLOBALS['IMAPSYNC_OPTIONS']);
|
||||||
process_get_return($GLOBALS['IMAPSYNC_OPTIONS']['whitelist']);
|
|
||||||
break;
|
break;
|
||||||
case "syncjob_source":
|
case "syncjob_source":
|
||||||
switch ($object) {
|
switch ($object) {
|
||||||
|
|||||||
@@ -1297,7 +1297,8 @@
|
|||||||
"custom_params": "Eigene Parameter",
|
"custom_params": "Eigene Parameter",
|
||||||
"custom_params_hint": "Pro Zeile eine imapsync-Option. Es werden nur erlaubte Optionen akzeptiert; der Wert darf beliebige Zeichen enthalten (Leerzeichen, Regex, …) und wird als ein einzelnes Argument übergeben.",
|
"custom_params_hint": "Pro Zeile eine imapsync-Option. Es werden nur erlaubte Optionen akzeptiert; der Wert darf beliebige Zeichen enthalten (Leerzeichen, Regex, …) und wird als ein einzelnes Argument übergeben.",
|
||||||
"custom_param_option": "Option",
|
"custom_param_option": "Option",
|
||||||
"custom_param_value": "Wert (optional)",
|
"custom_param_value": "Wert",
|
||||||
|
"custom_param_no_value": "kein Wert nötig",
|
||||||
"custom_param_add": "Parameter hinzufügen",
|
"custom_param_add": "Parameter hinzufügen",
|
||||||
"order_id": "Position",
|
"order_id": "Position",
|
||||||
"order_id_hint": "Kleinere Position läuft zuerst; Ändern sortiert die globale Warteschlange um.",
|
"order_id_hint": "Kleinere Position läuft zuerst; Ändern sortiert die globale Warteschlange um.",
|
||||||
|
|||||||
@@ -1304,7 +1304,8 @@
|
|||||||
"custom_params": "Custom parameters",
|
"custom_params": "Custom parameters",
|
||||||
"custom_params_hint": "Add one imapsync option per row. Only allowlisted options are accepted; the value may contain any character (spaces, regex, …) and is passed as a single argument.",
|
"custom_params_hint": "Add one imapsync option per row. Only allowlisted options are accepted; the value may contain any character (spaces, regex, …) and is passed as a single argument.",
|
||||||
"custom_param_option": "Option",
|
"custom_param_option": "Option",
|
||||||
"custom_param_value": "Value (optional)",
|
"custom_param_value": "Value",
|
||||||
|
"custom_param_no_value": "no value needed",
|
||||||
"custom_param_add": "Add parameter",
|
"custom_param_add": "Add parameter",
|
||||||
"order_id": "Position",
|
"order_id": "Position",
|
||||||
"order_id_hint": "Lower position runs first; setting it re-sorts the global queue.",
|
"order_id_hint": "Lower position runs first; setting it re-sorts the global queue.",
|
||||||
|
|||||||
@@ -96,7 +96,6 @@
|
|||||||
<button type="button" class="btn btn-sm btn-secondary imapsync-cp-add"><i class="bi bi-plus-lg"></i> {{ lang.syncjobs.custom_param_add }}</button>
|
<button type="button" class="btn btn-sm btn-secondary imapsync-cp-add"><i class="bi bi-plus-lg"></i> {{ lang.syncjobs.custom_param_add }}</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="custom_params" class="imapsync-cp-value" value="{{ result.custom_params }}">
|
<input type="hidden" name="custom_params" class="imapsync-cp-value" value="{{ result.custom_params }}">
|
||||||
<small class="text-muted d-block mt-1">{{ lang.syncjobs.custom_params_hint }}</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
|
|||||||
@@ -1034,7 +1034,6 @@
|
|||||||
<button type="button" class="btn btn-sm btn-secondary imapsync-cp-add"><i class="bi bi-plus-lg"></i> {{ lang.syncjobs.custom_param_add }}</button>
|
<button type="button" class="btn btn-sm btn-secondary imapsync-cp-add"><i class="bi bi-plus-lg"></i> {{ lang.syncjobs.custom_param_add }}</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="custom_params" class="imapsync-cp-value" value="">
|
<input type="hidden" name="custom_params" class="imapsync-cp-value" value="">
|
||||||
<small class="text-muted d-block mt-1">{{ lang.syncjobs.custom_params_hint }}</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
|
|||||||
@@ -118,7 +118,6 @@
|
|||||||
<button type="button" class="btn btn-sm btn-secondary imapsync-cp-add"><i class="bi bi-plus-lg"></i> {{ lang.syncjobs.custom_param_add }}</button>
|
<button type="button" class="btn btn-sm btn-secondary imapsync-cp-add"><i class="bi bi-plus-lg"></i> {{ lang.syncjobs.custom_param_add }}</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="custom_params" class="imapsync-cp-value" value="">
|
<input type="hidden" name="custom_params" class="imapsync-cp-value" value="">
|
||||||
<small class="text-muted d-block mt-1">{{ lang.syncjobs.custom_params_hint }}</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user