# -*- coding: UTF-8 -*-# Copyright (c) 2007 - 2010, Pascal Volk# See COPYING for distribution information."""Virtual Mail Manager's Account class to manage e-mail accounts."""importVirtualMailManager.constants.ERRORasERRfromVirtualMailManager.DomainimportDomainfromVirtualMailManager.EmailAddressimportEmailAddressfromVirtualMailManager.ExceptionsimportVMMAccountExceptionasAccEfromVirtualMailManager.MailLocationimportMailLocationfromVirtualMailManager.TransportimportTransportimportVirtualMailManagerasVMMclassAccount(object):"""Class to manage e-mail accounts."""__slots__=('_addr','_base','_gid','_mid','_passwd','_tid','_uid','_dbh')def__init__(self,dbh,address,password=None):self._dbh=dbhself._base=Noneifisinstance(address,EmailAddress):self._addr=addresselse:raiseTypeError("Argument 'address' is not an EmailAddress")self._uid=0self._gid=0self._mid=0self._tid=0self._passwd=passwordself._setAddr()self._exists()ifself._uid<1andVMM.VirtualMailManager.aliasExists(self._dbh,self._addr):# TP: Hm, what quotation marks should be used?# If you are unsure have a look at:# http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usageraiseAccE(_(u"There is already an alias with the address “%s”.")%\self._addr,ERR.ALIAS_EXISTS)ifself._uid<1andVMM.VirtualMailManager.relocatedExists(self._dbh,self._addr):raiseAccE(_(u"There is already a relocated user with the address “%s”.")%\self._addr,ERR.RELOCATED_EXISTS)def_exists(self):dbc=self._dbh.cursor()dbc.execute("SELECT uid, mid, tid FROM users \WHERE gid=%s AND local_part=%s",self._gid,self._addr._localpart)result=dbc.fetchone()dbc.close()ifresultisnotNone:self._uid,self._mid,self._tid=resultreturnTrueelse:returnFalsedef_setAddr(self):dom=Domain(self._dbh,self._addr._domainname)self._gid=dom.getID()ifself._gid==0:raiseAccE(_(u"The domain “%s” doesn't exist.")%\self._addr._domainname,ERR.NO_SUCH_DOMAIN)self._base=dom.getDir()self._tid=dom.getTransportID()def_setID(self):dbc=self._dbh.cursor()dbc.execute("SELECT nextval('users_uid')")self._uid=dbc.fetchone()[0]dbc.close()def_prepare(self,maillocation):self._setID()self._mid=MailLocation(self._dbh,maillocation=maillocation).getID()def_switchState(self,state,dcvers,service):ifnotisinstance(state,bool):returnFalseifnotservicein(None,'all','imap','pop3','sieve','smtp'):raiseAccE(_(u"Unknown service “%s”.")%service,ERR.UNKNOWN_SERVICE)ifself._uid<1:raiseAccE(_(u"The account “%s” doesn't exist.")%self._addr,ERR.NO_SUCH_ACCOUNT)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__aliaseCount(self):dbc=self._dbh.cursor()q="SELECT COUNT(destination) FROM alias WHERE destination = '%s'"\%self._addrdbc.execute(q)a_count=dbc.fetchone()[0]dbc.close()returna_countdefsetPassword(self,password):self._passwd=passworddefgetUID(self):returnself._uiddefgetGID(self):returnself._giddefgetDir(self,directory):ifdirectory=='domain':return'%s'%self._baseelifdirectory=='home':return'%s/%i'%(self._base,self._uid)defenable(self,dcvers,service=None):self._switchState(True,dcvers,service)defdisable(self,dcvers,service=None):self._switchState(False,dcvers,service)defsave(self,maillocation,dcvers,smtp,pop3,imap,sieve):ifself._uid<1: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._gid,self._mid,self._tid,smtp,pop3,imap,sieve)dbc=self._dbh.cursor()dbc.execute(sql)self._dbh.commit()dbc.close()else:raiseAccE(_(u'The account “%s” already exists.')%self._addr,ERR.ACCOUNT_EXISTS)defmodify(self,what,value):ifself._uid==0:raiseAccE(_(u"The account “%s” doesn't exist.")%self._addr,ERR.NO_SUCH_ACCOUNT)ifwhatnotin['name','password','transport']:returnFalsedbc=self._dbh.cursor()ifwhat=='password':dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s',value,self._uid)elifwhat=='transport':self._tid=Transport(self._dbh,transport=value).getID()dbc.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()defgetInfo(self,dcvers):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()ifinfoisNone:raiseAccE(_(u"The account “%s” doesn't exist.")%self._addr,ERR.NO_SUCH_ACCOUNT)else:keys=['name','uid','gid','maildir','transport','smtp','pop3','imap',sieve_col]info=dict(zip(keys,info))forservicein('smtp','pop3','imap',sieve_col):ifbool(info[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['maildir']='%s/%s/%s'%(self._base,info['uid'],MailLocation(self._dbh,mid=info['maildir']).getMailLocation())info['transport']=Transport(self._dbh,tid=info['transport']).getTransport()returninfodefgetAliases(self):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=[]iflen(addresses)>0:aliases=[alias[0]foraliasinaddresses]returnaliasesdefdelete(self,delalias):ifself._uid<1:raiseAccE(_(u"The account “%s” doesn't exist.")%self._addr,ERR.NO_SUCH_ACCOUNT)dbc=self._dbh.cursor()ifdelalias=='delalias':dbc.execute('DELETE FROM users WHERE uid= %s',self._uid)u_rc=dbc.rowcount# 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))ifu_rc>0ordbc.rowcount>0:self._dbh.commit()else:# check first for aliasesa_count=self.__aliaseCount()ifa_count==0:dbc.execute('DELETE FROM users WHERE uid = %s',self._uid)ifdbc.rowcount>0:self._dbh.commit()else:dbc.close()raiseAccE(_(u"There are %(count)d aliases with the destination address\ “%(address)s”.")%{'count':a_count,'address':self._addr},ERR.ALIAS_PRESENT)dbc.close()defgetAccountByID(uid,dbh):try:uid=long(uid)exceptValueError:raiseAccE(_(u'uid must be an int/long.'),ERR.INVALID_AGUMENT)ifuid<1:raiseAccE(_(u'uid must be greater than 0.'),ERR.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()ifinfoisNone:raiseAccE(_(u"There is no account with the UID “%d”.")%uid,ERR.NO_SUCH_ACCOUNT)keys=['address','uid','gid']info=dict(zip(keys,info))returninfo