# HG changeset patch # User Pascal Volk # Date 1265857691 0 # Node ID 0684790fff7ce4a8e813d4e3a18f65d7e99807f3 # Parent 02d467e4fbab45d5a80021273189d73234384abc VMM: renamed function chk_domainname() -> check_domainname(). Moved EmailAddress.check_localpart() -> VirtualMailManager.check_localpart(). Some small code cleanups in class EmailAddress. diff -r 02d467e4fbab -r 0684790fff7c VirtualMailManager/AliasDomain.py --- a/VirtualMailManager/AliasDomain.py Wed Feb 10 08:55:51 2010 +0000 +++ b/VirtualMailManager/AliasDomain.py Thu Feb 11 03:08:11 2010 +0000 @@ -5,7 +5,7 @@ """Virtual Mail Manager's AliasDomain class to manage alias domains.""" import VirtualMailManager.constants.ERROR as ERR -from VirtualMailManager import chk_domainname +from VirtualMailManager import check_domainname from VirtualMailManager.Exceptions import VMMAliasDomainException as VADE class AliasDomain(object): @@ -13,7 +13,7 @@ __slots__ = ('__gid', '__name', '_domain', '_dbh') def __init__(self, dbh, domainname, targetDomain=None): self._dbh = dbh - self.__name = chk_domainname(domainname) + self.__name = check_domainname(domainname) self.__gid = 0 self._domain = targetDomain self._exists() diff -r 02d467e4fbab -r 0684790fff7c VirtualMailManager/Domain.py --- a/VirtualMailManager/Domain.py Wed Feb 10 08:55:51 2010 +0000 +++ b/VirtualMailManager/Domain.py Thu Feb 11 03:08:11 2010 +0000 @@ -6,7 +6,7 @@ from random import choice -from VirtualMailManager import chk_domainname +from VirtualMailManager import check_domainname from VirtualMailManager.constants.ERROR import \ ACCOUNT_AND_ALIAS_PRESENT, ACCOUNT_PRESENT, ALIAS_PRESENT, \ DOMAIN_ALIAS_EXISTS, DOMAIN_EXISTS, NO_SUCH_DOMAIN @@ -29,7 +29,7 @@ transport -- default vmm.cfg/misc/transport (str) """ self._dbh = dbh - self._name = chk_domainname(domainname) + self._name = check_domainname(domainname) self._basedir = basedir if transport is not None: self._transport = Transport(self._dbh, transport=transport) @@ -281,7 +281,7 @@ def search(dbh, pattern=None, like=False): if pattern is not None and like is False: - pattern = chk_domainname(pattern) + pattern = check_domainname(pattern) sql = 'SELECT gid, domainname, is_primary FROM domain_name' if pattern is None: pass @@ -317,7 +317,7 @@ Raises an `VMMDomainException` if the domain does not exist. """ - domainname = chk_domainname(domainname) + domainname = check_domainname(domainname) dbc = dbh.cursor() dbc.execute('SELECT gid FROM domain_name WHERE domainname=%s', domainname) gid = dbc.fetchone() diff -r 02d467e4fbab -r 0684790fff7c VirtualMailManager/EmailAddress.py --- a/VirtualMailManager/EmailAddress.py Wed Feb 10 08:55:51 2010 +0000 +++ b/VirtualMailManager/EmailAddress.py Thu Feb 11 03:08:11 2010 +0000 @@ -8,15 +8,14 @@ Virtual Mail Manager's EmailAddress class to handle e-mail addresses. """ -import re - -from VirtualMailManager import chk_domainname +from VirtualMailManager import check_domainname, check_localpart from VirtualMailManager.constants.ERROR import \ - DOMAIN_NO_NAME, INVALID_ADDRESS, LOCALPART_INVALID, LOCALPART_TOO_LONG + DOMAIN_NO_NAME, INVALID_ADDRESS, LOCALPART_INVALID from VirtualMailManager.Exceptions import VMMEmailAddressException as VMMEAE RE_LOCALPART = """[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]""" +_ = lambda msg: msg class EmailAddress(object): @@ -58,48 +57,28 @@ return "EmailAddress('%s@%s')" % (self._localpart, self._domainname) def __str__(self): - return "%s@%s" % (self._localpart, self._domainname) + 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.""" + address. If so, it will assign the corresponding values to the + attributes `_localpart` and `_domainname`.""" parts = address.split('@') p_len = len(parts) - if p_len is 2: - self._localpart = check_localpart(parts[0]) - if len(parts[1]) > 0: - self._domainname = chk_domainname(parts[1]) - else: - raise VMMEAE(_(u"Missing domain name after “%s@”.") % - self._localpart, DOMAIN_NO_NAME) - elif p_len < 2: - raise VMMEAE(_(u"Missing '@' sign in e-mail address “%s”.") % - address, INVALID_ADDRESS) + if p_len < 2: + raise VMMEAE(_(u"Missing the '@' sign in address %r") % address, + INVALID_ADDRESS) elif p_len > 2: - raise VMMEAE(_(u"Too many '@' signs in e-mail address “%s”.") % - address, INVALID_ADDRESS) - - -_ = lambda msg: msg + raise VMMEAE(_(u"Too many '@' signs in address %r") % address, + INVALID_ADDRESS) + if not parts[0]: + raise VMMEAE(_(u"Missing local-part in address %r") % address, + LOCALPART_INVALID) + if not parts[1]: + raise VMMEAE(_(u"Missing domain name in address %r") % address, + DOMAIN_NO_NAME) + self._localpart = check_localpart(parts[0]) + self._domainname = check_domainname(parts[1]) -def check_localpart(localpart): - """Validates the local-part of an e-mail address. - - Argument: - localpart -- local-part of the e-mail address that should be validated - """ - if len(localpart) < 1: - raise VMMEAE(_(u'No local-part specified.'), LOCALPART_INVALID) - if len(localpart) > 64: - raise VMMEAE(_(u'The local-part “%s” is too long') % localpart, - LOCALPART_TOO_LONG) - invalid_chars = set(re.findall(RE_LOCALPART, localpart)) - if invalid_chars: - i_chrs = u''.join((u'“%s” ' % c for c in invalid_chars)) - raise VMMEAE(_(u"The local-part “%(l_part)s” contains invalid\ - characters: %(i_chrs)s") % {'l_part': localpart, 'i_chrs': i_chrs}, - LOCALPART_INVALID) - return localpart - del _ diff -r 02d467e4fbab -r 0684790fff7c VirtualMailManager/__init__.py --- a/VirtualMailManager/__init__.py Wed Feb 10 08:55:51 2010 +0000 +++ b/VirtualMailManager/__init__.py Thu Feb 11 03:08:11 2010 +0000 @@ -12,8 +12,8 @@ from encodings.idna import ToASCII, ToUnicode from VirtualMailManager.constants.ERROR import \ - DOMAIN_INVALID, DOMAIN_TOO_LONG, NOT_EXECUTABLE, NO_SUCH_BINARY, \ - NO_SUCH_DIRECTORY + DOMAIN_INVALID, DOMAIN_TOO_LONG, LOCALPART_INVALID, LOCALPART_TOO_LONG, \ + NOT_EXECUTABLE, NO_SUCH_BINARY, NO_SUCH_DIRECTORY from VirtualMailManager.constants.VERSION import * from VirtualMailManager.Exceptions import VMMException @@ -24,8 +24,8 @@ # version information from VERSION '__author__', '__date__', '__version__', # error codes - 'ENCODING', 'ace2idna', 'chk_domainname', 'exec_ok', 'expand_path', - 'get_unicode', 'idn2ascii', 'is_dir', + 'ENCODING', 'ace2idna', 'check_domainname', 'check_localpart', 'exec_ok', + 'expand_path', 'get_unicode', 'idn2ascii', 'is_dir', ] @@ -37,18 +37,26 @@ locale.setlocale(locale.LC_ALL, 'C') ENCODING = locale.nl_langinfo(locale.CODESET) -RE_ASCII_CHARS = """^[\x20-\x7E]*$""" -RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$""" +RE_ASCII_CHARS = r"^[\x20-\x7E]*$" +RE_DOMAIN = r"^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$" +RE_LOCALPART = r"[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]" + + +# there may be many domain and e-mail address checks +re_obj_domain = re.compile(RE_DOMAIN) +re_obj_localpart = re.compile(RE_LOCALPART) gettext.install('vmm', '/usr/local/share/locale', unicode=1) + def get_unicode(string): """Converts `string` to `unicode`, if necessary.""" if isinstance(string, unicode): return string return unicode(string, ENCODING, 'replace') + def expand_path(path): """Expands paths, starting with ``.`` or ``~``, to an absolute path.""" if path.startswith('.'): @@ -57,6 +65,7 @@ return os.path.expanduser(path) return path + def is_dir(path): """Checks if ``path`` is a directory. @@ -64,10 +73,11 @@ """ path = expand_path(path) if not os.path.isdir(path): - raise VMMException(_(u'“%s” is not a directory') % get_unicode(path), - NO_SUCH_DIRECTORY) + raise VMMException(_(u'“%s” is not a directory') % + get_unicode(path), NO_SUCH_DIRECTORY) return path + def exec_ok(binary): """Checks if the ``binary`` exists and if it is executable. @@ -83,27 +93,49 @@ get_unicode(binary), NOT_EXECUTABLE) return binary + def idn2ascii(domainname): """Converts the idn domain name `domainname` into punycode.""" return '.'.join([ToASCII(lbl) for lbl in domainname.split('.') if lbl]) + def ace2idna(domainname): """Converts the domain name `domainname` from ACE according to IDNA.""" return u'.'.join([ToUnicode(lbl) for lbl in domainname.split('.') if lbl]) -def chk_domainname(domainname): + +def check_domainname(domainname): """Returns the validated domain name `domainname`. It also converts the name of the domain from IDN to ASCII, if necessary. - Throws an VMMException, if the domain name is too long or doesn't look + Throws an `VMMException`, if the domain name is too long or doesn't look like a valid domain name (label.label.label). """ - if not re.match(RE_ASCII_CHARS, domainname): + if not re_obj_domain.match(domainname): domainname = idn2ascii(domainname) if len(domainname) > 255: - raise VMMException(_(u'The domain name is too long.'), DOMAIN_TOO_LONG) - if not re.match(RE_DOMAIN, domainname): - raise VMMException(_(u'The domain name “%s” is invalid.') % domainname, + raise VMMException(_(u'The domain name is too long'), DOMAIN_TOO_LONG) + if not re_obj_domain.match(domainname): + raise VMMException(_(u'The domain name %r is invalid') % domainname, DOMAIN_INVALID) return domainname + + +def check_localpart(localpart): + """Returns the validated local-part *localpart*. + + Throws a `VMMException` if the local-part is too long or contains + invalid characters. + """ + if len(localpart) > 64: + raise VMMException(_(u'The local-part %r is too long') % localpart, + LOCALPART_TOO_LONG) + invalid_chars = set(re_obj_localpart.findall(localpart)) + if invalid_chars: + i_chars = u''.join((u'"%s" ' % c for c in invalid_chars)) + raise VMMException(_(u"The local-part %(l_part)r contains invalid \ +characters: %(i_chars)s") % + {'l_part': localpart, 'i_chars': i_chars}, + LOCALPART_INVALID) + return localpart