VirtualMailManager/maillocation.py
changeset 571 a4aead244f75
parent 568 14abdd04ddf5
child 637 ca6621caff2f
child 675 d24f094d1cb5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VirtualMailManager/maillocation.py	Thu Jun 28 19:26:50 2012 +0000
@@ -0,0 +1,161 @@
+# -*- coding: UTF-8 -*-
+# Copyright (c) 2008 - 2012, Pascal Volk
+# See COPYING for distribution information.
+"""
+    VirtualMailManager.maillocation
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Virtual Mail Manager's maillocation module to handle Dovecot's
+    mail_location setting for accounts.
+
+"""
+
+from VirtualMailManager.constants import MAILLOCATION_INIT
+from VirtualMailManager.errors import MailLocationError as MLErr
+from VirtualMailManager.pycompat import all
+
+
+__all__ = ('MailLocation', 'known_format')
+
+_ = lambda msg: msg
+_format_info = {
+    'maildir': dict(dovecot_version=0x10000f00, postfix=True),
+    'mdbox': dict(dovecot_version=0x20000b05, postfix=False),
+    'sdbox': dict(dovecot_version=0x20000c03, postfix=False),
+}
+
+
+class MailLocation(object):
+    """Class to handle mail_location relevant information."""
+    __slots__ = ('_directory', '_mbfmt', '_mid', '_dbh')
+    _kwargs = ('mid', 'mbfmt', 'directory')
+
+    def __init__(self, dbh, **kwargs):
+        """Creates a new MailLocation instance.
+
+        Either the mid keyword or the mbfmt and directory keywords must be
+        specified.
+
+        Arguments:
+
+        `dbh` : pyPgSQL.PgSQL.Connection
+          A database connection for the database access.
+
+        Keyword arguments:
+
+        `mid` : int
+          the id of a mail_location
+        `mbfmt` : str
+          the mailbox format of the mail_location. One out of: ``maildir``,
+          ``sdbox`` and ``mdbox``.
+        `directory` : str
+          name of the mailbox root directory.
+        """
+        self._dbh = dbh
+        self._directory = None
+        self._mbfmt = None
+        self._mid = 0
+
+        for key in kwargs.iterkeys():
+            if key not in self.__class__._kwargs:
+                raise ValueError('unrecognized keyword: %r' % key)
+        mid = kwargs.get('mid')
+        if mid:
+            assert isinstance(mid, (int, long))
+            self._load_by_mid(mid)
+        else:
+            args = kwargs.get('mbfmt'), kwargs.get('directory')
+            assert all(isinstance(arg, basestring) for arg in args)
+            if args[0].lower() not in _format_info:
+                raise MLErr(_(u"Unsupported mailbox format: '%s'") % args[0],
+                            MAILLOCATION_INIT)
+            directory = args[1].strip()
+            if not directory:
+                raise MLErr(_(u"Empty directory name"), MAILLOCATION_INIT)
+            if len(directory) > 20:
+                raise MLErr(_(u"Directory name is too long: '%s'") % directory,
+                            MAILLOCATION_INIT)
+            self._load_by_names(args[0].lower(), directory)
+
+    def __str__(self):
+        return u'%s:~/%s' % (self._mbfmt, self._directory)
+
+    @property
+    def directory(self):
+        """The mail_location's directory name."""
+        return self._directory
+
+    @property
+    def dovecot_version(self):
+        """The required Dovecot version for this mailbox format."""
+        return _format_info[self._mbfmt]['dovecot_version']
+
+    @property
+    def postfix(self):
+        """`True` if Postfix supports this mailbox format, else `False`."""
+        return _format_info[self._mbfmt]['postfix']
+
+    @property
+    def mbformat(self):
+        """The mail_location's mailbox format."""
+        return self._mbfmt
+
+    @property
+    def mail_location(self):
+        """The mail_location, e.g. ``maildir:~/Maildir``"""
+        return self.__str__()
+
+    @property
+    def mid(self):
+        """The mail_location's unique ID."""
+        return self._mid
+
+    def _load_by_mid(self, mid):
+        """Load mail_location relevant information by *mid*"""
+        dbc = self._dbh.cursor()
+        dbc.execute('SELECT format, directory FROM mailboxformat, '
+                    'maillocation WHERE mid = %u AND '
+                    'maillocation.fid = mailboxformat.fid' % mid)
+        result = dbc.fetchone()
+        dbc.close()
+        if not result:
+            raise ValueError('Unknown mail_location id specified: %r' % mid)
+        self._mid = mid
+        self._mbfmt, self._directory = result
+
+    def _load_by_names(self, mbfmt, directory):
+        """Try to load mail_location relevant information by *mbfmt* and
+        *directory* name. If it fails goto _save()."""
+        dbc = self._dbh.cursor()
+        dbc.execute("SELECT mid FROM maillocation WHERE fid = (SELECT fid "
+                    "FROM mailboxformat WHERE format = %s) AND directory = %s",
+                    (mbfmt, directory))
+        result = dbc.fetchone()
+        dbc.close()
+        if not result:
+            self._save(mbfmt, directory)
+        else:
+            self._mid = result[0]
+            self._mbfmt = mbfmt
+            self._directory = directory
+
+    def _save(self, mbfmt, directory):
+        """Save a new mail_location in the database."""
+        dbc = self._dbh.cursor()
+        dbc.execute("SELECT nextval('maillocation_id')")
+        mid = dbc.fetchone()[0]
+        dbc.execute("INSERT INTO maillocation (fid, mid, directory) VALUES ("
+                    "(SELECT fid FROM mailboxformat WHERE format = %s), %s, "
+                    "%s)",  (mbfmt, mid, directory))
+        self._dbh.commit()
+        dbc.close()
+        self._mid = mid
+        self._mbfmt = mbfmt
+        self._directory = directory
+
+
+def known_format(mbfmt):
+    """Checks if the mailbox format *mbfmt* is known, returns bool."""
+    return mbfmt.lower() in _format_info
+
+del _