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