Sunil Mohan Adapa d6a9392704
email_server: Use rollback journal for aliases sqlite DB
WAL journaling mode is causing problems with postfix unable to open the database
despite ownership and permission changes. Default is the DELETE rollback journal
mode. The former gives more performance but in our case, writes are very rare
and the DB is used mostly just for reads.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2021-11-03 19:44:48 -04:00

116 lines
3.0 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""Manage email aliases."""
import contextlib
import pwd
import sqlite3
from dataclasses import InitVar, dataclass, field
from plinth import actions
@dataclass
class Alias:
value: str
name: str
enabled: bool = field(init=False)
status: InitVar[int]
def __post_init__(self, status):
self.enabled = (status != 0)
@contextlib.contextmanager
def _get_cursor():
"""Return a DB cursor as context manager."""
# Turn ON autocommit mode
db_path = '/var/lib/postfix/freedombox-aliases/aliases.sqlite3'
connection = sqlite3.connect(db_path, isolation_level=None)
connection.row_factory = sqlite3.Row
try:
cursor = connection.cursor()
yield cursor
finally:
connection.close()
def get(username):
"""Get all aliases of a user."""
query = 'SELECT name, value, status FROM alias WHERE value=?'
with _get_cursor() as cursor:
rows = cursor.execute(query, (username, ))
return [Alias(**row) for row in rows]
def exists(name):
"""Return whether alias is already taken."""
try:
pwd.getpwnam(name)
return True
except KeyError:
pass
with _get_cursor() as cursor:
query = 'SELECT COUNT(*) FROM alias WHERE name=?'
cursor.execute(query, (name, ))
return cursor.fetchone()[0] != 0
def put(username, name):
"""Insert if not exists a new alias."""
query = 'INSERT INTO alias (name, value, status) VALUES (?, ?, ?)'
with _get_cursor() as cursor:
try:
cursor.execute(query, (name, username, 1))
except sqlite3.IntegrityError:
pass # Alias exists, rare since we are already checking
def delete(username, aliases):
"""Delete a set of aliases."""
query = 'DELETE FROM alias WHERE value=? AND name=?'
parameter_seq = ((username, name) for name in aliases)
with _get_cursor() as cursor:
cursor.execute('BEGIN')
cursor.executemany(query, parameter_seq)
cursor.execute('COMMIT')
def enable(username, aliases):
"""Enable a list of aliases."""
return _set_status(username, aliases, 1)
def disable(username, aliases):
"""Disable a list of aliases."""
return _set_status(username, aliases, 0)
def _set_status(username, aliases, status):
"""Set the status value of a list of aliases."""
query = 'UPDATE alias SET status=? WHERE value=? AND name=?'
parameter_seq = ((status, username, name) for name in aliases)
with _get_cursor() as cursor:
cursor.execute('BEGIN')
cursor.executemany(query, parameter_seq)
cursor.execute('COMMIT')
def first_setup():
"""Create the database file and schema inside it."""
actions.superuser_run('email_server', ['aliases', 'setup'])
# Create schema if not exists
query = '''
BEGIN;
CREATE TABLE IF NOT EXISTS alias (
name TEXT NOT NULL,
value TEXT NOT NULL,
status INTEGER NOT NULL,
PRIMARY KEY (name)
);
COMMIT;
'''
with _get_cursor() as cursor:
cursor.executescript(query)