VirtualMailManager/maillocation.py
changeset 760 b678a1c43027
parent 748 659c4476c57c
child 761 e4e656f19771
equal deleted inserted replaced
748:659c4476c57c 760:b678a1c43027
     1 # -*- coding: UTF-8 -*-
       
     2 # Copyright (c) 2008 - 2014, Pascal Volk
       
     3 # See COPYING for distribution information.
       
     4 """
       
     5     VirtualMailManager.maillocation
       
     6     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
     7 
       
     8     Virtual Mail Manager's maillocation module to handle Dovecot's
       
     9     mail_location setting for accounts.
       
    10 
       
    11 """
       
    12 
       
    13 from VirtualMailManager.constants import MAILLOCATION_INIT
       
    14 from VirtualMailManager.errors import MailLocationError as MLErr
       
    15 from VirtualMailManager.pycompat import all
       
    16 
       
    17 
       
    18 __all__ = ('MailLocation', 'known_format')
       
    19 
       
    20 _ = lambda msg: msg
       
    21 _format_info = {
       
    22     'maildir': dict(dovecot_version=0x10000f00, postfix=True),
       
    23     'mdbox': dict(dovecot_version=0x20000b05, postfix=False),
       
    24     'sdbox': dict(dovecot_version=0x20000c03, postfix=False),
       
    25 }
       
    26 
       
    27 
       
    28 class MailLocation(object):
       
    29     """Class to handle mail_location relevant information."""
       
    30     __slots__ = ('_directory', '_mbfmt', '_mid', '_dbh')
       
    31     _kwargs = ('mid', 'mbfmt', 'directory')
       
    32 
       
    33     def __init__(self, dbh, **kwargs):
       
    34         """Creates a new MailLocation instance.
       
    35 
       
    36         Either the mid keyword or the mbfmt and directory keywords must be
       
    37         specified.
       
    38 
       
    39         Arguments:
       
    40 
       
    41         `dbh` : pyPgSQL.PgSQL.Connection
       
    42           A database connection for the database access.
       
    43 
       
    44         Keyword arguments:
       
    45 
       
    46         `mid` : int
       
    47           the id of a mail_location
       
    48         `mbfmt` : str
       
    49           the mailbox format of the mail_location. One out of: ``maildir``,
       
    50           ``sdbox`` and ``mdbox``.
       
    51         `directory` : str
       
    52           name of the mailbox root directory.
       
    53         """
       
    54         self._dbh = dbh
       
    55         self._directory = None
       
    56         self._mbfmt = None
       
    57         self._mid = 0
       
    58 
       
    59         for key in kwargs.iterkeys():
       
    60             if key not in self.__class__._kwargs:
       
    61                 raise ValueError('unrecognized keyword: %r' % key)
       
    62         mid = kwargs.get('mid')
       
    63         if mid:
       
    64             assert isinstance(mid, (int, long))
       
    65             self._load_by_mid(mid)
       
    66         else:
       
    67             args = kwargs.get('mbfmt'), kwargs.get('directory')
       
    68             assert all(isinstance(arg, basestring) for arg in args)
       
    69             if args[0].lower() not in _format_info:
       
    70                 raise MLErr(_(u"Unsupported mailbox format: '%s'") % args[0],
       
    71                             MAILLOCATION_INIT)
       
    72             directory = args[1].strip()
       
    73             if not directory:
       
    74                 raise MLErr(_(u"Empty directory name"), MAILLOCATION_INIT)
       
    75             if len(directory) > 20:
       
    76                 raise MLErr(_(u"Directory name is too long: '%s'") % directory,
       
    77                             MAILLOCATION_INIT)
       
    78             self._load_by_names(args[0].lower(), directory)
       
    79 
       
    80     def __str__(self):
       
    81         return u'%s:~/%s' % (self._mbfmt, self._directory)
       
    82 
       
    83     @property
       
    84     def directory(self):
       
    85         """The mail_location's directory name."""
       
    86         return self._directory
       
    87 
       
    88     @property
       
    89     def dovecot_version(self):
       
    90         """The required Dovecot version for this mailbox format."""
       
    91         return _format_info[self._mbfmt]['dovecot_version']
       
    92 
       
    93     @property
       
    94     def postfix(self):
       
    95         """`True` if Postfix supports this mailbox format, else `False`."""
       
    96         return _format_info[self._mbfmt]['postfix']
       
    97 
       
    98     @property
       
    99     def mbformat(self):
       
   100         """The mail_location's mailbox format."""
       
   101         return self._mbfmt
       
   102 
       
   103     @property
       
   104     def mail_location(self):
       
   105         """The mail_location, e.g. ``maildir:~/Maildir``"""
       
   106         return self.__str__()
       
   107 
       
   108     @property
       
   109     def mid(self):
       
   110         """The mail_location's unique ID."""
       
   111         return self._mid
       
   112 
       
   113     def _load_by_mid(self, mid):
       
   114         """Load mail_location relevant information by *mid*"""
       
   115         dbc = self._dbh.cursor()
       
   116         dbc.execute('SELECT format, directory FROM mailboxformat, '
       
   117                     'maillocation WHERE mid = %u AND '
       
   118                     'maillocation.fid = mailboxformat.fid' % mid)
       
   119         result = dbc.fetchone()
       
   120         dbc.close()
       
   121         if not result:
       
   122             raise ValueError('Unknown mail_location id specified: %r' % mid)
       
   123         self._mid = mid
       
   124         self._mbfmt, self._directory = result
       
   125 
       
   126     def _load_by_names(self, mbfmt, directory):
       
   127         """Try to load mail_location relevant information by *mbfmt* and
       
   128         *directory* name. If it fails goto _save()."""
       
   129         dbc = self._dbh.cursor()
       
   130         dbc.execute("SELECT mid FROM maillocation WHERE fid = (SELECT fid "
       
   131                     "FROM mailboxformat WHERE format = %s) AND directory = %s",
       
   132                     (mbfmt, directory))
       
   133         result = dbc.fetchone()
       
   134         dbc.close()
       
   135         if not result:
       
   136             self._save(mbfmt, directory)
       
   137         else:
       
   138             self._mid = result[0]
       
   139             self._mbfmt = mbfmt
       
   140             self._directory = directory
       
   141 
       
   142     def _save(self, mbfmt, directory):
       
   143         """Save a new mail_location in the database."""
       
   144         dbc = self._dbh.cursor()
       
   145         dbc.execute("SELECT nextval('maillocation_id')")
       
   146         mid = dbc.fetchone()[0]
       
   147         dbc.execute("INSERT INTO maillocation (fid, mid, directory) VALUES ("
       
   148                     "(SELECT fid FROM mailboxformat WHERE format = %s), %s, "
       
   149                     "%s)",  (mbfmt, mid, directory))
       
   150         self._dbh.commit()
       
   151         dbc.close()
       
   152         self._mid = mid
       
   153         self._mbfmt = mbfmt
       
   154         self._directory = directory
       
   155 
       
   156 
       
   157 def known_format(mbfmt):
       
   158     """Checks if the mailbox format *mbfmt* is known, returns bool."""
       
   159     return mbfmt.lower() in _format_info
       
   160 
       
   161 del _