mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-03-10 06:10:14 +01:00
Improve Sieve: folder dropdown in rules UI, INBOX. prefix fix, robust upgrade regexes
- Replace free text input with folder dropdown for move-to-folder rules - Auto-prefix INBOX. namespace to folder names in sieve scripts - Strip INBOX. prefix when parsing sieve scripts back to rules - Make upgrade setupSieve() regexes more flexible to handle config variations - Add os.makedirs for conf.d directory in both install and upgrade - Validate ManageSieve config with both inet_listener and service checks
This commit is contained in:
@@ -696,6 +696,7 @@ module cyberpanel_ols {
|
||||
|
||||
# Write ManageSieve config
|
||||
managesieve_conf = '/etc/dovecot/conf.d/20-managesieve.conf'
|
||||
os.makedirs('/etc/dovecot/conf.d', exist_ok=True)
|
||||
with open(managesieve_conf, 'w') as f:
|
||||
f.write("""protocols = $protocols sieve
|
||||
|
||||
|
||||
@@ -2857,6 +2857,8 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
Upgrade.stdOut("Dovecot not installed, skipping Sieve setup.", 0)
|
||||
return
|
||||
|
||||
import re
|
||||
|
||||
dovecot_conf = '/etc/dovecot/dovecot.conf'
|
||||
with open(dovecot_conf, 'r') as f:
|
||||
content = f.read()
|
||||
@@ -2864,33 +2866,37 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
changed = False
|
||||
|
||||
# Add sieve to protocols if missing
|
||||
if 'sieve' not in content.split('\n')[0]:
|
||||
content = content.replace('protocols = imap pop3', 'protocols = imap pop3 sieve', 1)
|
||||
protocols_match = re.search(r'^protocols\s*=\s*(.+)$', content, re.MULTILINE)
|
||||
if protocols_match and 'sieve' not in protocols_match.group(1):
|
||||
content = content.replace(protocols_match.group(0),
|
||||
protocols_match.group(0) + ' sieve')
|
||||
changed = True
|
||||
|
||||
# Add sieve plugin to protocol lda if missing
|
||||
import re
|
||||
lda_match = re.search(r'(protocol lda\s*\{[^}]*mail_plugins\s*=\s*)(zlib)(\s*\n)', content)
|
||||
if lda_match and 'sieve' not in lda_match.group(0):
|
||||
# Add sieve plugin to protocol lda mail_plugins if missing
|
||||
lda_match = re.search(r'(protocol lda\s*\{[^}]*mail_plugins\s*=\s*)([^\n]+)', content)
|
||||
if lda_match and 'sieve' not in lda_match.group(2):
|
||||
content = content.replace(lda_match.group(0),
|
||||
lda_match.group(1) + 'zlib sieve' + lda_match.group(3))
|
||||
lda_match.group(1) + lda_match.group(2).rstrip() + ' sieve')
|
||||
changed = True
|
||||
|
||||
# Add lda_mailbox_autocreate/autosubscribe for sieve fileinto
|
||||
if 'lda_mailbox_autocreate' not in content:
|
||||
content = re.sub(
|
||||
r'(protocol lda\s*\{[^}]*mail_plugins\s*=\s*zlib sieve\n)',
|
||||
r'\1 lda_mailbox_autocreate = yes\n lda_mailbox_autosubscribe = yes\n',
|
||||
content)
|
||||
changed = True
|
||||
lda_plugins = re.search(r'(protocol lda\s*\{[^}]*mail_plugins\s*=[^\n]+\n)', content)
|
||||
if lda_plugins:
|
||||
content = content.replace(lda_plugins.group(0),
|
||||
lda_plugins.group(0) +
|
||||
' lda_mailbox_autocreate = yes\n lda_mailbox_autosubscribe = yes\n')
|
||||
changed = True
|
||||
|
||||
# Add sieve storage settings to plugin section
|
||||
if 'sieve_dir' not in content:
|
||||
content = re.sub(
|
||||
r'(plugin\s*\{[^}]*zlib_save_level\s*=\s*6\n)',
|
||||
r'\1\n sieve = ~/sieve/.dovecot.sieve\n sieve_dir = ~/sieve\n',
|
||||
content)
|
||||
changed = True
|
||||
plugin_match = re.search(r'(plugin\s*\{[^}]*)(})', content)
|
||||
if plugin_match:
|
||||
content = content.replace(plugin_match.group(0),
|
||||
plugin_match.group(1) +
|
||||
'\n sieve = ~/sieve/.dovecot.sieve\n sieve_dir = ~/sieve\n\n' +
|
||||
plugin_match.group(2))
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
with open(dovecot_conf, 'w') as f:
|
||||
@@ -2901,11 +2907,11 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
if os.path.exists(sql_conf):
|
||||
with open(sql_conf, 'r') as f:
|
||||
sql_content = f.read()
|
||||
if 'as home' not in sql_content and "user_query" in sql_content:
|
||||
sql_content = sql_content.replace(
|
||||
"user_query = SELECT '5000' as uid, '5000' as gid, mail FROM e_users WHERE email='%u';",
|
||||
"user_query = SELECT '5000' as uid, '5000' as gid, mail, CONCAT('/home/vmail/', SUBSTRING_INDEX(email, '@', -1), '/', SUBSTRING_INDEX(email, '@', 1)) as home FROM e_users WHERE email='%u';"
|
||||
)
|
||||
if 'as home' not in sql_content and 'user_query' in sql_content:
|
||||
sql_content = re.sub(
|
||||
r"(user_query\s*=\s*SELECT\s+'5000'\s+as\s+uid,\s+'5000'\s+as\s+gid,\s+mail)\s+(FROM\s+e_users\s+WHERE\s+email='%u';)",
|
||||
r"\1, CONCAT('/home/vmail/', SUBSTRING_INDEX(email, '@', -1), '/', SUBSTRING_INDEX(email, '@', 1)) as home \2",
|
||||
sql_content)
|
||||
with open(sql_conf, 'w') as f:
|
||||
f.write(sql_content)
|
||||
|
||||
@@ -2915,10 +2921,11 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
if os.path.exists(managesieve_conf):
|
||||
with open(managesieve_conf, 'r') as f:
|
||||
existing = f.read()
|
||||
if 'inet_listener sieve' in existing and not existing.strip().startswith('#'):
|
||||
if 'inet_listener sieve' in existing and 'service managesieve' in existing:
|
||||
write_managesieve = False
|
||||
|
||||
if write_managesieve:
|
||||
os.makedirs('/etc/dovecot/conf.d', exist_ok=True)
|
||||
with open(managesieve_conf, 'w') as f:
|
||||
f.write("""protocols = $protocols sieve
|
||||
|
||||
|
||||
@@ -208,7 +208,11 @@ class SieveClient:
|
||||
# Map action
|
||||
if action_type == 'move':
|
||||
requires.add('fileinto')
|
||||
action = 'fileinto "%s";' % action_value
|
||||
# Ensure folder uses INBOX. namespace prefix for dovecot
|
||||
folder = action_value
|
||||
if folder and not folder.startswith('INBOX.'):
|
||||
folder = 'INBOX.%s' % folder
|
||||
action = 'fileinto "%s";' % folder
|
||||
elif action_type == 'forward':
|
||||
requires.add('redirect')
|
||||
action = 'redirect "%s";' % action_value
|
||||
@@ -254,6 +258,9 @@ class SieveClient:
|
||||
action_type = 'move'
|
||||
av = re.search(r'fileinto\s+"([^"]+)"', action_block)
|
||||
action_value = av.group(1) if av else ''
|
||||
# Strip INBOX. namespace prefix for display
|
||||
if action_value.startswith('INBOX.'):
|
||||
action_value = action_value[6:]
|
||||
elif 'redirect' in action_block:
|
||||
action_type = 'forward'
|
||||
av = re.search(r'redirect\s+"([^"]+)"', action_block)
|
||||
|
||||
@@ -392,10 +392,17 @@
|
||||
<option value="flag">Flag</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="wm-field" ng-if="editingRule.actionType !== 'discard' && editingRule.actionType !== 'flag'">
|
||||
<label>{% trans "Target" %}</label>
|
||||
<div class="wm-field" ng-if="editingRule.actionType === 'move'">
|
||||
<label>{% trans "Target Folder" %}</label>
|
||||
<select ng-model="editingRule.actionValue" class="form-control">
|
||||
<option value="">-- {% trans "Select Folder" %} --</option>
|
||||
<option ng-repeat="f in folders" value="{$ f.display_name || f.name $}">{$ f.display_name || f.name $}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="wm-field" ng-if="editingRule.actionType === 'forward'">
|
||||
<label>{% trans "Forward To" %}</label>
|
||||
<input type="text" ng-model="editingRule.actionValue" class="form-control"
|
||||
placeholder="{$ editingRule.actionType === 'move' ? 'Folder name' : 'Email address' $}">
|
||||
placeholder="Email address">
|
||||
</div>
|
||||
</div>
|
||||
<div class="wm-form-actions">
|
||||
|
||||
Reference in New Issue
Block a user