Make PL/pgSQL function feed back identity for mailboxes/relocated when there
are catchall destinations.
Without catchall aliases, if no virtual_alias matches, the query can just
return NULL and Postfix will later check mailboxes/relocated for the address
to rewrite.
However, since virtual aliases are handled long before mailboxes/relocated,
a catchall alias would also catch mail to mailboxes and relocated addresses,
which we do not want.
The way to tell postfix to keep delivering is for the virtual alias map to
return the search key itself (identity function).
This patch changes the postfix_virtual_alias_maps Pl/pgSQL function to do
exactly that, but only if there are catchall destinations defined for the
domain in question — otherwise it returns NULL when no match is found.
# -*- coding: UTF-8 -*-# Copyright (c) 2008 - 2011, Pascal Volk# See COPYING for distribution information.""" VirtualMailManager.emailaddress ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual Mail Manager's EmailAddress class to handle e-mail addresses."""importrefromVirtualMailManager.domainimportcheck_domainname,get_gidfromVirtualMailManager.constantsimport \DOMAIN_NO_NAME,INVALID_ADDRESS,LOCALPART_INVALID,LOCALPART_TOO_LONG, \DOMAIN_INVALIDfromVirtualMailManager.errorsimportDomainError,EmailAddressErrorasEAErrRE_LOCALPART=re.compile(r"[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]")_=lambdamsg:msgclassEmailAddress(object):"""Simple class for validated e-mail addresses."""__slots__=('_localpart','_domainname')def__init__(self,address,_validate=True):"""Creates a new instance from the string/unicode ``address``."""assertisinstance(address,basestring)self._localpart=Noneself._domainname=Noneif_validate:self._chk_address(address)@propertydeflocalpart(self):"""The local-part of the address *local-part@domain*"""returnself._localpart@propertydefdomainname(self):"""The domain part of the address *local-part@domain*"""returnself._domainnamedef__eq__(self,other):ifisinstance(other,self.__class__):returnself._localpart==other._localpartand \self._domainname==other._domainnamereturnNotImplementeddef__ne__(self,other):ifisinstance(other,self.__class__):returnself._localpart!=other._localpartor \self._domainname!=other._domainnamereturnNotImplementeddef__hash__(self):returnhash((self._localpart.lower(),self._domainname.lower()))def__repr__(self):return"EmailAddress('%s@%s')"%(self._localpart,self._domainname)def__str__(self):return'%s@%s'%(self._localpart,self._domainname)def_chk_address(self,address):"""Checks if the string ``address`` could be used for an e-mail address. If so, it will assign the corresponding values to the attributes `_localpart` and `_domainname`."""parts=address.split('@')p_len=len(parts)ifp_len<2:raiseEAErr(_(u"Missing the '@' sign in address: '%s'")%address,INVALID_ADDRESS)elifp_len>2:raiseEAErr(_(u"Too many '@' signs in address: '%s'")%address,INVALID_ADDRESS)ifnotparts[0]:raiseEAErr(_(u"Missing local-part in address: '%s'")%address,LOCALPART_INVALID)ifnotparts[1]:raiseEAErr(_(u"Missing domain name in address: '%s'")%address,DOMAIN_NO_NAME)self._localpart=check_localpart(parts[0])self._domainname=check_domainname(parts[1])classDestinationEmailAddress(EmailAddress):"""Provides additionally the domains group ID - when the domain is known in the database."""__slots__=('_gid','_localhost')def__init__(self,address,dbh,_validate=False):"""Creates a new DestinationEmailAddress instance Arguments: `address`: string/unicode a e-mail address like user@example.com `dbh`: pyPgSQL.PgSQL.Connection/pyPgSQL.PgSQL.connection a database connection for the database access """super(DestinationEmailAddress,self).__init__(address,_validate)self._localhost=Falseifnot_validate:try:self._chk_address(address)exceptDomainError,err:iferr.codeisDOMAIN_INVALIDand \address.split('@')[1]=='localhost':self._localhost=Trueself._domainname='localhost'else:raiseself._gid=0ifnotself._localhost:self._find_domain(dbh)else:self._localpart=self._localpart.lower()def_find_domain(self,dbh):"""Checks if the domain is known"""self._gid=get_gid(dbh,self._domainname)ifself._gid:self._localpart=self._localpart.lower()@propertydefat_localhost(self):"""True when the address is something@localhost."""returnself._localhost@propertydefgid(self):"""The domains group ID. 0 if the domain is not known."""returnself._giddefcheck_localpart(localpart):"""Returns the validated local-part `localpart`. Throws a `EmailAddressError` if the local-part is too long or contains invalid characters. """iflen(localpart)>64:raiseEAErr(_(u"The local-part '%s' is too long.")%localpart,LOCALPART_TOO_LONG)invalid_chars=set(RE_LOCALPART.findall(localpart))ifinvalid_chars:i_chars=u''.join((u'"%s" '%cforcininvalid_chars))raiseEAErr(_(u"The local-part '%(l_part)s' contains invalid "u"characters: %(i_chars)s")%{'l_part':localpart,'i_chars':i_chars},LOCALPART_INVALID)returnlocalpartdel_