email_server: aliases: Drop hash DB and use sqlite3 directly

- Postfix has the ability to use sqlite3 databases directly. There is no need to
synchronize to a hash db and then use that.

- Store the aliases database in /var/lib/postfix/. This will make backup and
restore easier and remove dependence on FreedomBox and its data directory.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2021-10-14 18:29:33 -07:00 committed by James Valleroy
parent 91f18a0e52
commit 6e8b825d44
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
6 changed files with 26 additions and 49 deletions

View File

@ -30,6 +30,7 @@ package_conflicts_action = 'ignore'
packages = [
'postfix-ldap',
'postfix-sqlite',
'dovecot-pop3d',
'dovecot-imapd',
'dovecot-ldap',

View File

@ -2,13 +2,10 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import contextlib
import dbm
import logging
import os
import pwd
import sqlite3
from plinth.modules.email_server import lock
from plinth import actions
from . import models
@ -24,12 +21,7 @@ CREATE TABLE IF NOT EXISTS Alias (
COMMIT;
"""
mailsrv_dir = '/var/lib/plinth/mailsrv'
hash_db_path = mailsrv_dir + '/aliases'
sqlite_db_path = mailsrv_dir + '/aliases.sqlite3'
alias_sync_mutex = lock.Mutex('alias-sync')
logger = logging.getLogger(__name__)
sqlite_db_path = '/var/lib/postfix/freedombox-aliases/aliases.sqlite3'
@contextlib.contextmanager
@ -74,8 +66,6 @@ def put(uid_number, email_name):
with db_cursor() as cur:
cur.execute(s, (email_name, uid_number, 1, email_name))
schedule_hash_update()
def delete(uid_number, alias_list):
s = 'DELETE FROM Alias WHERE uid_number=? AND email_name=?'
@ -84,7 +74,6 @@ def delete(uid_number, alias_list):
cur.execute('BEGIN')
cur.executemany(s, parameter_seq)
cur.execute('COMMIT')
schedule_hash_update()
def set_enabled(uid_number, alias_list):
@ -102,46 +91,14 @@ def _set_status(uid_number, alias_list, status):
cur.execute('BEGIN')
cur.executemany(s, parameter_seq)
cur.execute('COMMIT')
schedule_hash_update()
def schedule_hash_update():
tmp = hash_db_path + '-tmp'
with alias_sync_mutex.lock_all(), db_cursor() as cur:
all_aliases = cur.execute('SELECT * FROM Alias')
# Delete the temp file if exists
if os.path.exists(tmp):
os.unlink(tmp)
# Create new alias db at temp path
db = dbm.ndbm.open(tmp, 'c')
try:
for row in all_aliases:
alias = models.Alias(**row)
key = alias.email_name.encode('ascii') + b'\0'
if alias.enabled:
value = str(alias.uid_number).encode('ascii')
value += b'@localhost\0'
else:
value = b'/dev/null\0'
db[key] = value
finally:
db.close()
# Atomically replace old alias db, rename(2)
os.rename(tmp + '.db', hash_db_path + '.db')
def first_setup():
actions.superuser_run('email_server', ['-i', 'aliases', 'setup'])
_create_db_schema_if_not_exists()
schedule_hash_update()
def _create_db_schema_if_not_exists():
# Create folder
if not os.path.isdir(mailsrv_dir):
os.mkdir(mailsrv_dir)
# Create schema if not exists
with db_cursor() as cur:
cur.executescript(map_db_schema_script)

View File

@ -3,6 +3,8 @@
Provides diagnosis and repair of email server configuration issues
"""
from . import domain, home, ldap, models, rcube, spam, tls
from . import aliases, domain, home, ldap, models, rcube, spam, tls
__all__ = ['domain', 'home', 'ldap', 'models', 'rcube', 'spam', 'tls']
__all__ = [
'aliases', 'domain', 'home', 'ldap', 'models', 'rcube', 'spam', 'tls'
]

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Privileged operations for managing aliases."""
import pathlib
import shutil
def action_setup():
"""Create a the sqlite3 database to be managed by FreedomBox."""
path = pathlib.Path('/var/lib/postfix/freedombox-aliases/')
path.mkdir(mode=0o750, exist_ok=True)
shutil.chown(path, user='plinth', group='postfix')

View File

@ -56,7 +56,7 @@ default_smtps_options = {
MAILSRV_DIR = '/var/lib/plinth/mailsrv'
ETC_ALIASES = 'hash:/etc/aliases'
BEFORE_ALIASES = 'ldap:/etc/postfix/freedombox-username-to-uid-number.cf'
AFTER_ALIASES = 'hash:' + aliases.hash_db_path
AFTER_ALIASES = 'sqlite:/etc/postfix/freedombox-aliases.cf'
logger = logging.getLogger(__name__)

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# Maintained by FreedomBox, do not edit.
dbpath = /var/lib/postfix/freedombox-aliases/aliases.sqlite3
query = SELECT uid_number FROM Alias WHERE email_name='%s' AND status=1