VirtualMailManager/domain.py
branchv0.6.x
changeset 390 660b42391c8e
parent 379 7518d927d443
child 392 ffce67e3c6eb
equal deleted inserted replaced
389:5f7e9f778b29 390:660b42391c8e
    14 
    14 
    15 from VirtualMailManager.constants import \
    15 from VirtualMailManager.constants import \
    16      ACCOUNT_AND_ALIAS_PRESENT, DOMAIN_ALIAS_EXISTS, DOMAIN_EXISTS, \
    16      ACCOUNT_AND_ALIAS_PRESENT, DOMAIN_ALIAS_EXISTS, DOMAIN_EXISTS, \
    17      DOMAIN_INVALID, DOMAIN_TOO_LONG, NO_SUCH_DOMAIN
    17      DOMAIN_INVALID, DOMAIN_TOO_LONG, NO_SUCH_DOMAIN
    18 from VirtualMailManager.errors import DomainError as DomErr
    18 from VirtualMailManager.errors import DomainError as DomErr
    19 from VirtualMailManager.pycompat import any
    19 from VirtualMailManager.pycompat import all, any
       
    20 from VirtualMailManager.quotalimit import QuotaLimit
    20 from VirtualMailManager.transport import Transport
    21 from VirtualMailManager.transport import Transport
    21 
    22 
    22 
    23 
    23 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
    24 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
    24 RE_DOMAIN = re.compile(r"^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$")
    25 RE_DOMAIN = re.compile(r"^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$")
    25 _ = lambda msg: msg
    26 _ = lambda msg: msg
    26 
    27 
    27 
    28 
    28 class Domain(object):
    29 class Domain(object):
    29     """Class to manage e-mail domains."""
    30     """Class to manage e-mail domains."""
    30     __slots__ = ('_directory', '_gid', '_name', '_transport', '_dbh', '_new')
    31     __slots__ = ('_directory', '_gid', '_name', '_qlimit', '_transport',
       
    32                  '_dbh', '_new')
    31 
    33 
    32     def __init__(self, dbh, domainname):
    34     def __init__(self, dbh, domainname):
    33         """Creates a new Domain instance.
    35         """Creates a new Domain instance.
    34 
    36 
    35         Loads all relevant data from the database, if the domain could be
    37         Loads all relevant data from the database, if the domain could be
    47           The name of the domain
    49           The name of the domain
    48         """
    50         """
    49         self._name = check_domainname(domainname)
    51         self._name = check_domainname(domainname)
    50         self._dbh = dbh
    52         self._dbh = dbh
    51         self._gid = 0
    53         self._gid = 0
       
    54         self._qlimit = None
    52         self._transport = None
    55         self._transport = None
    53         self._directory = None
    56         self._directory = None
    54         self._new = True
    57         self._new = True
    55         self._load()
    58         self._load()
    56 
    59 
    60 
    63 
    61         Raises a DomainError if Domain._name isn't the primary name of the
    64         Raises a DomainError if Domain._name isn't the primary name of the
    62         domain.
    65         domain.
    63         """
    66         """
    64         dbc = self._dbh.cursor()
    67         dbc = self._dbh.cursor()
    65         dbc.execute('SELECT dd.gid, tid, domaindir, is_primary FROM '
    68         dbc.execute('SELECT dd.gid, qid, tid, domaindir, is_primary FROM '
    66                     'domain_data dd, domain_name dn WHERE domainname = %s AND '
    69                     'domain_data dd, domain_name dn WHERE domainname = %s AND '
    67                     'dn.gid = dd.gid', (self._name,))
    70                     'dn.gid = dd.gid', (self._name,))
    68         result = dbc.fetchone()
    71         result = dbc.fetchone()
    69         dbc.close()
    72         dbc.close()
    70         if result:
    73         if result:
    71             if not result[3]:
    74             if not result[4]:
    72                 raise DomErr(_(u"The domain '%s' is an alias domain.") %
    75                 raise DomErr(_(u"The domain '%s' is an alias domain.") %
    73                              self._name, DOMAIN_ALIAS_EXISTS)
    76                              self._name, DOMAIN_ALIAS_EXISTS)
    74             self._gid, self._directory = result[0], result[2]
    77             self._gid, self._directory = result[0], result[3]
    75             self._transport = Transport(self._dbh, tid=result[1])
    78             self._qlimit = QuotaLimit(self._dbh, qid=result[1])
       
    79             self._transport = Transport(self._dbh, tid=result[2])
    76             self._new = False
    80             self._new = False
    77 
    81 
    78     def _set_gid(self):
    82     def _set_gid(self):
    79         """Sets the ID of the domain - if not set yet."""
    83         """Sets the ID of the domain - if not set yet."""
    80         assert self._gid == 0
    84         assert self._gid == 0
   107         database."""
   111         database."""
   108         if self._new:
   112         if self._new:
   109             raise DomErr(_(u"The domain '%s' doesn't exist.") % self._name,
   113             raise DomErr(_(u"The domain '%s' doesn't exist.") % self._name,
   110                          NO_SUCH_DOMAIN)
   114                          NO_SUCH_DOMAIN)
   111 
   115 
       
   116     def _update_tables(self, column, value, force=False):
       
   117         """Update various columns in the domain_data table. When *force* is
       
   118         `True` also the corresponding column in the users table will be
       
   119         updated.
       
   120 
       
   121         Arguments:
       
   122 
       
   123         `column` : basestring
       
   124           Name of the table column. Currently: qid and tid
       
   125         `value` : long
       
   126           The referenced key
       
   127         `force` : bool
       
   128           enforce the new setting also for existing users. Default: `False`
       
   129         """
       
   130         if column not in ('qid', 'tid'):
       
   131             raise ValueError('Unknown column: %r' % column)
       
   132         dbc = self._dbh.cursor()
       
   133         dbc.execute('UPDATE domain_data SET %s = %%s WHERE gid = %%s' % column,
       
   134                     (value, self._gid))
       
   135         if dbc.rowcount > 0:
       
   136             self._dbh.commit()
       
   137         if force:
       
   138             dbc.execute('UPDATE users SET %s = %%s WHERE gid = %%s' % column,
       
   139                         (value, self._gid))
       
   140             if dbc.rowcount > 0:
       
   141                 self._dbh.commit()
       
   142         dbc.close()
       
   143 
   112     @property
   144     @property
   113     def gid(self):
   145     def gid(self):
   114         """The GID of the Domain."""
   146         """The GID of the Domain."""
   115         return self._gid
   147         return self._gid
   116 
   148 
   121 
   153 
   122     @property
   154     @property
   123     def directory(self):
   155     def directory(self):
   124         """The Domain's directory."""
   156         """The Domain's directory."""
   125         return self._directory
   157         return self._directory
       
   158 
       
   159     @property
       
   160     def quotalimit(self):
       
   161         """The Domain's quota limit."""
       
   162         return self._qlimit
       
   163 
       
   164     @property
       
   165     def transport(self):
       
   166         """The Domain's transport."""
       
   167         return self._transport
   126 
   168 
   127     def set_directory(self, basedir):
   169     def set_directory(self, basedir):
   128         """Set the path value of the Domain's directory, inside *basedir*.
   170         """Set the path value of the Domain's directory, inside *basedir*.
   129 
   171 
   130         Argument:
   172         Argument:
   138         assert self._directory is None
   180         assert self._directory is None
   139         self._set_gid()
   181         self._set_gid()
   140         self._directory = os.path.join(basedir, choice(MAILDIR_CHARS),
   182         self._directory = os.path.join(basedir, choice(MAILDIR_CHARS),
   141                                        str(self._gid))
   183                                        str(self._gid))
   142 
   184 
   143     @property
   185     def set_quotalimit(self, quotalimit):
   144     def transport(self):
   186         """Set the quota limit for the new Domain.
   145         """The Domain's transport."""
   187 
   146         return self._transport
   188         Argument:
       
   189 
       
   190         `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit
       
   191           The quota limit of the new Domain.
       
   192         """
       
   193         if not self._new:
       
   194             raise DomErr(_(u"The domain '%s' already exists.") % self._name,
       
   195                          DOMAIN_EXISTS)
       
   196         assert isinstance(quotalimit, QuotaLimit)
       
   197         self._qlimit = quotalimit
   147 
   198 
   148     def set_transport(self, transport):
   199     def set_transport(self, transport):
   149         """Set the transport for the new Domain.
   200         """Set the transport for the new Domain.
   150 
   201 
   151         Argument:
   202         Argument:
   162     def save(self):
   213     def save(self):
   163         """Stores the new domain in the database."""
   214         """Stores the new domain in the database."""
   164         if not self._new:
   215         if not self._new:
   165             raise DomErr(_(u"The domain '%s' already exists.") % self._name,
   216             raise DomErr(_(u"The domain '%s' already exists.") % self._name,
   166                          DOMAIN_EXISTS)
   217                          DOMAIN_EXISTS)
   167         assert self._directory is not None and self._transport is not None
   218         assert all((self._directory, self._qlimit, self._transport))
   168         dbc = self._dbh.cursor()
   219         dbc = self._dbh.cursor()
   169         dbc.execute('INSERT INTO domain_data (gid, tid, domaindir) VALUES '
   220         dbc.execute('INSERT INTO domain_data (gid, qid, tid, domaindir) '
   170                     '(%s, %s, %s)', (self._gid, self._transport.tid,
   221                     'VALUES (%s, %s, %s, %s)', (self._gid, self._qlimit.qid,
   171                                      self._directory))
   222                     self._transport.tid, self._directory))
   172         dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) '
   223         dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) '
   173                     'VALUES (%s, %s, TRUE)', (self._name, self._gid))
   224                     'VALUES (%s, %s, TRUE)', (self._name, self._gid))
   174         self._dbh.commit()
   225         self._dbh.commit()
   175         dbc.close()
   226         dbc.close()
   176         self._new = False
   227         self._new = False
   196                     'domain_data'):
   247                     'domain_data'):
   197             dbc.execute("DELETE FROM %s WHERE gid = %u" % (tbl, self._gid))
   248             dbc.execute("DELETE FROM %s WHERE gid = %u" % (tbl, self._gid))
   198         self._dbh.commit()
   249         self._dbh.commit()
   199         dbc.close()
   250         dbc.close()
   200         self._gid = 0
   251         self._gid = 0
   201         self._directory = self._transport = None
   252         self._directory = self._qlimit = self._transport = None
   202         self._new = True
   253         self._new = True
       
   254 
       
   255     def update_quotalimit(self, quotalimit, force=False):
       
   256         """Update the quota limit of the Domain.
       
   257 
       
   258         If *force* is `True` the new *quotalimit* will be applied to
       
   259         all existing accounts of the domain. Otherwise the *quotalimit*
       
   260         will be only applied to accounts created from now on.
       
   261 
       
   262         Arguments:
       
   263 
       
   264         `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit
       
   265           the new quota limit of the domain.
       
   266         `force` : bool
       
   267           enforce new quota limit for all accounts, default `False`
       
   268         """
       
   269         self._chk_state()
       
   270         assert isinstance(quotalimit, QuotaLimit)
       
   271         if quotalimit == self._qlimit:
       
   272             return
       
   273         self._update_tables('qid', quotalimit.qid, force)
       
   274         self._qlimit = quotalimit
   203 
   275 
   204     def update_transport(self, transport, force=False):
   276     def update_transport(self, transport, force=False):
   205         """Sets a new transport for the Domain.
   277         """Sets a new transport for the Domain.
   206 
   278 
   207         If *force* is `True` the new *transport* will be assigned to all
   279         If *force* is `True` the new *transport* will be assigned to all
   217         """
   289         """
   218         self._chk_state()
   290         self._chk_state()
   219         assert isinstance(transport, Transport)
   291         assert isinstance(transport, Transport)
   220         if transport == self._transport:
   292         if transport == self._transport:
   221             return
   293             return
   222         dbc = self._dbh.cursor()
   294         self._update_tables('tid', transport.tid, force)
   223         dbc.execute("UPDATE domain_data SET tid = %s WHERE gid = %s",
       
   224                     (transport.tid, self._gid))
       
   225         if dbc.rowcount > 0:
       
   226             self._dbh.commit()
       
   227         if force:
       
   228             dbc.execute("UPDATE users SET tid = %s WHERE gid = %s",
       
   229                         (transport.tid, self._gid))
       
   230             if dbc.rowcount > 0:
       
   231                 self._dbh.commit()
       
   232         dbc.close()
       
   233         self._transport = transport
   295         self._transport = transport
   234 
   296 
   235     def get_info(self):
   297     def get_info(self):
   236         """Returns a dictionary with information about the domain."""
   298         """Returns a dictionary with information about the domain."""
   237         self._chk_state()
   299         self._chk_state()
   238         dbc = self._dbh.cursor()
   300         dbc = self._dbh.cursor()
   239         dbc.execute('SELECT gid, domainname, transport, domaindir, '
   301         dbc.execute('SELECT gid, domainname, transport, domaindir, '
   240                     'aliasdomains, accounts, aliases, relocated FROM '
   302                     'aliasdomains, accounts, aliases, relocated, bytes, '
   241                     'vmm_domain_info WHERE gid = %s', (self._gid,))
   303                     'messages FROM vmm_domain_info WHERE gid = %s',
       
   304                     (self._gid,))
   242         info = dbc.fetchone()
   305         info = dbc.fetchone()
   243         dbc.close()
   306         dbc.close()
   244         keys = ('gid', 'domainname', 'transport', 'domaindir', 'aliasdomains',
   307         keys = ('gid', 'domainname', 'transport', 'domaindir', 'aliasdomains',
   245                 'accounts', 'aliases', 'relocated')
   308                 'accounts', 'aliases', 'relocated', 'bytes', 'messages')
   246         return dict(zip(keys, info))
   309         return dict(zip(keys, info))
   247 
   310 
   248     def get_accounts(self):
   311     def get_accounts(self):
   249         """Returns a list with all accounts of the domain."""
   312         """Returns a list with all accounts of the domain."""
   250         self._chk_state()
   313         self._chk_state()