VMM: added 'Configuration' variable and set_configuration().
Handler.__init__() now exports its config via set_configuration().
# -*- coding: UTF-8 -*-# Copyright (c) 2007 - 2010, Pascal Volk# See COPYING for distribution information.""" VirtualMailManager.Account Virtual Mail Manager's Account class to manage e-mail accounts."""fromVirtualMailManager.DomainimportDomainfromVirtualMailManager.EmailAddressimportEmailAddressfromVirtualMailManager.TransportimportTransportfromVirtualMailManager.constants.ERRORimport \ACCOUNT_EXISTS,ACCOUNT_MISSING_PASSWORD,ALIAS_PRESENT, \INVALID_AGUMENT,NO_SUCH_ACCOUNT,NO_SUCH_DOMAIN, \UNKNOWN_MAILLOCATION_NAME,UNKNOWN_SERVICEfromVirtualMailManager.errorsimportAccountErrorasAErrfromVirtualMailManager.maillocationimportMailLocation,known_formatfromVirtualMailManager.pycompatimportall_=lambdamsg:msgclassAccount(object):"""Class to manage e-mail accounts."""__slots__=('_addr','_domain','_mid','_new','_passwd','_tid','_uid','_dbh')def__init__(self,dbh,address):"""Creates a new Account instance. When an account with the given *address* could be found in the database all relevant data will be loaded. Arguments: `dbh` : pyPgSQL.PgSQL.Connection A database connection for the database access. `address` : basestring The e-mail address of the (new) Account. """ifnotisinstance(address,EmailAddress):raiseTypeError("Argument 'address' is not an EmailAddress")self._addr=addressself._dbh=dbhself._domain=Domain(self._dbh,self._addr.domainname)ifnotself._domain.gid:raiseAErr(_(u"The domain '%s' doesn't exist.")%self._addr.domainname,NO_SUCH_DOMAIN)self._uid=0self._mid=0self._tid=0self._passwd=Noneself._new=Trueself._load()def_load(self):"""Load 'uid', 'mid' and 'tid' from the database and set _new to `False` - if the user could be found. """dbc=self._dbh.cursor()dbc.execute("SELECT uid, mid, tid FROM users WHERE gid=%s AND local_part=%s",self._domain.gid,self._addr.localpart)result=dbc.fetchone()dbc.close()ifresult:self._uid,self._mid,self._tid=resultself._new=Falsedef_set_uid(self):"""Set the unique ID for the new Account."""assertself._uid==0dbc=self._dbh.cursor()dbc.execute("SELECT nextval('users_uid')")self._uid=dbc.fetchone()[0]dbc.close()def_prepare(self,maillocation):"""Check and set different attributes - before we store the information in the database."""ifnotknown_format(maillocation):raiseAErr(_(u'Unknown mail_location mailbox format: %r')%maillocation,UNKNOWN_MAILLOCATION_NAME)self._mid=MailLocation(format=maillocation).midifnotself._tid:self._tid=self._domain.transport.tidself._set_uid()def_switch_state(self,state,dcvers,service):"""Switch the state of the Account's services on or off. See Account.enable()/Account.disable() for more information."""self._chk_state()ifservicenotin(None,'all','imap','pop3','sieve','smtp'):raiseAErr(_(u"Unknown service: '%s'.")%service,UNKNOWN_SERVICE)ifdcvers>11:sieve_col='sieve'else:sieve_col='managesieve'ifservicein('smtp','pop3','imap'):sql='UPDATE users SET %s = %s WHERE uid = %d'%(service,state,self._uid)elifservice=='sieve':sql='UPDATE users SET %s = %s WHERE uid = %d'%(sieve_col,state,self._uid)else:sql='UPDATE users SET smtp = %(s)s, pop3 = %(s)s, imap = %(s)s,\%(col)s = %(s)s WHERE uid = %(uid)d'% \{'s':state,'col':sieve_col,'uid':self._uid}dbc=self._dbh.cursor()dbc.execute(sql)ifdbc.rowcount>0:self._dbh.commit()dbc.close()def_count_aliases(self):"""Count all alias addresses where the destination address is the address of the Account."""dbc=self._dbh.cursor()sql="SELECT COUNT(destination) FROM alias WHERE destination = '%s'"\%self._addrdbc.execute(sql)a_count=dbc.fetchone()[0]dbc.close()returna_countdef_chk_state(self):"""Raise an AccountError if the Account is new - not yet saved in the database."""ifself._new:raiseAErr(_(u"The account '%s' doesn't exist.")%self._addr,NO_SUCH_ACCOUNT)@propertydefaddress(self):"""The Account's EmailAddress instance."""returnself._addr@propertydefdomain_directory(self):"""The directory of the domain the Account belongs to."""returnself._domain.directory@propertydefgid(self):"""The Account's group ID."""returnself._domain.gid@propertydefhome(self):"""The Account's home directory."""return'%s/%s'%(self._domain.directory,self._uid)@propertydefuid(self):"""The Account's unique ID."""returnself._uiddefset_password(self,password):"""Set a password for the new Account. If you want to update the password of an existing Account use Account.modify(). Argument: `password` : basestring The hashed password for the new Account."""self._passwd=passworddefset_transport(self,transport):"""Set the transport for the new Account. If you want to update the transport of an existing Account use Account.modify(). Argument: `transport` : basestring The string representation of the transport, e.g.: 'dovecot:' """self._tid=Transport(self._dbh,transport=transport).tiddefenable(self,dcvers,service=None):"""Enable a/all service/s for the Account. Possible values for the *service* are: 'imap', 'pop3', 'sieve' and 'smtp'. When all services should be enabled, use 'all' or the default value `None`. Arguments: `dcvers` : int The concatenated major and minor version number from `dovecot --version`. `service` : basestring The name of a service ('imap', 'pop3', 'smtp', 'sieve'), 'all' or `None`. """self._switch_state(True,dcvers,service)defdisable(self,dcvers,service=None):"""Disable a/all service/s for the Account. For more information see: Account.enable()."""self._switch_state(False,dcvers,service)defsave(self,maillocation,dcvers,smtp,pop3,imap,sieve):"""Save the new Account in the database. Arguments: `maillocation` : basestring The mailbox format of the mail_location: 'maildir', 'mbox', 'dbox' or 'mdbox'. `dcvers` : int The concatenated major and minor version number from `dovecot --version`. `smtp, pop3, imap, sieve` : bool Indicates if the user of the Account should be able to use this services. """ifnotself._new:raiseAErr(_(u"The account '%s' already exists.")%self._addr,ACCOUNT_EXISTS)ifnotself._passwd:raiseAErr(_(u"No password set for '%s'.")%self._addr,ACCOUNT_MISSING_PASSWORD)assertall(isinstance(service,bool)forservicein(smtp,pop3,imap,sieve))ifdcvers>11:sieve_col='sieve'else:sieve_col='managesieve'self._prepare(maillocation)sql="INSERT INTO users (local_part, passwd, uid, gid, mid, tid,\ smtp, pop3, imap, %s) VALUES ('%s', '%s', %d, %d, %d, %d, %s, %s, %s, %s)"%(sieve_col,self._addr.localpart,self._passwd,self._uid,self._domain.gid,self._mid,self._tid,smtp,pop3,imap,sieve)dbc=self._dbh.cursor()dbc.execute(sql)self._dbh.commit()dbc.close()self._new=Falsedefmodify(self,field,value):"""Update the Account's *field* to the new *value*. Possible values for *filed* are: 'name', 'password' and 'transport'. *value* is the *field*'s new value. Arguments: `field` : basestring The attribute name: 'name', 'password' or 'transport' `value` : basestring The new value of the attribute. The password is expected as a hashed password string. """iffieldnotin('name','password','transport'):raiseAErr(_(u"Unknown field: '%s'")%field,INVALID_AGUMENT)self._chk_state()dbc=self._dbh.cursor()iffield=='password':dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s',value,self._uid)eliffield=='transport':self._tid=Transport(self._dbh,transport=value).tiddbc.execute('UPDATE users SET tid = %s WHERE uid = %s',self._tid,self._uid)else:dbc.execute('UPDATE users SET name = %s WHERE uid = %s',value,self._uid)ifdbc.rowcount>0:self._dbh.commit()dbc.close()defget_info(self,dcvers):"""Returns a dict with some information about the Account. The keys of the dict are: 'address', 'gid', 'home', 'imap' 'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport' and 'uid'. Argument: `dcvers` : int The concatenated major and minor version number from `dovecot --version`. """self._chk_state()ifdcvers>11:sieve_col='sieve'else:sieve_col='managesieve'sql='SELECT name, uid, gid, mid, tid, smtp, pop3, imap, %s\ FROM users WHERE uid = %d'%(sieve_col,self._uid)dbc=self._dbh.cursor()dbc.execute(sql)info=dbc.fetchone()dbc.close()ifinfo:keys=('name','uid','gid','mid','transport','smtp','pop3','imap',sieve_col)info=dict(zip(keys,info))forservicein('smtp','pop3','imap',sieve_col):ifinfo[service]:# TP: A service (pop3/imap) is enabled/usable for a userinfo[service]=_('enabled')else:# TP: A service (pop3/imap) isn't enabled/usable for a userinfo[service]=_('disabled')info['address']=self._addrinfo['home']='%s/%s'%(self._domain.directory,info['uid'])info['mail_location']=MailLocation(mid=info['mid']).mail_locationifinfo['transport']==self._domain.transport.tid:info['transport']=self._domain.transport.transportelse:info['transport']=Transport(self._dbh,tid=info['transport']).transportdelinfo['mid']returninfo# nearly impossible‽raiseAErr(_(u"Couldn't fetch information for account: '%s'") \%self._addr,NO_SUCH_ACCOUNT)defget_aliases(self):"""Return a list with all alias e-mail addresses, whose destination is the address of the Account."""self._chk_state()dbc=self._dbh.cursor()dbc.execute("SELECT address ||'@'|| domainname FROM alias, domain_name\ WHERE destination = %s AND domain_name.gid = alias.gid\ AND domain_name.is_primary ORDER BY address",str(self._addr))addresses=dbc.fetchall()dbc.close()aliases=[]ifaddresses:aliases=[alias[0]foraliasinaddresses]returnaliasesdefdelete(self,delalias):"""Delete the Account from the database. Argument: `delalias` : basestring if the values of delalias is 'delalias', all aliases, which points to the Account, will be also deleted."""self._chk_state()dbc=self._dbh.cursor()ifdelalias=='delalias':dbc.execute('DELETE FROM users WHERE uid= %s',self._uid)# delete also all aliases where the destination address is the same# as for this account.dbc.execute("DELETE FROM alias WHERE destination = %s",str(self._addr))self._dbh.commit()else:# check first for aliasesa_count=self._count_aliases()ifa_count==0:dbc.execute('DELETE FROM users WHERE uid = %s',self._uid)self._dbh.commit()else:dbc.close()raiseAErr(_(u"There are %(count)d aliases with the \destination address '%(address)s'.")% \{'count':a_count,'address':self._addr},ALIAS_PRESENT)dbc.close()defget_account_by_uid(uid,dbh):"""Search an Account by its UID. This function returns a dict (keys: 'address', 'gid' and 'uid'), if an Account with the given *uid* exists. Argument: `uid` : long The Account unique ID. `dbh` : pyPgSQL.PgSQL.Connection a database connection for the database access. """try:uid=long(uid)exceptValueError:raiseAErr(_(u'UID must be an int/long.'),INVALID_AGUMENT)ifuid<1:raiseAErr(_(u'UID must be greater than 0.'),INVALID_AGUMENT)dbc=dbh.cursor()dbc.execute("SELECT local_part||'@'|| domain_name.domainname AS address,\ uid, users.gid FROM users LEFT JOIN domain_name ON (domain_name.gid \ = users.gid AND is_primary) WHERE uid = %s;",uid)info=dbc.fetchone()dbc.close()ifnotinfo:raiseAErr(_(u"There is no account with the UID '%d'.")%uid,NO_SUCH_ACCOUNT)info=dict(zip(('address','uid','gid'),info))returninfodel_