VirtualMailManager/__init__.py
branchv0.6.x
changeset 199 0684790fff7c
parent 188 cf1b5f22dbd2
child 208 efa1327b721f
equal deleted inserted replaced
198:02d467e4fbab 199:0684790fff7c
    10 import locale
    10 import locale
    11 
    11 
    12 from encodings.idna import ToASCII, ToUnicode
    12 from encodings.idna import ToASCII, ToUnicode
    13 
    13 
    14 from VirtualMailManager.constants.ERROR import \
    14 from VirtualMailManager.constants.ERROR import \
    15      DOMAIN_INVALID, DOMAIN_TOO_LONG, NOT_EXECUTABLE, NO_SUCH_BINARY, \
    15      DOMAIN_INVALID, DOMAIN_TOO_LONG, LOCALPART_INVALID, LOCALPART_TOO_LONG, \
    16      NO_SUCH_DIRECTORY
    16      NOT_EXECUTABLE, NO_SUCH_BINARY, NO_SUCH_DIRECTORY
    17 from VirtualMailManager.constants.VERSION import *
    17 from VirtualMailManager.constants.VERSION import *
    18 from VirtualMailManager.Exceptions import VMMException
    18 from VirtualMailManager.Exceptions import VMMException
    19 
    19 
    20 
    20 
    21 __all__ = [
    21 __all__ = [
    22     # imported modules
    22     # imported modules
    23     'os', 're', 'locale',
    23     'os', 're', 'locale',
    24     # version information from VERSION
    24     # version information from VERSION
    25     '__author__', '__date__', '__version__',
    25     '__author__', '__date__', '__version__',
    26     # error codes
    26     # error codes
    27     'ENCODING', 'ace2idna', 'chk_domainname', 'exec_ok', 'expand_path',
    27     'ENCODING', 'ace2idna', 'check_domainname', 'check_localpart', 'exec_ok',
    28     'get_unicode', 'idn2ascii', 'is_dir',
    28     'expand_path', 'get_unicode', 'idn2ascii', 'is_dir',
    29 ]
    29 ]
    30 
    30 
    31 
    31 
    32 # Try to set all of the locales according to the current
    32 # Try to set all of the locales according to the current
    33 # environment variables and get the character encoding.
    33 # environment variables and get the character encoding.
    35     locale.setlocale(locale.LC_ALL, '')
    35     locale.setlocale(locale.LC_ALL, '')
    36 except locale.Error:
    36 except locale.Error:
    37     locale.setlocale(locale.LC_ALL, 'C')
    37     locale.setlocale(locale.LC_ALL, 'C')
    38 ENCODING = locale.nl_langinfo(locale.CODESET)
    38 ENCODING = locale.nl_langinfo(locale.CODESET)
    39 
    39 
    40 RE_ASCII_CHARS = """^[\x20-\x7E]*$"""
    40 RE_ASCII_CHARS = r"^[\x20-\x7E]*$"
    41 RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
    41 RE_DOMAIN = r"^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"
       
    42 RE_LOCALPART = r"[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]"
       
    43 
       
    44 
       
    45 # there may be many domain and e-mail address checks
       
    46 re_obj_domain = re.compile(RE_DOMAIN)
       
    47 re_obj_localpart = re.compile(RE_LOCALPART)
    42 
    48 
    43 
    49 
    44 gettext.install('vmm', '/usr/local/share/locale', unicode=1)
    50 gettext.install('vmm', '/usr/local/share/locale', unicode=1)
       
    51 
    45 
    52 
    46 def get_unicode(string):
    53 def get_unicode(string):
    47     """Converts `string` to `unicode`, if necessary."""
    54     """Converts `string` to `unicode`, if necessary."""
    48     if isinstance(string, unicode):
    55     if isinstance(string, unicode):
    49         return string
    56         return string
    50     return unicode(string, ENCODING, 'replace')
    57     return unicode(string, ENCODING, 'replace')
       
    58 
    51 
    59 
    52 def expand_path(path):
    60 def expand_path(path):
    53     """Expands paths, starting with ``.`` or ``~``, to an absolute path."""
    61     """Expands paths, starting with ``.`` or ``~``, to an absolute path."""
    54     if path.startswith('.'):
    62     if path.startswith('.'):
    55         return os.path.abspath(path)
    63         return os.path.abspath(path)
    56     if path.startswith('~'):
    64     if path.startswith('~'):
    57         return os.path.expanduser(path)
    65         return os.path.expanduser(path)
    58     return path
    66     return path
    59 
    67 
       
    68 
    60 def is_dir(path):
    69 def is_dir(path):
    61     """Checks if ``path`` is a directory.
    70     """Checks if ``path`` is a directory.
    62 
    71 
    63     Throws a `VMMException` if ``path`` is not a directory.
    72     Throws a `VMMException` if ``path`` is not a directory.
    64     """
    73     """
    65     path = expand_path(path)
    74     path = expand_path(path)
    66     if not os.path.isdir(path):
    75     if not os.path.isdir(path):
    67         raise VMMException(_(u'“%s” is not a directory') % get_unicode(path),
    76         raise VMMException(_(u'“%s” is not a directory') %
    68                            NO_SUCH_DIRECTORY)
    77                             get_unicode(path), NO_SUCH_DIRECTORY)
    69     return path
    78     return path
       
    79 
    70 
    80 
    71 def exec_ok(binary):
    81 def exec_ok(binary):
    72     """Checks if the ``binary`` exists and if it is executable.
    82     """Checks if the ``binary`` exists and if it is executable.
    73 
    83 
    74     Throws a `VMMException` if the ``binary`` isn't a file or is not
    84     Throws a `VMMException` if the ``binary`` isn't a file or is not
    81     if not os.access(binary, os.X_OK):
    91     if not os.access(binary, os.X_OK):
    82         raise VMMException(_(u'File is not executable: “%s”') %
    92         raise VMMException(_(u'File is not executable: “%s”') %
    83                            get_unicode(binary), NOT_EXECUTABLE)
    93                            get_unicode(binary), NOT_EXECUTABLE)
    84     return binary
    94     return binary
    85 
    95 
       
    96 
    86 def idn2ascii(domainname):
    97 def idn2ascii(domainname):
    87     """Converts the idn domain name `domainname` into punycode."""
    98     """Converts the idn domain name `domainname` into punycode."""
    88     return '.'.join([ToASCII(lbl) for lbl in domainname.split('.') if lbl])
    99     return '.'.join([ToASCII(lbl) for lbl in domainname.split('.') if lbl])
       
   100 
    89 
   101 
    90 def ace2idna(domainname):
   102 def ace2idna(domainname):
    91     """Converts the domain name `domainname` from ACE according to IDNA."""
   103     """Converts the domain name `domainname` from ACE according to IDNA."""
    92     return u'.'.join([ToUnicode(lbl) for lbl in domainname.split('.') if lbl])
   104     return u'.'.join([ToUnicode(lbl) for lbl in domainname.split('.') if lbl])
    93 
   105 
    94 def chk_domainname(domainname):
   106 
       
   107 def check_domainname(domainname):
    95     """Returns the validated domain name `domainname`.
   108     """Returns the validated domain name `domainname`.
    96 
   109 
    97     It also converts the name of the domain from IDN to ASCII, if necessary.
   110     It also converts the name of the domain from IDN to ASCII, if necessary.
    98 
   111 
    99     Throws an VMMException, if the domain name is too long or doesn't look
   112     Throws an `VMMException`, if the domain name is too long or doesn't look
   100     like a valid domain name (label.label.label).
   113     like a valid domain name (label.label.label).
   101     """
   114     """
   102     if not re.match(RE_ASCII_CHARS, domainname):
   115     if not re_obj_domain.match(domainname):
   103         domainname = idn2ascii(domainname)
   116         domainname = idn2ascii(domainname)
   104     if len(domainname) > 255:
   117     if len(domainname) > 255:
   105         raise VMMException(_(u'The domain name is too long.'), DOMAIN_TOO_LONG)
   118         raise VMMException(_(u'The domain name is too long'), DOMAIN_TOO_LONG)
   106     if not re.match(RE_DOMAIN, domainname):
   119     if not re_obj_domain.match(domainname):
   107         raise VMMException(_(u'The domain name “%s” is invalid.') % domainname,
   120         raise VMMException(_(u'The domain name %r is invalid') % domainname,
   108                            DOMAIN_INVALID)
   121                            DOMAIN_INVALID)
   109     return domainname
   122     return domainname
       
   123 
       
   124 
       
   125 def check_localpart(localpart):
       
   126     """Returns the validated local-part *localpart*.
       
   127 
       
   128     Throws a `VMMException` if the local-part is too long or contains
       
   129     invalid characters.
       
   130     """
       
   131     if len(localpart) > 64:
       
   132         raise VMMException(_(u'The local-part %r is too long') % localpart,
       
   133                            LOCALPART_TOO_LONG)
       
   134     invalid_chars = set(re_obj_localpart.findall(localpart))
       
   135     if invalid_chars:
       
   136         i_chars = u''.join((u'"%s" ' % c for c in invalid_chars))
       
   137         raise VMMException(_(u"The local-part %(l_part)r contains invalid \
       
   138 characters: %(i_chars)s") %
       
   139                            {'l_part': localpart, 'i_chars': i_chars},
       
   140                            LOCALPART_INVALID)
       
   141     return localpart