VirtualMailManager/VirtualMailManager.py
changeset 38 c44ea4526546
parent 35 22cc616aef61
child 40 ab0748a5da9a
equal deleted inserted replaced
37:89f952b0f366 38:c44ea4526546
    16 import os
    16 import os
    17 import re
    17 import re
    18 import sys
    18 import sys
    19 import gettext
    19 import gettext
    20 from encodings.idna import ToASCII, ToUnicode
    20 from encodings.idna import ToASCII, ToUnicode
       
    21 from getpass import getpass
    21 from shutil import rmtree
    22 from shutil import rmtree
    22 from subprocess import Popen, PIPE
    23 from subprocess import Popen, PIPE
    23 
    24 
    24 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net
    25 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net
    25 
    26 
    70             self.__chkenv()
    71             self.__chkenv()
    71 
    72 
    72     def __chkCfgFile(self):
    73     def __chkCfgFile(self):
    73         """Checks the configuration file, returns bool"""
    74         """Checks the configuration file, returns bool"""
    74         if not os.path.isfile(self.__cfgFileName):
    75         if not os.path.isfile(self.__cfgFileName):
    75             raise VMMException((_(u"The file »%s« does not exists.") %
    76             raise VMMException((_(u"The file '%s' does not exists.") %
    76                 self.__cfgFileName, ERR.CONF_NOFILE))
    77                 self.__cfgFileName, ERR.CONF_NOFILE))
    77         fstat = os.stat(self.__cfgFileName)
    78         fstat = os.stat(self.__cfgFileName)
    78         try:
    79         try:
    79             fmode = self.__getFileMode()
    80             fmode = self.__getFileMode()
    80         except:
    81         except:
   137         if len(localpart) > 64:
   138         if len(localpart) > 64:
   138             raise VMMException((_('The local part is too long'),
   139             raise VMMException((_('The local part is too long'),
   139                 ERR.LOCALPART_TOO_LONG))
   140                 ERR.LOCALPART_TOO_LONG))
   140         if re.compile(RE_LOCALPART).search(localpart):
   141         if re.compile(RE_LOCALPART).search(localpart):
   141             raise VMMException((
   142             raise VMMException((
   142                 _(u'The local part »%s« contains invalid characters.') %
   143                 _(u"The local part '%s' contains invalid characters.") %
   143                 localpart, ERR.LOCALPART_INVALID))
   144                 localpart, ERR.LOCALPART_INVALID))
   144         return localpart
   145         return localpart
   145 
   146 
   146     def idn2ascii(self, domainname):
   147     def idn2ascii(self, domainname):
   147         """Converts an idn domainname in punycode.
   148         """Converts an idn domainname in punycode.
   189 
   190 
   190     def __chkEmailAddress(self, address):
   191     def __chkEmailAddress(self, address):
   191         try:
   192         try:
   192             localpart, domain = address.split('@')
   193             localpart, domain = address.split('@')
   193         except ValueError:
   194         except ValueError:
   194             raise VMMException((_(u"Missing '@' sign in e-mail address »%s«.") %
   195             raise VMMException((_(u"Missing '@' sign in e-mail address '%s'.") %
   195                 address, ERR.INVALID_ADDRESS))
   196                 address, ERR.INVALID_ADDRESS))
   196         except AttributeError:
   197         except AttributeError:
   197             raise VMMException((_(u"»%s« looks not like an e-mail address.") %
   198             raise VMMException((_(u"'%s' looks not like an e-mail address.") %
   198                 address, ERR.INVALID_ADDRESS))
   199                 address, ERR.INVALID_ADDRESS))
   199         domain = self.__chkDomainname(domain)
   200         domain = self.__chkDomainname(domain)
   200         localpart = self.__chkLocalpart(localpart)
   201         localpart = self.__chkLocalpart(localpart)
   201         return '%s@%s' % (localpart, domain)
   202         return '%s@%s' % (localpart, domain)
   202 
   203 
   204         address = self.__chkEmailAddress(address)
   205         address = self.__chkEmailAddress(address)
   205         self.__dbConnect()
   206         self.__dbConnect()
   206         if not password is None:
   207         if not password is None:
   207             password = self.__pwhash(password)
   208             password = self.__pwhash(password)
   208         return Account(self.__dbh, address, password)
   209         return Account(self.__dbh, address, password)
       
   210 
       
   211     def _readpass(self):
       
   212         clear0 = ''
       
   213         clear1 = '1'
       
   214         while clear0 != clear1:
       
   215             while len(clear0) < 1:
       
   216                 clear0 = getpass(prompt=_('Enter new password: '))
       
   217                 if len(clear0) < 1:
       
   218                     sys.stderr.write('%s\n'
       
   219                             % _('Sorry, empty passwords are not permitted'))
       
   220             clear1 = getpass(prompt=_('Retype new password: '))
       
   221             if clear0 != clear1:
       
   222                 clear0 = ''
       
   223                 sys.stderr.write('%s\n' % _('Sorry, passwords do not match'))
       
   224         return clear0
   209 
   225 
   210     def __getAlias(self, address, destination=None):
   226     def __getAlias(self, address, destination=None):
   211         address = self.__chkEmailAddress(address)
   227         address = self.__chkEmailAddress(address)
   212         if not destination is None:
   228         if not destination is None:
   213             if destination.count('@'):
   229             if destination.count('@'):
   408         """
   424         """
   409         try:
   425         try:
   410             if not section:
   426             if not section:
   411                 self.__Cfg.configure(self.__cfgSections)
   427                 self.__Cfg.configure(self.__cfgSections)
   412             elif section not in self.__cfgSections:
   428             elif section not in self.__cfgSections:
   413                 raise VMMException((_(u"Invalid section: »%s«") % section,
   429                 raise VMMException((_(u"Invalid section: '%s'") % section,
   414                     ERR.INVALID_SECTION))
   430                     ERR.INVALID_SECTION))
   415             else:
   431             else:
   416                 self.__Cfg.configure([section])
   432                 self.__Cfg.configure([section])
   417         except:
   433         except:
   418             raise
   434             raise
   422         dom.save()
   438         dom.save()
   423         self.__domdirmake(dom.getDir(), dom.getID())
   439         self.__domdirmake(dom.getDir(), dom.getID())
   424 
   440 
   425     def domain_transport(self, domainname, transport, force=None):
   441     def domain_transport(self, domainname, transport, force=None):
   426         if force is not None and force != 'force':
   442         if force is not None and force != 'force':
   427             raise VMMDomainException((_(u'Invalid argument: »%s«') % force,
   443             raise VMMDomainException((_(u"Invalid argument: '%s'") % force,
   428                 ERR.INVALID_OPTION))
   444                 ERR.INVALID_OPTION))
   429         dom = self.__getDomain(domainname, None)
   445         dom = self.__getDomain(domainname, None)
   430         if force is None:
   446         if force is None:
   431             dom.updateTransport(transport)
   447             dom.updateTransport(transport)
   432         else:
   448         else:
   433             dom.updateTransport(transport, force=True)
   449             dom.updateTransport(transport, force=True)
   434 
   450 
   435     def domain_delete(self, domainname, force=None):
   451     def domain_delete(self, domainname, force=None):
   436         if not force is None and force not in ['deluser','delalias','delall']:
   452         if not force is None and force not in ['deluser','delalias','delall']:
   437             raise VMMDomainException((_(u'Invalid argument: »%s«') % force,
   453             raise VMMDomainException((_(u"Invalid argument: '%s'") % force,
   438                 ERR.INVALID_OPTION))
   454                 ERR.INVALID_OPTION))
   439         dom = self.__getDomain(domainname)
   455         dom = self.__getDomain(domainname)
   440         gid = dom.getID()
   456         gid = dom.getID()
   441         domdir = dom.getDir()
   457         domdir = dom.getDir()
   442         if self.__Cfg.getboolean('misc', 'forcedel') or force == 'delall':
   458         if self.__Cfg.getboolean('misc', 'forcedel') or force == 'delall':
   461         if detailed is None:
   477         if detailed is None:
   462             return dominfo
   478             return dominfo
   463         elif detailed == 'detailed':
   479         elif detailed == 'detailed':
   464             return dominfo, dom.getAccounts(), dom.getAliases()
   480             return dominfo, dom.getAccounts(), dom.getAliases()
   465         else:
   481         else:
   466             raise VMMDomainException(('%s: »%s«' % (_('Invalid argument'),
   482             raise VMMDomainException(("%s: '%s'" % (_('Invalid argument'),
   467                 detailed),  ERR.INVALID_OPTION))
   483                 detailed),  ERR.INVALID_OPTION))
   468 
   484 
   469     def domain_list(self, pattern=None):
   485     def domain_list(self, pattern=None):
   470         from Domain import search
   486         from Domain import search
   471         like = False
   487         like = False
   479                 elif pattern.endswith('%'):
   495                 elif pattern.endswith('%'):
   480                     domain = pattern[:-1]
   496                     domain = pattern[:-1]
   481                 re.compile(RE_DOMAIN_SRCH)
   497                 re.compile(RE_DOMAIN_SRCH)
   482                 if not re.match(RE_DOMAIN_SRCH, domain):
   498                 if not re.match(RE_DOMAIN_SRCH, domain):
   483                     raise VMMException((
   499                     raise VMMException((
   484                     _(u'The pattern »%s« contains invalid characters.') %
   500                     _(u"The pattern '%s' contains invalid characters.") %
   485                     pattern, ERR.DOMAIN_INVALID))
   501                     pattern, ERR.DOMAIN_INVALID))
   486             else:
   502             else:
   487                 pattern = self.__chkDomainname(pattern)
   503                 pattern = self.__chkDomainname(pattern)
   488         self.__dbConnect()
   504         self.__dbConnect()
   489         return search(self.__dbh, pattern=pattern, like=like)
   505         return search(self.__dbh, pattern=pattern, like=like)
   490 
   506 
   491     def user_add(self, emailaddress, password):
   507     def user_add(self, emailaddress, password):
   492         acc = self.__getAccount(emailaddress, password)
   508         acc = self.__getAccount(emailaddress, password)
       
   509         if password is None:
       
   510             password = self._readpass()
       
   511             acc.setPassword(self.__pwhash(password))
   493         acc.save(self.__Cfg.get('maildir', 'folder'),
   512         acc.save(self.__Cfg.get('maildir', 'folder'),
   494                 self.__Cfg.getboolean('services', 'smtp'),
   513                 self.__Cfg.getboolean('services', 'smtp'),
   495                 self.__Cfg.getboolean('services', 'pop3'),
   514                 self.__Cfg.getboolean('services', 'pop3'),
   496                 self.__Cfg.getboolean('services', 'imap'),
   515                 self.__Cfg.getboolean('services', 'imap'),
   497                 self.__Cfg.getboolean('services', 'managesieve'))
   516                 self.__Cfg.getboolean('services', 'managesieve'))
   529         self.__dbConnect()
   548         self.__dbConnect()
   530         return getAccountByID(uid, self.__dbh)
   549         return getAccountByID(uid, self.__dbh)
   531 
   550 
   532     def user_password(self, emailaddress, password):
   551     def user_password(self, emailaddress, password):
   533         acc = self.__getAccount(emailaddress)
   552         acc = self.__getAccount(emailaddress)
       
   553         if acc.getUID() == 0:
       
   554            raise VMMException((_("Account doesn't exists"),ERR.NO_SUCH_ACCOUNT))
       
   555         if password is None:
       
   556             password = self._readpass()
   534         acc.modify('password', self.__pwhash(password))
   557         acc.modify('password', self.__pwhash(password))
   535 
   558 
   536     def user_name(self, emailaddress, name):
   559     def user_name(self, emailaddress, name):
   537         acc = self.__getAccount(emailaddress)
   560         acc = self.__getAccount(emailaddress)
   538         acc.modify('name', name)
   561         acc.modify('name', name)