VirtualMailManager/handler.py
branchv0.6.x
changeset 325 06c0457036a0
parent 320 011066435e6f
child 327 bb9ff81928f1
equal deleted inserted replaced
324:38e344ba3d0f 325:06c0457036a0
    12    or deletes directories of domains or users.
    12    or deletes directories of domains or users.
    13 """
    13 """
    14 
    14 
    15 import os
    15 import os
    16 import re
    16 import re
       
    17 import stat
    17 
    18 
    18 from shutil import rmtree
    19 from shutil import rmtree
    19 from subprocess import Popen, PIPE
    20 from subprocess import Popen, PIPE
    20 
    21 
    21 from pyPgSQL import PgSQL  # python-pgsql - http://pypgsql.sourceforge.net
    22 from pyPgSQL import PgSQL  # python-pgsql - http://pypgsql.sourceforge.net
    23 from VirtualMailManager.account import Account
    24 from VirtualMailManager.account import Account
    24 from VirtualMailManager.alias import Alias
    25 from VirtualMailManager.alias import Alias
    25 from VirtualMailManager.aliasdomain import AliasDomain
    26 from VirtualMailManager.aliasdomain import AliasDomain
    26 from VirtualMailManager.common import exec_ok
    27 from VirtualMailManager.common import exec_ok
    27 from VirtualMailManager.config import Config as Cfg
    28 from VirtualMailManager.config import Config as Cfg
    28 from VirtualMailManager.constants import \
    29 from VirtualMailManager.constants import MIN_GID, MIN_UID, \
    29      ACCOUNT_EXISTS, ALIAS_EXISTS, CONF_NOFILE, CONF_NOPERM, CONF_WRONGPERM, \
    30      ACCOUNT_EXISTS, ALIAS_EXISTS, CONF_NOFILE, CONF_NOPERM, CONF_WRONGPERM, \
    30      DATABASE_ERROR, DOMAINDIR_GROUP_MISMATCH, DOMAIN_INVALID, \
    31      DATABASE_ERROR, DOMAINDIR_GROUP_MISMATCH, DOMAIN_INVALID, \
    31      FOUND_DOTS_IN_PATH, INVALID_ARGUMENT, MAILDIR_PERM_MISMATCH, \
    32      FOUND_DOTS_IN_PATH, INVALID_ARGUMENT, MAILDIR_PERM_MISMATCH, \
    32      NOT_EXECUTABLE, NO_SUCH_ACCOUNT, NO_SUCH_ALIAS, NO_SUCH_BINARY, \
    33      NOT_EXECUTABLE, NO_SUCH_ACCOUNT, NO_SUCH_ALIAS, NO_SUCH_BINARY, \
    33      NO_SUCH_DIRECTORY, NO_SUCH_RELOCATED, RELOCATED_EXISTS
    34      NO_SUCH_DIRECTORY, NO_SUCH_RELOCATED, RELOCATED_EXISTS
    34 from VirtualMailManager.domain import Domain, get_gid
    35 from VirtualMailManager.domain import Domain, get_gid
    35 from VirtualMailManager.emailaddress import EmailAddress
    36 from VirtualMailManager.emailaddress import EmailAddress
    36 from VirtualMailManager.errors import \
    37 from VirtualMailManager.errors import \
    37      DomainError, NotRootError, PermissionError, VMMError
    38      DomainError, NotRootError, PermissionError, VMMError
    38 from VirtualMailManager.mailbox import new as new_mailbox
    39 from VirtualMailManager.mailbox import new as new_mailbox
    39 from VirtualMailManager.pycompat import any
    40 from VirtualMailManager.pycompat import all, any
    40 from VirtualMailManager.relocated import Relocated
    41 from VirtualMailManager.relocated import Relocated
    41 from VirtualMailManager.transport import Transport
    42 from VirtualMailManager.transport import Transport
    42 
    43 
    43 
    44 
    44 _ = lambda msg: msg
    45 _ = lambda msg: msg
   226         return Domain(self._dbh, domainname)
   227         return Domain(self._dbh, domainname)
   227 
   228 
   228     def _get_disk_usage(self, directory):
   229     def _get_disk_usage(self, directory):
   229         """Estimate file space usage for the given directory.
   230         """Estimate file space usage for the given directory.
   230 
   231 
   231         Keyword arguments:
   232         Arguments:
   232         directory -- the directory to summarize recursively disk usage for
   233 
   233         """
   234         `directory` : basestring
   234         if self._isdir(directory):
   235           The directory to summarize recursively disk usage for
       
   236         """
       
   237         if os.path.isdir(directory):
   235             return Popen([self._cfg.dget('bin.du'), "-hs", directory],
   238             return Popen([self._cfg.dget('bin.du'), "-hs", directory],
   236                 stdout=PIPE).communicate()[0].split('\t')[0]
   239                          stdout=PIPE).communicate()[0].split('\t')[0]
   237         else:
   240         else:
       
   241             self._warnings.append(_('No such directory: %s') % directory)
   238             return 0
   242             return 0
   239 
       
   240     def _isdir(self, directory):
       
   241         """Check if `directory` is a directory. Returns bool.
       
   242         When `directory` isn't a directory, a warning will be appended to
       
   243         _warnings."""
       
   244         isdir = os.path.isdir(directory)
       
   245         if not isdir:
       
   246             self._warnings.append(_('No such directory: %s') % directory)
       
   247         return isdir
       
   248 
   243 
   249     def _make_domain_dir(self, domain):
   244     def _make_domain_dir(self, domain):
   250         """Create a directory for the `domain` and its accounts."""
   245         """Create a directory for the `domain` and its accounts."""
   251         cwd = os.getcwd()
   246         cwd = os.getcwd()
   252         hashdir, domdir = domain.directory.split(os.path.sep)[-2:]
   247         hashdir, domdir = domain.directory.split(os.path.sep)[-2:]
   265         os.chdir(account.domain_directory)
   260         os.chdir(account.domain_directory)
   266         os.mkdir('%s' % account.uid, self._cfg.dget('account.directory_mode'))
   261         os.mkdir('%s' % account.uid, self._cfg.dget('account.directory_mode'))
   267         os.chown('%s' % account.uid, account.uid, account.gid)
   262         os.chown('%s' % account.uid, account.uid, account.gid)
   268 
   263 
   269     def _delete_home(self, domdir, uid, gid):
   264     def _delete_home(self, domdir, uid, gid):
   270         """Delete a user's home directory."""
   265         """Delete a user's home directory.
   271         if uid > 0 and gid > 0:
   266 
   272             userdir = '%s' % uid
   267         Arguments:
   273             if userdir.count('..') or domdir.count('..'):
   268 
   274                 raise VMMError(_(u'Found ".." in home directory path.'),
   269         `domdir` : basestring
   275                                FOUND_DOTS_IN_PATH)
   270           The directory of the domain the user belongs to
   276             if os.path.isdir(domdir):
   271           (commonly AccountObj.domain_directory)
   277                 os.chdir(domdir)
   272         `uid` : int/long
   278                 if os.path.isdir(userdir):
   273           The user's UID (commonly AccountObj.uid)
   279                     mdstat = os.stat(userdir)
   274         `gid` : int/long
   280                     if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
   275           The user's GID (commonly AccountObj.gid)
   281                         raise VMMError(_(u'Detected owner/group mismatch in '
   276         """
   282                                          u'home directory.'),
   277         assert all(isinstance(xid, (long, int)) for xid in (uid, gid)) and \
   283                                        MAILDIR_PERM_MISMATCH)
   278                 isinstance(domdir, basestring)
   284                     rmtree(userdir, ignore_errors=True)
   279         if uid < MIN_UID or gid < MIN_GID:
   285                 else:
   280             raise VMMError(_(u"UID '%(uid)u' and/or GID '%(gid)u' are less "
   286                     raise VMMError(_(u"No such directory: %s") %
   281                              u"than %(min_uid)u/%(min_gid)u.") % {'uid': uid,
   287                                    os.path.join(domdir, userdir),
   282                            'gid': gid, 'min_gid': MIN_GID, 'min_uid': MIN_UID},
   288                                    NO_SUCH_DIRECTORY)
   283                            MAILDIR_PERM_MISMATCH)
       
   284         if domdir.count('..'):
       
   285             raise VMMError(_(u'Found ".." in domain directory path: %s') %
       
   286                            domdir, FOUND_DOTS_IN_PATH)
       
   287         if not os.path.isdir(domdir):
       
   288             raise VMMError(_(u"No such directory: %s") % domdir,
       
   289                            NO_SUCH_DIRECTORY)
       
   290         os.chdir(domdir)
       
   291         userdir = '%s' % uid
       
   292         if not os.path.isdir(userdir):
       
   293             self._warnings.append(_(u"No such directory: %s") %
       
   294                                   os.path.join(domdir, userdir))
       
   295             return
       
   296         mdstat = os.lstat(userdir)
       
   297         if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
       
   298             raise VMMError(_(u'Detected owner/group mismatch in home '
       
   299                              u'directory.'), MAILDIR_PERM_MISMATCH)
       
   300         rmtree(userdir, ignore_errors=True)
   289 
   301 
   290     def _delete_domain_dir(self, domdir, gid):
   302     def _delete_domain_dir(self, domdir, gid):
   291         """Delete a domain's directory."""
   303         """Delete a domain's directory.
   292         if gid > 0:
   304 
   293             if not self._isdir(domdir):
   305         Arguments:
   294                 return
   306 
   295             basedir = self._cfg.dget('misc.base_directory')
   307         `domdir` : basestring
   296             domdirdirs = domdir.replace(basedir + '/', '').split('/')
   308           The domain's directory (commonly DomainObj.directory)
   297             domdirparent = os.path.join(basedir, domdirdirs[0])
   309         `gid` : int/long
   298             if basedir.count('..') or domdir.count('..'):
   310           The domain's GID (commonly DomainObj.gid)
   299                 raise VMMError(_(u'Found ".." in domain directory path.'),
   311         """
   300                                FOUND_DOTS_IN_PATH)
   312         assert isinstance(domdir, basestring) and isinstance(gid, (long, int))
   301             if os.path.isdir(domdirparent):
   313         if gid < MIN_GID:
   302                 os.chdir(domdirparent)
   314             raise VMMError(_(u"GID '%(gid)u' is less than '%(min_gid)u'.") %
   303                 if os.lstat(domdirdirs[1]).st_gid != gid:
   315                            {'gid': gid, 'min_gid': MIN_GID},
   304                     raise VMMError(_(u'Detected group mismatch in domain '
   316                            DOMAINDIR_GROUP_MISMATCH)
   305                                      u'directory.'), DOMAINDIR_GROUP_MISMATCH)
   317         if domdir.count('..'):
   306                 rmtree(domdirdirs[1], ignore_errors=True)
   318             raise VMMError(_(u'Found ".." in domain directory path: %s') %
       
   319                            domdir, FOUND_DOTS_IN_PATH)
       
   320         try:
       
   321             dirst = os.lstat(domdir)
       
   322         except OSError:
       
   323             dirst = None
       
   324         if not dirst or not stat.S_ISDIR(dirst.st_mode):
       
   325             self._warnings.append(_('No such directory: %s') % domdir)
       
   326             return
       
   327         if dirst.st_gid != gid:
       
   328             raise VMMError(_(u'Detected group mismatch in domain directory: '
       
   329                              u'%s') % domdir, DOMAINDIR_GROUP_MISMATCH)
       
   330         rmtree(domdir, ignore_errors=True)
   307 
   331 
   308     def has_warnings(self):
   332     def has_warnings(self):
   309         """Checks if warnings are present, returns bool."""
   333         """Checks if warnings are present, returns bool."""
   310         return bool(len(self._warnings))
   334         return bool(len(self._warnings))
   311 
   335