VirtualMailManager/account.py
branchv0.6.x
changeset 528 4b8c3f51d7da
parent 527 e09139525580
child 531 cf3eb03c1c4f
equal deleted inserted replaced
527:e09139525580 528:4b8c3f51d7da
     6     ~~~~~~~~~~~~~~~~~~~~~~~~~~
     6     ~~~~~~~~~~~~~~~~~~~~~~~~~~
     7 
     7 
     8     Virtual Mail Manager's Account class to manage e-mail accounts.
     8     Virtual Mail Manager's Account class to manage e-mail accounts.
     9 """
     9 """
    10 
    10 
    11 from VirtualMailManager.common import version_str, \
    11 from VirtualMailManager.common import version_str
    12      format_domain_default
       
    13 from VirtualMailManager.constants import \
    12 from VirtualMailManager.constants import \
    14      ACCOUNT_EXISTS, ACCOUNT_MISSING_PASSWORD, ALIAS_PRESENT, \
    13      ACCOUNT_EXISTS, ACCOUNT_MISSING_PASSWORD, ALIAS_PRESENT, \
    15      INVALID_ARGUMENT, INVALID_MAIL_LOCATION, NO_SUCH_ACCOUNT, \
    14      INVALID_ARGUMENT, INVALID_MAIL_LOCATION, NO_SUCH_ACCOUNT, \
    16      NO_SUCH_DOMAIN, VMM_ERROR
    15      NO_SUCH_DOMAIN, VMM_ERROR
    17 from VirtualMailManager.domain import Domain
    16 from VirtualMailManager.domain import Domain
    58             # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
    57             # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
    59             raise AErr(_(u"The domain '%s' does not exist.") %
    58             raise AErr(_(u"The domain '%s' does not exist.") %
    60                        self._addr.domainname, NO_SUCH_DOMAIN)
    59                        self._addr.domainname, NO_SUCH_DOMAIN)
    61         self._uid = 0
    60         self._uid = 0
    62         self._mail = None
    61         self._mail = None
    63         self._qlimit = None
    62         self._qlimit = self._domain.quotalimit
    64         self._services = None
    63         self._services = self._domain.serviceset
    65         self._transport = None
    64         self._transport = self._domain.transport
    66         self._passwd = None
    65         self._passwd = None
    67         self._new = True
    66         self._new = True
    68         self._load()
    67         self._load()
    69 
    68 
    70     def __nonzero__(self):
    69     def __nonzero__(self):
    80                                                self._addr.localpart))
    79                                                self._addr.localpart))
    81         result = dbc.fetchone()
    80         result = dbc.fetchone()
    82         dbc.close()
    81         dbc.close()
    83         if result:
    82         if result:
    84             self._uid, _mid, _qid, _ssid, _tid = result
    83             self._uid, _mid, _qid, _ssid, _tid = result
    85 
    84             if _qid != self._qlimit.qid:
    86             def load_helper(ctor, own, field, dbresult):
    85                 self._qlimit = QuotaLimit(self._dbh, qid=_qid)
    87                 cur = None if own is None else getattr(own, field)
    86             if _ssid != self._services.ssid:
    88                 if cur != dbresult:
    87                 self._services = ServiceSet(self._dbh, ssid=_ssid)
    89                     kwargs = { field : dbresult }
    88             if _tid != self._transport.tid:
    90                     return None if dbresult is None \
    89                 self._transport = Transport(self._dbh, tid=_tid)
    91                                 else ctor(self._dbh, **kwargs)
       
    92 
       
    93             self._qlimit = load_helper(QuotaLimit, self._qlimit, 'qid', _qid)
       
    94             self._services = load_helper(ServiceSet, self._services, 'ssid',
       
    95                                          _ssid)
       
    96             self._transport = load_helper(Transport, self._transport, 'tid',
       
    97                                           _tid)
       
    98             self._mail = MailLocation(self._dbh, mid=_mid)
    90             self._mail = MailLocation(self._dbh, mid=_mid)
    99             self._new = False
    91             self._new = False
   100 
    92 
   101     def _set_uid(self):
    93     def _set_uid(self):
   102         """Set the unique ID for the new Account."""
    94         """Set the unique ID for the new Account."""
   229         dbc = self._dbh.cursor()
   221         dbc = self._dbh.cursor()
   230         dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, '
   222         dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, '
   231                     'qid, ssid, tid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
   223                     'qid, ssid, tid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
   232                     (self._addr.localpart,
   224                     (self._addr.localpart,
   233                      pwhash(self._passwd, user=self._addr), self._uid,
   225                      pwhash(self._passwd, user=self._addr), self._uid,
   234                      self._domain.gid, self._mail.mid,
   226                      self._domain.gid, self._mail.mid, self._qlimit.qid,
   235                      self._qlimit.qid if self._qlimit else None,
   227                      self._services.ssid, self._transport.tid))
   236                      self._services.ssid if self._services else None,
       
   237                      self._transport.tid if self._transport else None))
       
   238         self._dbh.commit()
   228         self._dbh.commit()
   239         dbc.close()
   229         dbc.close()
   240         self._new = False
   230         self._new = False
   241 
   231 
   242     def modify(self, field, value):
   232     def modify(self, field, value):
   275         """
   265         """
   276         if cfg_dget('misc.dovecot_version') < 0x10102f00:
   266         if cfg_dget('misc.dovecot_version') < 0x10102f00:
   277             raise VMMError(_(u'PostgreSQL-based dictionary quota requires '
   267             raise VMMError(_(u'PostgreSQL-based dictionary quota requires '
   278                              u'Dovecot >= v1.1.2.'), VMM_ERROR)
   268                              u'Dovecot >= v1.1.2.'), VMM_ERROR)
   279         self._chk_state()
   269         self._chk_state()
       
   270         assert isinstance(quotalimit, QuotaLimit)
   280         if quotalimit == self._qlimit:
   271         if quotalimit == self._qlimit:
   281             return
   272             return
       
   273         self._update_tables('qid', quotalimit.qid)
   282         self._qlimit = quotalimit
   274         self._qlimit = quotalimit
   283         if quotalimit is not None:
       
   284             assert isinstance(quotalimit, QuotaLimit)
       
   285             quotalimit = quotalimit.qid
       
   286         self._update_tables('qid', quotalimit)
       
   287 
   275 
   288     def update_serviceset(self, serviceset):
   276     def update_serviceset(self, serviceset):
   289         """Assign a different set of services to the Account.
   277         """Assign a different set of services to the Account.
   290 
   278 
   291         Argument:
   279         Argument:
   292 
   280 
   293         `serviceset` : VirtualMailManager.serviceset.ServiceSet
   281         `serviceset` : VirtualMailManager.serviceset.ServiceSet
   294           the new service set.
   282           the new service set.
   295         """
   283         """
   296         self._chk_state()
   284         self._chk_state()
       
   285         assert isinstance(serviceset, ServiceSet)
   297         if serviceset == self._services:
   286         if serviceset == self._services:
   298             return
   287             return
       
   288         self._update_tables('ssid', serviceset.ssid)
   299         self._services = serviceset
   289         self._services = serviceset
   300         if serviceset is not None:
       
   301             assert isinstance(serviceset, ServiceSet)
       
   302             serviceset = serviceset.ssid
       
   303         self._update_tables('ssid', serviceset)
       
   304 
   290 
   305     def update_transport(self, transport):
   291     def update_transport(self, transport):
   306         """Sets a new transport for the Account.
   292         """Sets a new transport for the Account.
   307 
   293 
   308         Arguments:
   294         Arguments:
   309 
   295 
   310         `transport` : VirtualMailManager.transport.Transport
   296         `transport` : VirtualMailManager.transport.Transport
   311           the new transport
   297           the new transport
   312         """
   298         """
   313         self._chk_state()
   299         self._chk_state()
       
   300         assert isinstance(transport, Transport)
   314         if transport == self._transport:
   301         if transport == self._transport:
   315             return
   302             return
       
   303         if transport.transport.lower() in ('virtual', 'virtual:') and \
       
   304            not self._mail.postfix:
       
   305             raise AErr(_(u"Invalid transport '%(transport)s' for mailbox "
       
   306                          u"format '%(mbfmt)s'.") %
       
   307                        {'transport': transport, 'mbfmt': self._mail.mbformat},
       
   308                        INVALID_MAIL_LOCATION)
       
   309         self._update_tables('tid', transport.tid)
   316         self._transport = transport
   310         self._transport = transport
   317         if transport is not None:
       
   318             assert isinstance(transport, Transport)
       
   319             if transport.transport.lower() in ('virtual', 'virtual:') and \
       
   320                 not self._mail.postfix:
       
   321                 raise AErr(_(u"Invalid transport '%(transport)s' for mailbox "
       
   322                              u"format '%(mbfmt)s'.") %
       
   323                            {'transport': transport, 'mbfmt': self._mail.mbformat},
       
   324                            INVALID_MAIL_LOCATION)
       
   325             transport = transport.tid
       
   326         self._update_tables('tid', transport)
       
   327 
       
   328     def _get_info_transport(self):
       
   329         if self._transport:
       
   330             return self._transport.transport
       
   331         return format_domain_default(self._domain.transport.transport)
       
   332 
       
   333     def _get_info_serviceset(self):
       
   334         if self._services:
       
   335             services = self._services.services
       
   336             fmt = lambda s: s
       
   337         else:
       
   338             services = self._domain.serviceset.services
       
   339             fmt = format_domain_default
       
   340 
       
   341         ret = {}
       
   342         for service, state in services.iteritems():
       
   343             # TP: A service (e.g. pop3 or imap) may be enabled/usable or
       
   344             # disabled/unusable for a user.
       
   345             ret[service] = fmt((_('disabled'), _('enabled'))[state])
       
   346         return ret
       
   347 
   311 
   348     def get_info(self):
   312     def get_info(self):
   349         """Returns a dict with some information about the Account.
   313         """Returns a dict with some information about the Account.
   350 
   314 
   351         The keys of the dict are: 'address', 'gid', 'home', 'imap'
   315         The keys of the dict are: 'address', 'gid', 'home', 'imap'
   352         'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport', 'uid',
   316         'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport', 'uid',
   353         'uq_bytes', 'uq_messages', 'ql_bytes', 'ql_messages', and
   317         'uq_bytes', 'uq_messages', 'ql_bytes', and 'ql_messages'.
   354         'ql_domaindefault'.
       
   355         """
   318         """
   356         self._chk_state()
   319         self._chk_state()
   357         dbc = self._dbh.cursor()
   320         dbc = self._dbh.cursor()
   358         dbc.execute('SELECT name, CASE WHEN bytes IS NULL THEN 0 ELSE bytes '
   321         dbc.execute('SELECT name, CASE WHEN bytes IS NULL THEN 0 ELSE bytes '
   359                     'END, CASE WHEN messages IS NULL THEN 0 ELSE messages END '
   322                     'END, CASE WHEN messages IS NULL THEN 0 ELSE messages END '
   361                     'users.uid = %s', (self._uid,))
   324                     'users.uid = %s', (self._uid,))
   362         info = dbc.fetchone()
   325         info = dbc.fetchone()
   363         dbc.close()
   326         dbc.close()
   364         if info:
   327         if info:
   365             info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info))
   328             info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info))
   366             info.update(self._get_info_serviceset())
   329             for service, state in self._services.services.iteritems():
       
   330                 # TP: A service (e.g. pop3 or imap) may be enabled/usable or
       
   331                 # disabled/unusable for a user.
       
   332                 info[service] = (_('disabled'), _('enabled'))[state]
   367             info['address'] = self._addr
   333             info['address'] = self._addr
   368             info['gid'] = self._domain.gid
   334             info['gid'] = self._domain.gid
   369             info['home'] = '%s/%s' % (self._domain.directory, self._uid)
   335             info['home'] = '%s/%s' % (self._domain.directory, self._uid)
   370             info['mail_location'] = self._mail.mail_location
   336             info['mail_location'] = self._mail.mail_location
   371             if self._qlimit:
   337             info['ql_bytes'] = self._qlimit.bytes
   372                 info['ql_bytes'] = self._qlimit.bytes
   338             info['ql_messages'] = self._qlimit.messages
   373                 info['ql_messages'] = self._qlimit.messages
   339             info['transport'] = self._transport.transport
   374                 info['ql_domaindefault'] = False
       
   375             else:
       
   376                 info['ql_bytes'] = self._domain.quotalimit.bytes
       
   377                 info['ql_messages'] = self._domain.quotalimit.messages
       
   378                 info['ql_domaindefault'] = True
       
   379             info['transport'] = self._get_info_transport()
       
   380             info['uid'] = self._uid
   340             info['uid'] = self._uid
   381             return info
   341             return info
   382         # nearly impossibleā€½
   342         # nearly impossibleā€½
   383         raise AErr(_(u"Could not fetch information for account: '%s'") %
   343         raise AErr(_(u"Could not fetch information for account: '%s'") %
   384                    self._addr, NO_SUCH_ACCOUNT)
   344                    self._addr, NO_SUCH_ACCOUNT)