#-*- coding: utf-8 -*-

# Copyright 2010 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import os
import stat
import re
import sys
import pwd

import subprocess
import ldap
import time
import types

import getpass

from datavars import DataVarsClient, DataVars, __version__,__app__

from calculate.lib.cl_template import (Template, iniParser,TemplatesError,
                                       ProgressTemplate)
from calculate.lib.cl_print import color_print
from calculate.lib.cl_ldap import ldapUser
from calculate.lib.utils.ip import Pinger, isOpenPort, IPError
from calculate.lib.utils.files import (runOsCommand, getModeFile, removeDir,
                                       isMount, readFile, pathJoin, tarLinks)
from calculate.lib.utils.common import (getpathenv, appendProgramToEnvFile,
                                        removeProgramToEnvFile)
from _cl_keys import getKey, clearKey
from calculate.lib.convertenv import convertEnv
from calculate.lib.encrypt import encrypt
from cl_client_cache import userCache
from shutil import copy2
from socket import gethostbyname
import tarfile
from collections import OrderedDict

from calculate.core.server.func import safetyWrapper

from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate
setLocalTranslate('cl_client3',sys.modules[__name__])
__ = getLazyLocalTranslate(_)

class ClientError(Exception):
    pass

class RsyncProgressBar:
    """
    Rsync with using setProgress
    """
    # get number of send file from info rsync stream
    senderre = re.compile("\[sender\] i=(\d+) ", re.S)
    # get number of receivce file from info rsync rtream
    receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S)
    pipe = None
    maximum = 1
    copyStarting = False

    def __init__(self, title, secondtitle, rsyncstr, parent,maximum=1):
        self.title = title
        self.secondtitle = secondtitle
        self.maximum = maximum
        self.rsyncstr = rsyncstr
        self.parent = parent
        self.value = 0

    def getFilesNum(self):
        """
        Get files count created by generator
        """
        if self.pipe:
            return self.value

    def getExitCode(self):
        """
        Get rsync exitcode
        """
        if self.pipe:
            return self.pipe.wait()
        return 255

    def getErrMessage(self):
        """
        Rsync error message 
        """
        if self.pipe:
            return self.pipe.stderr.read()
        return _("RsyncProgressBar: Wrong pipe")

    def runsilent(self):
        """
        Run rsync without progressbar
        """
        self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     close_fds=True, shell=True)
        while True:
            s = self.pipe.stdout.readline()
            if len(s) == 0:
                break
            q = self.receiverre.search(s)
            if q:
                self.value = int(q.groups()[0])

    def run(self):
        """
        Run rsync with progressbar
        """
        self.parent.startTask(self.title,progress=True)
        self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     close_fds=True, shell=True)
        oldpercent = 0
        while True:
            s = self.pipe.stdout.readline()
            if len(s) == 0:
                break
            q = self.receiverre.search(s)
            if q:
                if not self.copyStarting:
                    self.parent.endTask()
                    self.parent.startTask(self.secondtitle,progress=True)
                    #self.parent.setProgress(0)
                    self.copyStarting = True
                self.value = int(q.groups()[0])
                if self.maximum:
                    newvalue = int(100*self.value / self.maximum)
                    newvalue = min(newvalue,99)
                    if newvalue > oldpercent:
                        self.parent.setProgress(newvalue)
                        oldpercent = newvalue
        self.parent.setProgress(100)

    def close(self):
        self.parent.endTask()

class ldapData(ldapUser):
    """LDAP methods"""

    def getReplDN(self):
        """
        Get from LDAP last domain server

        Branch has ((username,systemname,host))
        """
        usersDN = self.getUsersDN()
        if usersDN:
            partDN = "ou=Worked,ou=Replication,ou=LDAP"
            servicesDN = "ou=Services"
            baseDN = usersDN.rpartition(servicesDN+",")[2]
            replDN = self.addDN(partDN, servicesDN, baseDN)
            return replDN
        return False

    def searchPrevHost(self, userName, osLinuxShort):
        """Find server which user use"""
        connectData = self.getBindConnectData()
        if connectData:
            bindDn, bindPw, host = connectData
            replDN = self.getReplDN()
            # find string for service replication branch
            userAndOsName = "%s@%s"%(userName,osLinuxShort)
            findAttr = "uid=%s"%userAndOsName
            # connect to LDAP
            if not self.ldapConnect(bindDn, bindPw, host):
                return False
            resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL,
                                                findAttr, ["host"])
            return resSearch
        return False

    def _gethostbyname(self,hostname):
        try:
            return gethostbyname(hostname)
        except:
            pass
        return None

    def getNameRemoteServer(self,userName, osLinuxShort, curHost):
        """
        Get remote domain hostname or empty if profile is keeped on 
        current server
        """
        searchPrevHost = self.searchPrevHost(userName, osLinuxShort)
        if searchPrevHost and searchPrevHost[0][0][1].has_key('host'):
            prevHost = searchPrevHost[0][0][1]['host'][0]
        else:
            prevHost = None
        # get ip address of previous server and current server
        prevIp = self._gethostbyname(prevHost)
        curIp = self._gethostbyname(curHost)
        # if actual profile not found or on local
        if not prevHost or curIp and prevIp == curIp:
            return False
        else:
            return prevHost

    def isRepl(self):
        """
        Is on or off replication on server
        """
        connectData = self.getBindConnectData()
        if connectData:
            bindDn, bindPw, host = connectData
            usersDN = self.getUsersDN()
            partDN = "ou=Replication,ou=LDAP"
            servicesDN = "ou=Services"
            baseDN = usersDN.rpartition(servicesDN+",")[2]
            replDN = self.addDN(partDN, servicesDN, baseDN)
            findAttr = "ou=Worked"
            # connect to LDAP
            if not self.ldapConnect(bindDn, bindPw, host):
                return False
            resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL,
                                                findAttr,
                                                [findAttr.partition("=")[0]])
            if resSearch:
                return True
        return False

class commandServer(color_print):
    """Server command object"""

    def setServerCommand(self, command, varsCommand, fileConfig,
                         uid=None, gid=None):
        """Set server command"""
        pathConfig = os.path.split(fileConfig)[0]
        # create user config directory
        if not os.path.exists(pathConfig):
            os.makedirs(pathConfig)
            if not uid is None and not gid is None:
                os.chown(pathConfig, uid, gid)
        objConfig = iniParser(fileConfig)
        varsRun = {"run":"on"}
        varsRun.update(varsCommand)
        if not objConfig.setVar(["command"]+command, varsRun):
            raise ClientError(_("Failed to write variables in file %s")\
                            %fileConfig)
        if not uid is None and not gid is None:
            os.chown(fileConfig, uid, gid)
        return True

    def checkUserPwdLDAP(self, server, userDN, password):
        """Проверка пароля Unix пользователя на сервере"""
        ldapInit = ldap.initialize("ldap://%s"%server)
        errMessage = ""
        try: 
            ldapInit.bind_s(userDN, password)
        except ldap.INVALID_CREDENTIALS:
            errMessage = _("Wrong password")
            return False, errMessage
        except ldap.LDAPError, e:
            errMessage = e[0]['desc']
            return False, errMessage
        return True, errMessage

    def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False):
        """Получить пароль у пользователя

        options   - полученные опции командной строки
        optDialog - опция командной строки для вывода диалога для получения
                    пароля
        optStdIn  - опция командной строки для получения пароля из
                    стандартного ввода (stdin)
        pwDialog  - структура для вывода приглашения в режиме диалога
        """
        userPwd = ""
        if optStdIn and options.has_key(optStdIn):
            pwdA = sys.stdin.readline().rstrip()
            pwdB = sys.stdin.readline().rstrip()
        elif optDialog and options.has_key(optDialog):
            if not pwDialog:
                pwDialog = [_("New password"),
                            _("Retype the new password")]
            pwdA = getpass.getpass(pwDialog[0]+":")
            pwdB = getpass.getpass(pwDialog[1]+":")
        if (optStdIn and options.has_key(optStdIn)) or\
            (optDialog and options.has_key(optDialog)):
            if not pwdA or not (pwdA == pwdB):
                self.printERROR (_("ERROR") + ": " +\
                _("passwords do not match"))
                return False
            userPwd = pwdA
        return userPwd

class Client(commandServer, encrypt):
    """
    Client logic object

    Has fundamental methods:
    mountUserResAndSync - mount user resources and sync profile from server
    umountUserResAndSync - umount user resources and sync profile to server
    mountRemote - mount remote domain resource
    delDomain - remove workstation from domain
    addDomain - add workstation to domain
    changePasswd - change password on server
    """

    # object for user data in LDAP
    ldapDataObj = ldapData()
    # user calculate config directory
    pathConfig = ".calculate"
    # client soft config file
    configFileSoft = os.path.join(pathConfig, "ini.env")
    # client config file
    configFileDesktop = os.path.join(pathConfig, "desktop.env")
    # client config for server
    configFileServer = os.path.join(pathConfig, "server.env")
    # file list of user profile
    listTemplFile = os.path.join(pathConfig, "files.txt")

    # files which not cleaning from user home directory
    skipHomeFile = ["Home","Disks","Share","FTP",configFileDesktop]

    # option dict of services from /var/calculate/remote/server.env
    optionsInfo = {}

    # convertation object from old remote env
    convObj = None
    # private user files
    privateFiles = [configFileServer]
    # private user directories
    privateDirs =  []

    def __init__(self):
        self.clVars = None
        self.clTempl = None

    def initVars(self,datavars=None):
        """Primary variables initialization"""
        if not datavars:
            self.clVars = DataVarsClient()
            self.clVars.importClient()
            self.clVars.flIniFile()
        else:
            self.clVars = datavars

    def removeVars(self):
        """
        Remove domain variables. Useing on remove package
        and undomain action
        """
        self.clVars.Delete("cl_remote_host", "local")
        self.clVars.Delete("cl_remote_pw", "local")
        self.clVars.Delete("os_remote_auth")
        self.clVars.Delete("os_remote_client")
        self.clVars.Set("cl_remote_host", "", True)
        self.clVars.Set("cl_remote_pw", "", True)
        self.clVars.Set("os_remote_auth", "", True)
        self.clVars.Set("os_remote_client", "", True)

    def applyTemplatesFromSystem(self):
        """Apply templates for system"""
        if self.clVars.Get('cl_action') == 'domain':
            self.startTask(_("Appling domain templates"),progress=True)
        else:
            self.startTask(_("Appling undomain templates"),progress=True)
        if self.clTempl:
            self.closeClTemplate()
        self.clTempl = ProgressTemplate(self.setProgress,self.clVars,
                                        cltObj=True)
        dirsFiles = self.clTempl.applyTemplates()
        if self.clTempl.getError():
            self.printERROR(self.clTempl.getError().strip())
            return False
        else:
            self.endTask()
            return dirsFiles

    def isTwoSessionsUser(self, userName):
        """
        Check on second user login
        """
        xSession = False
        foundTwoSession = False
        resWho = self.execProg("who")
        if resWho:
            for pattern in ("%s\s+\d+\s+","%s\s+tty\d+.*:\d+"):
                reFoundUser = re.compile(pattern%(userName))
                for string in resWho:
                    if reFoundUser.search(string):
                        if xSession:
                            self.printERROR(\
                                _("Second X session for user %s cannot be opened.")\
                                  %userName)
                            return True
                        else:
                            xSession = True
        return False

    def isDomain(self):
        """
        Check domain or not workstation
        """
        foundMountRemote = isMount("/var/calculate/remote")
        remoteHost = self.clVars.Get("cl_remote_host")
        if remoteHost and foundMountRemote:
            return True
        return False

    def getSyncStatus(self,rpath):
        """
        Get local config 'date' and 'status_sync' or None
        """
        fileConfig = os.path.join(rpath, self.configFileDesktop)
        # get data from desktop config on success run get by archive
        if os.path.exists(fileConfig):
            objConfig = iniParser(fileConfig)
            data = self.getDataInConfig("main", ["status_sync"],
                                                 objConfig)
            if data:
                return data.get("status_sync",None)
        return None


    def getUserMountResources(self, userName, homeDir, flagRemoteServer):
        """
        Get information about remote profile resource
        """
        home = os.path.split(homeDir)[0]
        dictResources = OrderedDict(# share resource
                         share={"resource":"share",
                                  "path":os.path.join(homeDir,"Share")},
                         profile={"resource":"unix",
                                  "path":os.path.join(home,"."+userName)},
                         # resource - home directory
                         home={"resource":"homes",
                                 "path":os.path.join(homeDir,"Home")})
        if self.getInfoService("ftp", "host"):
            # resource ftp
            dictResources["ftp"] = {"resource":"ftp",
                                   "path":os.path.join(homeDir,"FTP")}
        if flagRemoteServer:
            # remote user profile
            dictResources["remote_profile"] = {"resource":"unix",
                            "path":os.path.join(home,"."+userName+"."+"remote")}
        return dictResources

    def mountSambaRes(self,host,userName,userPwd,uid,gid,res,rpath,
                      mountUidList=['ftp','homes','share']):
        """Mount samba resource"""
        if res in mountUidList:
            # mount by uid
            mountStr = "mount -t cifs -o user=%s,uid=%s,gid=%s,noperm"\
                       %(userName,uid,gid) +\
                       " //%s/%s %s" %(host, res, rpath)
        else:
            # mount by root
            mountStr = "mount -t cifs -o user=%s"%(userName)+\
                       " //%s/%s %s" %(host, res, rpath)
        textLine = self.execProg(mountStr, envProg={"PASSWD":userPwd})
        return textLine

    def mountSleepRes(self,host,userPwd,res,rpath):
        """
        Mount user resource with waiting if need
        """
        for waittime in [0.5,2,5]:
            if self.mountSambaRes(host,self.userName,userPwd,
                                  self.uid,self.gid,res,rpath) is False:
                # check on mount path
                if isMount(rpath):
                    if not self.umountSleepPath(rpath):
                        return False
                # wait
                time.sleep(waittime)
            else:
                return True
        return False

    def copyTemplateDir(self, srcDir, destDir):
        """
        Move directory srcDir to destDir
        """
        if os.system("mv %s %s &>/dev/null"%(srcDir, destDir)) != 0:
            raise ClientError(_("Failed to move %s")%srcDir + " " +\
                            _("to %s")%destDir)

    def upgradeUserProfile(self, userName, userHome, pathTemplates):
        """
        Update user profile (example /home/.user/.CLD to /home/.user/CLD)
        """
        # directory which keop old profile
        home = os.path.split(userHome)[0]
        if os.path.exists(pathTemplates):
            if self.clVars.Get("cl_profile_all_set") == "on":
                osLinuxShort = "all"
            else:
                osLinuxShort = self.clVars.Get("os_linux_shortname")
            pathNewTemplate = os.path.join(pathTemplates, osLinuxShort)
            pathOldTemplate = os.path.join(pathTemplates, "."+osLinuxShort)
            if not os.path.exists(pathNewTemplate) and\
                os.path.exists(pathOldTemplate) and\
                os.listdir(pathOldTemplate):
                # move profile
                self.copyTemplateDir(pathOldTemplate,pathNewTemplate)
            if not os.path.exists(pathNewTemplate):
                # create directory for profile keeping
                os.mkdir(pathNewTemplate)
                os.chmod(pathNewTemplate, 0700)
            if os.path.exists(pathOldTemplate) and\
                not os.listdir(pathOldTemplate):
                os.rmdir(pathOldTemplate)
        return True

    def syncUser(self, userName, userHome, sync, homeTemplate, \
                 host="default"):
        """
        Sync user profile from server or to server
        """
        flagError = False
        execStr = ""
        skipPaths = self.clVars.Get("cl_sync_skip_path")
        if not skipPaths:
            self.printERROR(
                _("The variable 'cl_sync_skip_path' is empty")%userHome)
            return False
        deletePaths = self.clVars.Get("cl_sync_del_path")
        if not deletePaths:
            deletePaths = []
        excludePaths = " ".join(map(lambda x: '--exclude="/%s"'\
                                    %x.replace('"',"").replace("'",""),
                                    skipPaths + deletePaths))
        if sync == "login":
            if os.path.exists(userHome) and\
                os.path.exists(homeTemplate):
                filterPath = " ".join(map(lambda x: '--filter="P /%s"'\
                                        %x.replace('"',"").replace("'",""),
                                        skipPaths))
                execStr = '/usr/bin/rsync --delete-excluded --delete %s %s '\
                          '-rlptgo -x -v -v -v  %s/ %s/' \
                           %(excludePaths, filterPath, homeTemplate, userHome)
        elif sync == "logout":
            if os.path.exists(userHome) and os.listdir(userHome) and\
                os.path.exists(homeTemplate):
                execStr = '/usr/bin/rsync --delete-excluded --delete %s \
-rlptgo -x -v -v -v %s/ %s/'%(excludePaths, userHome, homeTemplate)
        else:
            raise ClientError(
                _("Method syncUser: option sync=%s incorrect")%str(sync))
        if execStr:
            host = "<i>" + host +"</i>"
            if sync == "login":
                rsync = RsyncProgressBar(\
                        _("Receiving the file list from %s") % host,
                        _("Downloading the user profile from %s") % host,
                        execStr,self)
            else:
                rsync = RsyncProgressBar(\
                        _("Sending the file list to %s") % host,
                        _("Uploading the user profile to %s") % host,
                        execStr,self)
            pathConfig = os.path.join(homeTemplate,
                                      self.pathConfig)
            # remove old ini file
            prevIniFile = os.path.join(homeTemplate,".calculate.ini")
            if os.path.exists(prevIniFile):
                os.remove(prevIniFile)
            # create home directory
            if not os.path.exists(pathConfig):
                self.createUserDirectory(pathConfig)
            numfiles = 0
            configFileName = os.path.join(homeTemplate, self.configFileDesktop)
            if sync == "login":
                # get rsync files
                try:
                    numfiles = \
                        iniParser(configFileName).getVar('rsync','files')
                    if numfiles is False:
                        if os.path.exists(configFileName):
                            os.remove(configFileName)
                        numfiles = 0
                    else:
                        numfiles = int(numfiles)
                except:
                    numfiles = 0
            rsync.maximum = numfiles
            if sync == "login":
                rsync.run()
            if sync == "logout":
                rsync.run()
                try:
                    if iniParser(configFileName).setVar('rsync',
                                                {'files':rsync.getFilesNum()}):
                        os.chmod(configFileName, 0600)
                        os.chown(configFileName,self.uid,self.gid)
                except:
                    pass
            rsync.close()
            if rsync.getExitCode() != 0:
                try:
                    if iniParser(configFileName).setVar(\
                            'rsync',{'exitcode':rsync.getExitCode()}):
                        os.chmod(configFileName, 0600)
                        os.chown(configFileName,self.uid,self.gid)
                except:
                    pass
                self.printERROR(rsync.getErrMessage())
                self.printERROR(_("Failed to execute rsync") + " " + \
                            str(sync) + " ...")
                return False
        # change permissions
        changeDirs = [userHome, homeTemplate]
        for changeDir in changeDirs:
            # get directory permissions
            mode = getModeFile(changeDir, mode="mode")
            # if permission wrong( not 0700) then change it
            if mode != 0700:
                os.chmod(changeDir,0700)
        return True

    def clearHomeDir(self, homeDir):
        """Clear home directory"""
        rmFiles = list(set(os.listdir(homeDir))-\
                    set(self.skipHomeFile))
        for rmFile in rmFiles:
            delFile = os.path.join(homeDir,rmFile)
            if os.path.islink(delFile):
                os.unlink(delFile)
            elif os.path.isfile(delFile):
                os.remove(delFile)
            elif os.path.isdir(delFile):
                if not removeDir(delFile):
                    return False
            elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]):
                os.remove(delFile)
        return True

    def syncLoginProfile(self, host, homeDir, homeProfile,
                         flagClearHomeDir=True):
        """
        Get user profile from server
        """
        # if currect server has any files then sync it
        if filter(lambda x: not x in ('.calculate',), os.listdir(homeProfile)):
            if not self.syncUser(self.userName, homeDir, "login",
                                 homeProfile, host=host):
                return False
        # remove home directory on workstation
        else:
            if flagClearHomeDir:
                # clean home directory
                if not self.clearHomeDir(homeDir):
                    return False
        return True

    def getDataInConfig(self, section, listVars, objConfig):
        """
        Read variables listVars from section in configuration file
        """
        varsConfig = {}
        for varName in listVars:
            varsConfig[varName] = objConfig.getVar(section,varName)
        if objConfig.getError():
            return False
        return varsConfig

    def foundArchFile(self,strCurrentTime,archPathProcess,
                      archPathSuccess, remoteServer=False):
        """Found user profile archive"""
        self.startTask(_("Packing the archive on the server"),
                        progress=True)
        for sleeptime in [0.1,0.2,0.5]:
            # archive in packing process found
            if os.path.exists(archPathProcess) or \
                os.path.exists(archPathSuccess):
                break
            time.sleep(sleeptime)
        else:
            # archive not found
            self.endTask(False)
            return False

        # wait finish packing
        if os.path.exists(archPathProcess) or \
            os.path.exists(archPathSuccess):
            while os.path.exists(archPathProcess) or \
                os.path.exists(archPathSuccess):
                for waittime in [0.5,1,2]:
                    if os.path.exists(archPathSuccess):
                        self.endTask()
                        return True
                    try:
                        startSize = os.stat(archPathProcess).st_size
                        time.sleep(waittime)
                        if startSize != os.stat(archPathProcess).st_size:
                            break
                    except OSError as e:
                        if os.path.exists(archPathSuccess):
                            self.endTask()
                            return True
                # run out of time size change
                else:
                    # archive size was not change above 4s => archive not found
                    self.endTask(False)
                    return False
        self.endTask(False)
        return False

    def fileReader(self, fileName, stdin, maxSize):
        """
        Read file by block
        """
        fd = os.open(fileName, os.O_RDONLY)
        currSize = 0
        # default buffer size
        buffSize=131072
        dataBlock = os.read(fd, buffSize)
        while dataBlock:
            currSize += len(dataBlock)
            self.setProgress(currSize*100/maxSize)
            stdin.write(dataBlock)
            dataBlock = os.read(fd, buffSize)
        stdin.close()
        os.close(fd)

    def moveArch(self, srcArchFile, dstArchFile, remoteServer=False, mode=0600):
        """
        Move archive from one directory to other
        """
        class copyTo(color_print):
            def __init__(self, destFile):
                self.FD = open(destFile, "w")
                os.chmod(destFile, mode)
            def write(self, data):
                self.FD.write(data)
            def close(self):
                self.FD.close()
        try:
            copyToObj = copyTo(dstArchFile)
        except:
            raise ClientError(_("Failed to create file '%s'")%dstArchFile)
        archiveSize = os.stat(srcArchFile).st_size or 1
        try:
            self.fileReader(srcArchFile, copyToObj, archiveSize)
        except:
            raise ClientError(_("Failed to copy '%(from)s' -> '%(to)s'")\
                            %{'from':srcArchFile,
                              'to':dstArchFile})

    def unpackArch(self, homeDir, archFile, remoteServer=False):
        """
        Unpack archive into user home directory
        """
        archiveSize = os.stat(archFile).st_size
        execStr = "tar -C '%s' -xzf -" %homeDir
        # run unpacking process
        pipe = subprocess.Popen(execStr,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                close_fds=True,
                                env=os.environ, shell=True)
        # read file into pipe
        self.fileReader(archFile, pipe.stdin,archiveSize)
        ret = pipe.wait()
        pipe.stdout.close()
        pipe.stderr.close()
        if ret:
            self.printWARNING(_("Failed to execute %s") %execStr)
            self.printWARNING(_("Failed to unpack %s") %archFile)
            return False
        return True

    def setVarToConfig(self, nameSection, varsDict, configFileName):
        """Записывает переменную в файл конфигурации"""
        # Создаем директорию для конфигурационных файлов
        pathConfig = os.path.split(configFileName)[0]
        if not os.path.exists(pathConfig):
            self.createUserDirectory(pathConfig)
        try:
            if iniParser(configFileName).setVar(nameSection, varsDict):
                os.chmod(configFileName, 0600)
                os.chown(configFileName,self.uid,self.gid)
        except:
            return False
        return True

    def convertDate(self,strdate,dateformat="%Y-%m-%d %H:%M:%S"):
        """
        Convert date from string format (dateformat) to stuct or None
        """
        if strdate:
            try:
                return time.strptime(strdate,dateformat)
            except ValueError:
                pass
        return None

    def getDateObjClientConf(self, fileConfig):
        """
        Return time object from config file .calculate/desktop.env
        """
        if os.path.exists(fileConfig):
            objConfig = iniParser(fileConfig)
            data = self.getDataInConfig("main", ["date", "date_logout"],
                                        objConfig)
            timeLogout = data["date_logout"]
            timeConfig = data["date"]
            dates = filter(None,
                    [self.convertDate(timeLogout),
                     self.convertDate(timeConfig)])
            if dates:
                return dates[0]
        return False

    def createUserDirectory(self,userdir):
        """
        Create directory with user permissions
        """
        if not os.path.exists(userdir):
            try:
                os.mkdir(userdir)
                os.chown(userdir, self.uid, self.gid)
                os.chmod(userdir,0700)
            except OSError:
                raise ClientError(_("Error creating the directory")+
                                  _("Permission denied: '%s'")%userdir)

    def checkNeedSync(self,rpath,curTimeObj,curStatusSync,osLinuxShort):
        """
        Check about need sync with domain profile by profile time
        """
        # upgrade user profile or create need profile directory
        self.upgradeUserProfile(self.userName, self.homeDir, rpath)
        # profile directory
        fileConfig = os.path.join(self.homeDir, self.configFileServer)
        pathProfile = os.path.join(rpath, osLinuxShort)
        if readFile(fileConfig).strip():
            return True,pathProfile
        fileSoftConfigThis = os.path.join(pathProfile,
                                      self.configFileSoft)
        fileSoftConfigCur = os.path.join(self.homeDir,
                                      self.configFileSoft)
        xSessionCur = iniParser(fileSoftConfigCur).getVar('main','xsession')
        xSessionThis = iniParser(fileSoftConfigThis).getVar('main','xsession')
        # check profile date on current server
        fileConfigThis = os.path.join(pathProfile,
                                      self.configFileDesktop)
        if iniParser(fileConfigThis).getVar('main','status_sync') == "success":
            self.setVarToConfig("main", {"status_sync":"success_mount"},
                                fileConfigThis)
        thisTimeObj = self.getDateObjClientConf(fileConfigThis)
        if curStatusSync == "success_logout" and \
            xSessionCur == xSessionThis and \
            thisTimeObj and curTimeObj and \
            curTimeObj >= thisTimeObj:
            return False,pathProfile
        return True,pathProfile

    def mountLocalDomainRes(self,userPwd,dictRes):
        """
        Mount all local samba resource
        """
        res = True
        configFileName = os.path.join(self.homeDir, self.configFileDesktop)
        for name in dictRes.keys():
            if name == "remote_profile":
                continue
            rpath = dictRes[name]["path"]
            res = dictRes[name]["resource"]
            # create user directory
            self.createUserDirectory(rpath)
            # is directory already mount
            if isMount(rpath):
                continue
            else:
                if self.mountSleepRes(self.domain, userPwd,
                                      res, rpath) is False:
                    if name == "profile":
                        self.printWARNING(_("Failed to mount user profile"))
                        res = False
                        continue
                    else:
                        raise ClientError(
                            _("Failed to mount Samba resource [%s]")%res)
                elif name == "profile":
                    # upgrade user profile or create need profile directory
                    self.upgradeUserProfile(self.userName, self.homeDir, rpath)
        else:
            return res
        return False

    def syncLocalDomainRes(self,userPwd,dictRes,curTimeObj,curStatusSync,
                           osLinuxShort,fromRemote=False):
        """
        Sync profile with current server

        fromRemote - method called by remote sync method
        """
        if self.mountLocalDomainRes(userPwd,dictRes):
            if not self.sync: return True
            if "profile" in dictRes:
                rpath = dictRes["profile"]["path"]
                needSync,lDomainProfile = self.checkNeedSync(rpath, curTimeObj,
                                                    curStatusSync,osLinuxShort)
                if not needSync:
                    if fromRemote:
                        self.printSUCCESS(_("Local user profile does not need "
                             "to update with local domain"))
                    else:
                        self.printSUCCESS(_("Local user profile will be used"))
                    # Copy private files from the server
                    self.copyPrivateFiles(lDomainProfile, self.homeDir)
                    return True
                else:
                    # sync profile with current server
                    return self.syncLoginProfile(self.domain,self.homeDir,
                                                 lDomainProfile)
        return False

    def _getRemoteServerProfileByArch(self,dictRes,rHomeProfile,
                                      dateDefaultTemplate,userPwd,curTimeObj,
                                      curStatusSync,remoteServer,
                                      osLinuxShort):
        """
        Try get profile from remote server
        """
        # current time
        currTime = str(float(time.time()))
        archPathSuccess,archPathProcess,archPathSrc,archPathDst = \
            (None,None,None,None)
        try:
            rpath = dictRes["remote_profile"]["path"]
            res = dictRes["remote_profile"]["resource"]
            # create command for create incremental arch
            fileConfig = \
                os.path.join(rHomeProfile,self.configFileServer)
            varsConfig = {"arch_date":dateDefaultTemplate,
                          "curr_time":currTime}
            try:
                self.setServerCommand(["pack"], varsConfig, fileConfig)
            except ClientError as e:
                return False
            rFileConfig = os.path.join(rHomeProfile, self.configFileDesktop)
            self.setVarToConfig("main", {"status_sync":"success"},
                            rFileConfig)
            self.umountSleepPath(rpath)

            if not self.syncLocalDomainRes(userPwd,dictRes,curTimeObj,
                                       curStatusSync,osLinuxShort,
                                       fromRemote=True):
                return False

            if self.mountSleepRes(remoteServer, userPwd, res, rpath) is False:
                return False
            self.setVarToConfig("main", {"status_sync":"success_mount"},
                            rFileConfig)
            extSuccess = "gz"
            extProcess = "process"
            strCurrentTime = currTime.replace(".","_")
            archPathTmp = os.path.join(rpath, "profile.%s.%s.tar"\
                           %(osLinuxShort, strCurrentTime))
            # result archive file name
            archPathSuccess = "%s.%s"%(archPathTmp,extSuccess)
            # archive in pack process
            archPathProcess = "%s.%s"%(archPathTmp,extProcess)
            # find archive file
            if not self.foundArchFile(strCurrentTime, archPathProcess,
                                      archPathSuccess,
                                      remoteServer):
                self.printWARNING(_("Failed to find profile archive from %s")%
                            remoteServer)
                return False
            if os.path.exists(archPathSuccess):
                archPathSrc = archPathSuccess
                archPathDst = ".".join(filter(lambda x: x!="/",
                                       archPathSuccess.rpartition("/")))
                # get archive
                self.startTask(_("Copying archive from the server"),
                       progress=True)
                try:
                    self.moveArch(archPathSrc, archPathDst,
                                     remoteServer)
                except ClientError as e:
                    self.printWARNING(str(e))
                    return False
                self.endTask()
                # unpack archive into home directory
                self.startTask(_("Unpack profile archive"),progress=True)
                if not self.unpackArch(self.homeDir, archPathDst,
                                       remoteServer):
                    self.printERROR(_("Failed to unpack archive"))
                    return False
                # get removed files list
                pathListFile = os.path.join(rHomeProfile,
                                            self.listTemplFile)
                if not self.removeFilesInProfile(self.homeDir,pathListFile):
                    self.printWARNING(_("Unable to remove needless files"))
                    return False
                self.endTask()
                return True
            return False
        finally:
            for rmFile in (archPathSuccess,archPathProcess,
                           archPathSrc,archPathDst):
                if rmFile and os.path.exists(rmFile):
                    os.remove(rmFile)

    def syncRemoteDomainRes(self,userPwd,dictRes,remoteServer,curTimeObj,
                            curStatusSync,osLinuxShort):
        """
        Sync if actual profile is kept on a remote server
        """
        # mount directories path
        if not "remote_profile" in dictRes:
            return False
        if not self.sync:
            self.mountLocalDomainRes(userPwd,dictRes)
            return True
        rpath = dictRes["remote_profile"]["path"]
        res = dictRes["remote_profile"]["resource"]
        # create user directory
        self.createUserDirectory(rpath)
        # mount remote profile
        if isMount(rpath):
            self.printWARNING(_("Remote profile already mounted"))
            return False
        else:
            if self.mountSleepRes(remoteServer, userPwd, res, rpath) is False:
                return False

        try:
            rpath = dictRes["remote_profile"]["path"]
            needSync,pathRProfile = self.checkNeedSync(rpath,curTimeObj,
                                                       curStatusSync,osLinuxShort)
            if not needSync:
                # Copy private files from the server
                self.printSUCCESS(_("Local user profile will be used"))
                if not self.mountLocalDomainRes(userPwd,dictRes):
                    return False
                self.copyPrivateFiles(pathRProfile, self.homeDir)
                return True
            localServer = self.domain
            # mount local samba resources
            if self.mountLocalDomainRes(userPwd,dictRes):
                lpath = dictRes["profile"]["path"]
                homeLProfile = os.path.join(lpath, osLinuxShort)
                if not os.path.exists(homeLProfile):
                    homeLProfile = os.path.join(lpath, "." + osLinuxShort)
                lFileConfig = os.path.join(homeLProfile, self.configFileDesktop)
                # get data from desktop config on success run get by archive
                if os.path.exists(lFileConfig):
                    objConfig = iniParser(lFileConfig)
                    data = self.getDataInConfig("main", ["status_sync",
                                                         "date","date_logout"],
                                                         objConfig)
                    if data:
                        status = data.get("status_sync")
                        date = data.get("date")
                        date_logout = data.get("date_logout")
                        if date and date_logout:
                            diffdate = \
                                abs(time.mktime(self.convertDate(date))-
                                   time.mktime(self.convertDate(date_logout)))
                        else:
                            diffdate = None
                        # if diff date less 10 min
                        if diffdate is None or diffdate < 600:
                            if date and "success" in status:
                                if self._getRemoteServerProfileByArch(dictRes,
                                            pathRProfile,date,userPwd,
                                            curTimeObj, curStatusSync,
                                            remoteServer, osLinuxShort):
                                    return True
                        else:
                            self.printWARNING(
                                _("Difference time between server and "
                                  "client more 10 minute"))
                            self.printWARNING(_("Profile synchronization "
                                "will be used without archiving"))
            # sync local before remote sync
            self.syncLocalDomainRes(userPwd,dictRes,curTimeObj,
                                       curStatusSync,osLinuxShort,
                                       fromRemote=True)
            # get user profile from remote domain
            return self.syncLoginProfile(remoteServer, self.homeDir,
                                         pathRProfile, False)
        finally:
            # unmount remote profile
            if self.sync and "remote_profile" in dictRes:
                if isMount(rpath):
                    if not self.umountSleepPath(rpath):
                        raise ClientError(_("Failed to unmount remote profile"))
                # remote directory for remote
                if os.path.exists(rpath) and not os.listdir(rpath):
                    os.rmdir(rpath)

    def errorUmountUserRes(self,error):
        """
        Post action for umount user res and write status_sync=error
        """
        self.closeClTemplate()
        if error and self.homeDir:
            umountResult = self.umountUserRes(self.homeDir)
            configFileName = os.path.join(self.homeDir, self.configFileDesktop)
            uid = self.clVars.Get('ur_uid')
            gid = self.clVars.Get('ur_gid')
            self.setVarToConfig("main", {"status_sync":"error"},
                                configFileName)
            self.printERROR(_("Failed to get user profile from domain"))
            return umountResult
        return True

    def initEnv(self):
        """
        Init object variables
        """
        try:
            self.uid = int(self.clVars.Get('ur_uid'))
            self.gid = int(self.clVars.Get('ur_gid'))
        except ValueError as e:
            raise ClientError(_("Failed to determinate UID and GID"))
        self.sync = self.clVars.Get('cl_client_sync') == 'on'
        self.userName = self.clVars.Get("ur_login")
        self.homeDir = self.clVars.Get('ur_home_path')
        self.domain = self.clVars.Get("cl_remote_host")

    @safetyWrapper(native_errors=(TemplatesError,ClientError),
                   man_int=__("Manually interrupted"),
                   post_action=errorUmountUserRes)
    def mountUserResAndSync(self,dv):
        """Mount user resources and sync profile"""
        self.initVars(dv)
        self.initEnv()
        if self.domain:
            foundMountRemote = isMount("/var/calculate/remote")
            if not foundMountRemote:
                self.mountRemote();
        # check on two session user login
        if self.isTwoSessionsUser(self.userName):
            return False
        hostAuth = self.clVars.Get("os_remote_auth")

        # if not domain
        if not hostAuth or not self.domain:
            self.printSUCCESS(_("The local profile will be used"))
            return True

        try:
            passwdUsers = map(lambda x: x[0],
                          map(lambda x: x.split(':'),
                          map(lambda x: x.strip(),
                          open("/etc/passwd").readlines())))
        except:
            self.printERROR(_("Failed to open /etc/passwd"))
            return False
        if self.userName in passwdUsers:
            try:
                pwdObj = pwd.getpwnam(self.userName)
            except:
                raise ClientError(_("Failed to found user %s")%self.userName)
            self.printWARNING(_("User information from /etc/passwd is used"))
            return True
        # check for domain workstation and [remote] was mounted
        if not self.isDomain():
            raise ClientError(_("The computer is not in the domain"))
        # user config filename
        configFileName = os.path.join(self.homeDir, self.configFileDesktop)
        # user time object from config file
        currentTimeObj = self.getDateObjClientConf(configFileName)
        currentStatusSync = self.getSyncStatus(self.homeDir)
        # create home directory if it is not exists
        if not os.path.exists(self.homeDir):
            os.makedirs(self.homeDir)
            os.chown(self.homeDir,self.uid,self.gid)
            os.chmod(self.homeDir,0700)
        # get local date and statusSync
        # write into config status "process"
        self.setVarToConfig("main", {"status_sync":"process"}, configFileName)
        # get user password from key kernel
        userPwd = getKey(self.userName)
        if not userPwd or userPwd == "XXXXXXXX":
            raise ClientError(_("User password not found"))
        # user cache
        if not self.cAddUserToCache(self.userName, userPwd):
            self.printWARNING(_("Unable cache user info"))
        # profile name
        if self.clVars.Get("cl_profile_all_set") == "on":
            osLinuxShort = "all"
        else:
            osLinuxShort = self.clVars.Get("os_linux_shortname")
        # if replication is on
        replOn = self.ldapDataObj.isRepl()
        # remote domain name
        if replOn:
            remoteServer = self.ldapDataObj.getNameRemoteServer(
                                self.userName, osLinuxShort, self.domain)
        else:
            remoteServer = ""
        # get mount directory
        dictRes = self.getUserMountResources(self.userName, self.homeDir,
                                                    remoteServer)
        if isMount(dictRes["profile"]["path"]):
            self.sync = False
        useRemoteProfile = remoteServer and replOn
        profileServer = self.domain
        # if profile on remote server
        if useRemoteProfile:
            if not self.syncRemoteDomainRes(userPwd,dictRes,
                            remoteServer,currentTimeObj,
                            currentStatusSync,osLinuxShort):
                syncStatus = "error"
                useRemoteProfile = False
                self.printWARNING(
                    _("Error synchronizing with the remote server %s")
                                %remoteServer)
            else:
                syncStatus = "success"
                profileServer = remoteServer
        # if profile not remote or remote sync is failed
        if not useRemoteProfile:
            if self.syncLocalDomainRes(userPwd,dictRes,currentTimeObj,
                                       currentStatusSync,osLinuxShort):
                # if not fallback for remote sync
                if not(remoteServer and replOn):
                    syncStatus = "success"
            else:
                syncStatus = "error"
                self.printWARNING(
                    _("Error synchronizing with the remote server %s")
                                %self.domain)
        if self.sync:
            self.setVarToConfig("main", {"status_sync":syncStatus},
                            configFileName)
        # unpack link from link arch
        self.unpackLinks(self.homeDir)

        self.printSUCCESS(_("Mounted user resource of the domain"))
        if self.sync:
            if syncStatus == "error":
                if remoteServer and replOn:
                    self.printWARNING(
                        _("Get a user fallback profile from the %s domain")
                          %profileServer)
                self.printWARNING(
                    _("Changings in the user profile will not "
                      "be saved to the domain"))
            else:
                self.printSUCCESS(_("Get a user profile from the %s domain")%
                              profileServer)
        return True

    def isSessionUser(self, userName):
        """
        Check for user in X session
        """
        reFoundUser = re.compile("%s\s+:\d+\s+"%(userName))
        resWho = self.execProg("who")
        if resWho:
            if any(reFoundUser.search(x) for x in resWho):
                return True
        return False

    def tarSymLinks(self,userHome,uid,gid):
        """Create tar archive of symlinks"""
        linkArch = pathJoin(userHome,".calculate/links.tar.bz2")
        try:
            for filename in tarLinks(userHome,linkArch,
                                 skip=self.clVars.Get("cl_sync_del_path")+
                                 self.clVars.Get("cl_sync_skip_path")):
                try:
                    os.unlink(filename)
                except OSError:
                    self.printWARNING(_("Failed to remove %s")%filename)
        except:
            self.printWARNING(_("Failed to make links archive"))
        try:
            if os.path.exists(linkArch):
                os.chown(linkArch,uid,gid)
        except:
            self.printWARNING(_("Failed to make links archive"))

    def unpackLinks(self,userHome):
        """Unpack archive of symlinks"""
        linksArch = pathJoin(userHome,".calculate/links.tar.bz2")
        try:
            if os.path.exists(linksArch):
                tf = tarfile.open(linksArch)
                tf.extractall(userHome)
                tf.close()
        except:
            self.printWARNING(_("Failed to unpack links archive"))

    def moveHomeDir(self, userHome):
        """
        Move non profile files in root of home profile to Home/Moved
        """
        pathProg = os.getcwd()
        os.chdir(userHome)
        dirs = []
        files = []
        movedLink = os.path.join('Moved')
        movedPath = os.path.join('Home',"Moved")
        skipPaths = self.clVars.Get("cl_moved_skip_path")
        if not skipPaths:
            skipPaths = ['Disks','Share','Home','Moved','FTP','Desktop']
        filesAndDir = filter(lambda x: not ('.' in x[0] or x in\
            skipPaths or os.path.islink(x)),
            os.listdir('.'))
        filesDir = []
        for fd in filesAndDir:
            if os.path.islink(fd):
                os.unlink(fd)
            else:
                filesDir.append(fd)
        # find files in Desktop
        pathDesktop = './Desktop'
        filesDirDesk = []
        if os.path.exists(pathDesktop):
            filesDirDesk = filter(lambda x: not os.path.islink(x) and\
                not os.path.split(x)[1].startswith('.') and\
                not x.rpartition('.')[2]=='desktop',
                map(lambda x: os.path.join(pathDesktop,x),\
                    os.listdir(pathDesktop)))
        movedPathDesk = os.path.join(movedPath, pathDesktop)
        filesDir += filesDirDesk
        if not filesDir or not os.path.exists("Home"):
            # remote empty Moved folder
            if os.path.exists(movedPath) and not os.listdir(movedPath):
                os.rmdir(movedPath)
            # remote link to Moved in home directory
            if os.path.islink(movedLink) and not os.path.exists(movedPath):
                os.unlink(movedLink)
            return True
        if not os.path.exists(movedPath):
            os.mkdir(movedPath)
        directFile = os.path.join(movedPath,".directory")
        if not os.path.exists(directFile):
            txt = "[Desktop Entry]\nIcon=folder-development"
            fd = os.open(directFile, os.O_CREAT)
            os.close(fd)
            FD  = open (directFile, "r+")
            FD.write(txt)
            FD.close()
        if not os.path.exists(movedLink):
            os.symlink(movedPath, movedLink)
        for fd in filesDir:
            execStr = "cp -r '%s' '%s'" %(fd, movedPath)
            textLine = self.execProg(execStr)
            if textLine is False:
                self.printERROR(_("Failed to execute") + " " + str(execStr))
                return False
            execStr = "rm -rf '%s'" %fd
            textLine = self.execProg(execStr)
            if textLine is False:
                self.printERROR(_("Failed to execute") + " " + str(execStr))
                return False
        os.chdir(pathProg)
        return True

    def removeNoiseFiles(self, userHome):
        """
        Remove files which hinder dm
        """
        noiseFiles = []
        for nsFile in noiseFiles:
            rmFile = os.path.join(userHome, nsFile)
            if os.path.exists(rmFile):
                os.remove(rmFile)
        return True

    def getPrivateFiles(self, userHome):
        """
        Get all private files relative user home directory
        """
        privateFiles = []
        for privateHomeDir in self.privateDirs:
            privateDir = os.path.join(userHome,privateHomeDir)
            if os.path.isdir(privateDir):
                # .ssh files relative user home directory
                privateFiles += map(lambda x:os.path.join(privateHomeDir,x),
                                    filter(lambda x:\
                                    os.path.isfile(os.path.join(privateDir,x)),
                                    os.listdir(privateDir)))
        return self.privateFiles + privateFiles

    def removePrivateFiles(self, userHome):
        """
        Remove private files
        """
        privateFiles = self.getPrivateFiles(userHome)
        for prFile in privateFiles:
            rmFile = os.path.join(userHome, prFile)
            if os.path.exists(rmFile):
                os.remove(rmFile)
        return True

    def copyPrivateFiles(self, serverProfileDir, userHomeDir):
        """
        Copy privates files from server to home directory
        """
        privateFiles = self.getPrivateFiles(serverProfileDir)
        for prFile in privateFiles:
            src = os.path.join(serverProfileDir, prFile)
            dst = os.path.join(userHomeDir, prFile)
            if os.path.exists(src):
                dstPath = os.path.dirname(dst)
                if not os.path.exists(dstPath):
                    listElPath = []
                    for el in filter(lambda x: x,
                                  dstPath.partition(userHomeDir)[2].split("/")):
                        listElPath.append(el)
                        joinPath = "/".join(listElPath)
                        dPath = os.path.join(userHomeDir, joinPath)
                        if not os.path.exists(dPath):
                            sPath = os.path.join(serverProfileDir, joinPath)
                            sMode, sUid, sGid = getModeFile(sPath)
                            os.mkdir(dPath,sMode)
                            os.chown(dPath, sUid, sGid)
                copy2(src, dst)
                sUid, sGid = getModeFile(src, mode="owner")
                os.chown(dst, sUid, sGid)
        return True

    def scanDirectory(self, scanDir, listFiles, skipPath=[], prefix=False,
                       flagDir=False):
        """
        Generate file/directory list
        """
        if not prefix:
            prefix = os.path.join(scanDir,"")
        if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]):
            for fileOrDir in os.listdir(scanDir):
                absPath = os.path.join(scanDir,fileOrDir)
                relPath = absPath.split(prefix)[1]
                if relPath in skipPath:
                    continue
                listFiles.append(relPath)
                stInfo = os.lstat(absPath)
                statInfo = stInfo[stat.ST_MODE]
                if stat.S_ISDIR(statInfo):
                    self.scanDirectory(absPath, listFiles,
                                       skipPath, prefix, True)

    def getListFilesTemplate(self, homeDir):
        """
        Generation file list in user home directory, exclude mount dirs
        """
        home = os.path.join(homeDir, "")
        execStr = "mount"
        textLines = self.execProg(execStr)
        # skip directories in scanning
        skipPaths = []
        if textLines:
            for line in textLines:
                if home in line:
                    skipPath =\
                    line.partition(home)[2].rpartition(" type")[0]
                    skipPaths.append(skipPath)
        # file list in user profile
        listFiles = []
        if not skipPaths:
            self.printERROR(_("Mounting point for server resources not found"))
            return False
        self.scanDirectory(homeDir, listFiles, skipPaths)
        return listFiles

    def removeFilesInProfile(self, homeDir, pathListFile):
        """
        Remove files which is absend in user profile
        """
        # read file list from config
        try:
            filesTemplateTxt = open(pathListFile).read()
        except:
            return False
        listFilesTemplate = filter(lambda x:  x.strip(),
                                  filesTemplateTxt.split("\n"))
        filesTemplate = set(listFilesTemplate)
        # get files in home directory
        listFilesHome = self.getListFilesTemplate(homeDir)
        if listFilesHome is False:
            return False
        filesHome = set(listFilesHome)
        filesRemove = list(filesHome - filesTemplate)
        filesRemove.sort(lambda x, y: cmp(len(y), len(x))) 
        rmPath = ""
        try:
            for rmFile in filesRemove:
                rmPath = os.path.join(homeDir, rmFile)
                if os.path.islink(rmPath):
                    os.unlink(rmPath)
                elif os.path.isfile(rmPath):
                    os.remove(rmPath)
                elif os.path.isdir(rmPath):
                    os.rmdir(rmPath)
                else:
                    os.remove(rmPath)
        except:
            return False
        return True

    def getRunCommandsWithEnv(self):
        """List run program"""
        def getCmd(procNum):
            cmdLineFile = '/proc/%s/cmdline'%procNum
            environFile = '/proc/%s/environ'%procNum
            try:
                if os.path.exists(cmdLineFile) and \
                   os.path.exists(environFile):
                    return (open(cmdLineFile,'r').read().strip(),
                            open(environFile,'r').read().strip())
            except:
                pass
            return ("","")
        if not os.access('/proc',os.R_OK):
            return []
        return map(getCmd,
               filter(lambda x:x.isdigit(),
               os.listdir('/proc')))


    def clearUserKey(self, userName):
        """
        Clear user key from kernel
        """
        # find user key in kernel and check on relogout
        if getKey(userName) and not \
            filter(lambda x:"xdm/xdm\x00--login" in x[0] and \
               ("USER=%s"%userName) in x[1],self.getRunCommandsWithEnv()):
            # clean
            ret = clearKey(userName)
            if ret == 0:
                return True
            else:
                self.printERROR(_("Failed to clear the kernel key for user %s")\
                                %userName)
                return False
        return True

    @safetyWrapper(native_errors=(TemplatesError,ClientError),
                   man_int=__("Manually interrupted"),
                   post_action=errorUmountUserRes)
    def umountUserResAndSync(self, dv):
        """
        Umount user resouces and synchronizatoin user profile
        """
        self.initVars(dv)
        self.initEnv()
        try:
            passwdUsers = map(lambda x: x[0],
                              map(lambda x: x.split(':'),
                                  map(lambda x: x.strip(),
                                      open("/etc/passwd").readlines())))
        except:
            self.printERROR(_("Failed to open /etc/passwd"))
            return False
        if not os.path.exists(self.homeDir):
            self.printERROR(_("User home directory %s not found")%self.homeDir)
            return False
        currentDateStr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        # domain workstation work in local mode
        if self.userName in passwdUsers:
            configFileName = os.path.join(self.homeDir, self.configFileDesktop)
            self.setVarToConfig("main", {"date_logout":currentDateStr},
                                configFileName)
            self.printWARNING(_("User information from /etc/passwd is used"))
            self.printSUCCESS(_("The local profile will be used"))
            return True
        hostAuth = self.clVars.Get("os_remote_auth")
        # local mode
        if not hostAuth or not self.domain:
            self.printSUCCESS(_("The local profile will be used"))
            return True
        # check for domain workstation and [remote] was mounted
        if not self.isDomain():
            raise ClientError(_("The computer is not in the domain"))
        # check is user in X session, do not umount user resources
        if self.isSessionUser(self.userName):
            self.printERROR(_("User %s is already in X session")%self.userName)
            self.printERROR(
                _("Failed to unmount user %s resource")%self.userName)
            return False
        # user package config
        configFileName = os.path.join(self.homeDir, self.configFileDesktop)
        self.setVarToConfig("main", {"date_logout":currentDateStr},
                            configFileName)
        if os.path.exists(self.homeDir):
            self.moveHomeDir(self.homeDir)
            if self.sync:
                self.tarSymLinks(self.homeDir,self.uid,self.gid)
        else:
            raise ClientError(_("Directory %s not found") % self.homeDir)
        # get status sync
        exitStr = iniParser(configFileName).getVar('main','status_sync')
        # check on error or net full executed -> process
        if exitStr == "process" or exitStr == "error":
            self.sync = False
        if self.clVars.Get("cl_profile_all_set") == "on":
            osLinuxShort = "all"
        else:
            osLinuxShort = self.clVars.Get("os_linux_shortname")
        # get mount resources
        dictRes = self.getUserMountResources(self.userName, self.homeDir,False)
        pathUnix = dictRes["profile"]["path"]
        homeProfile = os.path.join(pathUnix, osLinuxShort)
        if not isMount(pathUnix):
            self.printERROR(_("Mounted remote resources for user %s not found")%
                            self.userName)
            self.umountUserRes(self.homeDir)
            return False
        try:
            if self.sync:
                # sync profiles local to domain
                if not self.syncUser(self.userName, self.homeDir, "logout",
                                              homeProfile,host=self.domain):
                    return False
        finally:
            # remove files, which hinder correct dm work
            self.removeNoiseFiles(self.homeDir)
            # remove private files
            self.removePrivateFiles(self.homeDir)
            # clean user password from root keys
            self.clearUserKey(self.userName)
            # umount user res
            if not self.umountUserRes(self.homeDir):
                return False

        # remove empty directories
        for name in dictRes.keys():
            rpath = dictRes[name]["path"]
            if os.path.exists(rpath) and not os.listdir(rpath):
                try:
                    os.rmdir(rpath)
                except:
                    self.printERROR(_("Failed to remove dir %s")% path)
                    return False
        # remove Disks directory if it empty
        pathDisks = os.path.join(self.homeDir,"Disks")
        if os.path.exists(pathDisks) and not os.listdir(pathDisks):
            try:
                os.rmdir(pathDisks)
            except:
                self.printERROR(_("Failed to remove dir %s")% pathDisks)
                return False
        if self.sync:
            self.printSUCCESS(_("User profile saved in the domain"))
            self.setVarToConfig("main", {"status_sync":"success_logout"},
                                configFileName)
        self.printSUCCESS(_("Domain user resource unmounted"))
        return True

    def getMountUserPaths(self, homeDir=False):
        """
        Get users mount paths
        """
        if not homeDir:
            userName = self.clVars.Get("ur_login")
            try:
                homeDir = pwd.getpwnam(userName).pw_dir
            except:
                homeDir = os.path.join("/home",userName)
        dirStart, dirEnd = os.path.split(homeDir)
        mountTemplateDir = os.path.join(dirStart, ".%s" %dirEnd)
        mountRemoteTemplateDir = os.path.join(dirStart, ".%s.remote" %dirEnd)
        return filter(lambda x: x.startswith(homeDir) or\
                      x.startswith(mountTemplateDir) or\
                      x.startswith(mountRemoteTemplateDir),
                      map(lambda x: x.split(" ")[1],\
                          open("/proc/mounts").readlines()))

    def execProg(self, cmdStrProg, inStr=False, envProg={}):
        """
        Execute external program
        """
        env_path = {"PATH":getpathenv()}
        env = {}
        env.update(os.environ.items() + env_path.items() + envProg.items())
        retCode,programOut = runOsCommand(cmdStrProg,in_str=inStr,env_dict=env)
        if not retCode:
            return programOut
        return False

    def umountSleepPath(self, rpath):
        """
        Unmount path, sleep by failed and repeat
        """
        # check for mount
        if isMount(rpath):
            for waittime in [0,0.5,1,2]:
                time.sleep(waittime)
                if not self.execProg("umount %s"%rpath) is False \
                    or not isMount(rpath):
                    if not isMount(rpath):
                        return True
            self.execProg("fuser -km %s"%rpath)
            for waittime in [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]:
                time.sleep(waittime)
                if not self.execProg("umount %s"%rpath) is False \
                    or not isMount(rpath):
                    if not isMount(rpath):
                        return True
            else:
                if isMount(rpath):
                    raise ClientError(_("Failed to unmount path %s")%rpath)
        return True

    def umountUserRes(self, homeDir=False, removeEmpty=False):
        """
        Unmount user directories
        """
        res = True
        for umountPath in self.getMountUserPaths(homeDir):
            try:
                self.umountSleepPath(umountPath)
            except ClientError as e:
                self.printERROR(str(e))
                res = False
                continue
            if os.path.exists(umountPath) and not os.listdir(umountPath):
                os.rmdir(umountPath)
        return res

    def getDefaultRunlevelDaemons(self):
        """
        Get all daemons from default runlevel
        """
        execStr = "rc-update show"
        textLine = self.execProg(execStr)
        if textLine is False:
            self.printERROR(_("ERROR") + ": " + execStr)
            return False
        else:
            splLines = filter(lambda x: len(x)==2 and "default" in x[1],\
                              map(lambda x: x.split("|"),textLine))
            splLines = map(lambda x: x[0].strip(), splLines)
            return splLines

    def delDaemonAutostart(self, daemon):
        """
        Remove daemon from autorun
        """
        defaultDaemons = self.getDefaultRunlevelDaemons()
        if defaultDaemons is False:
            return False
        if daemon in defaultDaemons:
            execStr = "rc-update del %s default" %daemon
            textLine = self.execProg(execStr)
            if textLine is False:
                self.printERROR(_("ERROR") + ": " + execStr)
                self.printERROR(_("Failed to delete from the default runlevel"))
                return False
            else:
                return True
        return True

    def delDomain(self):
        """
        Remove from domain
        """
        pathRemote = "/var/calculate/remote"
        foundMountRemote = isMount(pathRemote)
        domain = self.clVars.Get("cl_remote_host")
        if foundMountRemote:
            textLineUmount = self.umountSleepPath(pathRemote)
            if not textLineUmount:
                return False
        if not domain:
            self.printWARNING(_("The computer is not in the domain"))
            return True
        # remove domain vars from env files
        self.removeVars()
        # apply templates for undomain
        self.clVars.Set("cl_action", "undomain", True)
        if not self.applyTemplatesFromSystem():
            self.printERROR(_("Failed to apply undomain templates"))
            return False
        # Delete LDAP users from system and clear cache
        if not self.cDelLdapSysUsersAndClearCache():
            self.printERROR(_("Failed to clear user cache"))
            return False
        # restart dbus
        self.restartDBus()
        if not self.delDaemonAutostart("client"):
            self.printERROR(
                _("Failed to remove client service from autostart"))
            return False
        self.printSUCCESS(_("Computer removed from domain %s")%domain + " ...")
        return True

    def getUserPassword(self, pwDialog=False):
        """Получить пароль у пользователя

        pwDialog  - приглашение ввода пароля
        """
        if os.readlink('/proc/self/fd/0') == '/dev/console':
            os.system('chvt 1 &>/dev/null')
        if not pwDialog:
            pwDialog = _("Password")
        userPwd = getpass.getpass(pwDialog+":")
        return userPwd

    def addDaemonAutostart(self, daemon):
        """Прописывает демона в автозагрузку"""
        defaultDaemons = self.getDefaultRunlevelDaemons()
        if defaultDaemons is False:
            return False
        if daemon in defaultDaemons:
            return True
        execStr = "rc-update add %s default" %daemon
        textLine = self.execProg(execStr)
        if textLine is False:
            self.printERROR(_("ERROR") + ": " + execStr)
            self.printERROR(_("Failed to add to the default runlevel"))
            return False
        else:
            return True

    def restartDBus(self):
        """Restart D-Bus service"""
        dbusDaemon = 'rc-service -i dbus'
        existsDaemon = 'rc-service -e dbus'
        # if exists dbus and running
        if os.system(existsDaemon) == 0 and \
            os.system(dbusDaemon + ' status &>/dev/null') == 0:
            # restart
            if os.system(dbusDaemon + ' restart -- -s &>/dev/null') != 0:
                self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...")
                return False
        return True

    def getInfoService(self, service, option, envFile=False):
        """
        Get service parameter from env
        """
        if not envFile:
            if self.convObj == None:
                # file /var/calculate/remote/server.env
                envFile = self.clVars.Get("cl_env_server_path")
                if os.access(envFile, os.R_OK):
                    self.convObj = False
                elif os.access("/var/calculate/remote/calculate.env", os.R_OK):
                    self.convObj = convertEnv()
            if self.convObj:
                return self.convObj.getVar(service, option)
        if not self.optionsInfo:
            if not envFile:
                # file /var/calculate/remote/server.env
                envFile = self.clVars.Get("cl_env_server_path")
            optInfo = self.clVars.GetRemoteInfo(envFile)
            if optInfo is False:
                return False
            if optInfo:
                self.optionsInfo = optInfo
        value = ''
        if service in self.optionsInfo and option in self.optionsInfo[service]:
                value = self.optionsInfo[service][option]
        return value

    @safetyWrapper(native_errors=(TemplatesError,ClientError),
                   man_int=__("Manually interrupted"))
    def domain(self,dv):
        self.initVars(dv)
        if self.clVars.Get('cl_client_mount_set') == 'on':
            return self.mountRemote()
        else:
            if self.clVars.Get('cl_localhost_set') == "on":
                return self.delDomain()
            else:
                return self.addDomain()

    def addDomain(self):
        """
        Add to domain
        """
        domainName = self.clVars.Get('cl_remote_host_new')
        remoteHost = self.clVars.Get("cl_remote_host") 
        netDomain = self.clVars.Get("os_net_domain")
        # get domain name
        if "." in domainName:
            domain = domainName
        else:
            domain = "%s.%s" %(domainName,netDomain)
        # check domain by ping
        for i in range(0,1):
            try:
                Pinger().ping(domain,1000,16)
                break
            except IPError as e:
                pass
        else:
            self.printERROR(_("Server %s does not respond")%domain)
            return False
        reFoundHostSamba = re.compile("Server=\[Samba.+\]")
        resSmbClient = self.execProg("smbclient -N -L %s" %domain)
        if not resSmbClient is False:
            for string in resSmbClient:
                if reFoundHostSamba.search(string):
                    foundHostSamba = True
                    break
        if not foundHostSamba:
            self.printERROR(_("Samba server not found in %s")%domain)
            return False
        if remoteHost and self.clVars.Get('cl_localhost_set') != 'on':
            self.printERROR(_("The computer is already in the domain %s")\
                              %remoteHost)
            self.printWARNING(_("Before joining the domain, "
                "you need to remove it from the previous domain"))
            self.printWARNING(_("Run 'cl-client -r'"))
            return False

        foundMountRemote = isMount("/var/calculate/remote")
        if foundMountRemote:
            self.printERROR(_("Samba resource [%s] mounted")%\
                                "remote" + " ...")
            return False
        else:
            def passwdQueue():
                remotePw = self.clVars.Get('cl_remote_pw')
                if remotePw:
                    yield remotePw
                if remotePw:
                    self.printERROR(_("Wrong password"))
                yield self.askPassword(\
                        _("Domain password for the desktop"),False)

            pathRemote = "/var/calculate/remote"
            for pwdRemote in passwdQueue():
                if not os.path.exists(pathRemote):
                    os.makedirs(pathRemote)
                if not self.mountSambaRes(domain,"client",pwdRemote,
                                   0,0,"remote",pathRemote) is False:
                    self.printSUCCESS(
                        _("Samba resource [%s] mounted")%"remote")
                    self.clVars.Write("cl_remote_host", domain, True, "local")
                    self.clVars.Write("cl_remote_pw", pwdRemote, True, "local")
                    break
            else:
                self.printERROR(
                    _("Failed to mount Samba resource [%s]")%"remote")
                return False
        servDn = self.getInfoService("ldap", "services_dn")
        unixDn = self.getInfoService("unix", "dn")
        bindDn = self.getInfoService("unix", "bind_dn")
        bindPw = self.getInfoService("unix", "bind_pw")
        # check info from server
        if not (servDn and unixDn and bindDn and bindPw):
            self.printERROR(_("Info not found on the server") + ": " +\
                _("services DN or unix DN or bind DN or bind password"))
            return False
        self.clVars.Set("cl_action", "domain", True)
        self.clVars.Set("os_remote_auth", domain)
        if not self.applyTemplatesFromSystem():
            self.printERROR(
                _("Failed to apply install templates or domain templates"))
            return False
        # restart dbus
        self.restartDBus()
        if not self.addDaemonAutostart("client"):
            return False
        # write domain to config
        self.clVars.Write("os_remote_auth", domain, True)
        # write current program version
        currentVersion = self.clVars.Get("cl_ver")
        self.clVars.Write("os_remote_client", currentVersion, True)
        self.printSUCCESS(_("Computer added to domain %s")%domain)
        return True

    def relevanceTemplates(self, hostAuth):
        """
        Determine relevance appling templates by program version
        """
        # ''  or hostname
        if hostAuth != self.clVars.Get("os_remote_auth"):
            return False
        if self.clTempl:
            self.closeClTemplate()
        self.clTempl = ProgressTemplate(self.setProgress,self.clVars,
                                        cltObj=True)
        # current program version
        currentVersion = self.clVars.Get("cl_ver")
        # version of program which appled templates
        previousVersion = self.clVars.Get("os_remote_client")
        cVersion,pVersion = self.clTempl._convertVers(currentVersion,previousVersion)
        if cVersion != pVersion:
            return False
        return True

    def closeClTemplate(self):
        if self.clTempl:
            if self.clTempl.cltObj:
                self.clTempl.cltObj.closeFiles()
            self.clTempl.closeFiles()
            self.clTempl = None


    def applyRelevanceTemplates(self, hostAuth=""):
        """
        Appling relevance templates
        """
        if not self.relevanceTemplates(hostAuth):
            if hostAuth:
                # add to domain
                self.clVars.Set("cl_action","domain",True)
            else:
                # del from domain
                self.clVars.Set("cl_action","undomain",True)
            self.clVars.Set("os_remote_auth", hostAuth)
            # apply system templates
            dirsAndFiles = self.applyTemplatesFromSystem()
            if not dirsAndFiles:
                if hostAuth:
                    self.printERROR(_("Failed to apply domain templates"))
                else:
                    self.printERROR(_("Failed to apply undomain templates"))
                return False
            if hostAuth:
                self.printSUCCESS(_("Templates set for network mode"))
                currentVersion = self.clVars.Get("cl_ver")
                self.clVars.Write("os_remote_client", currentVersion, True)
                self.clVars.Write("os_remote_auth", hostAuth, True)
            else:
                self.printSUCCESS(_("Templates set for local mode"))
                self.clVars.Delete("os_remote_auth")
                self.clVars.Delete("os_remote_client")
        return True

    def cDelLdapSysUsersAndSyncCache(self):
        """Delete LDAP users from system and synchronize cache"""
        cacheObj = userCache()
        if not cacheObj.deleteCacheUsersFromSystem():
            return False
        if not cacheObj.syncCacheToLdap():
            return False
        return True

    def cAddCacheUsersFromSystem(self):
        """Add cache users from system"""
        cacheObj = userCache()
        if not cacheObj.addCacheUsersFromSystem():
            return False
        return True

    def cAddUserToCache(self, userName, userPwd):
        """Add user to cache"""
        cacheObj = userCache()
        pwdHash = self.getHashPasswd(userPwd, "shadow_ssha256")
        if not cacheObj.addUserToCache(userName, pwdHash):
            return False
        return True

    def cDelLdapSysUsersAndClearCache(self):
        """Delete LDAP users from system and clear cache"""
        cacheObj = userCache()
        if not cacheObj.deleteCacheUsersFromSystem():
            return False
        if not cacheObj.clearCache():
            return False
        return True

    def moveHomeDirs(self):
        """Move home dirs /var/calculate/client-home -> /home

        if user in cache
        """
        cacheObj = userCache()
        loginUsersData = cacheObj.getLoginDomainUsers()
        if loginUsersData is False:
            return False
        previousHome = "/var/calculate/client-home"
        if isMount(previousHome):
            return True
        if os.path.exists(previousHome):
            flagMovedUsers = False
            for userName,x,uid,gid,gecos,directory,shell in loginUsersData:
                homeDir = directory
                pathUserList = filter(lambda x: x, directory.split('/'))
                if not pathUserList:
                    continue
                pathUser = "/".join(pathUserList[1:])
                srcDir = pathJoin(previousHome, pathUser)
                if os.path.exists(srcDir) and not os.path.exists(homeDir):
                    flagMovedUsers = True
                    destDir = os.path.dirname(homeDir)
                    self.printWARNING(_("Moved %(src)s to %(dest)s")\
                                      %{"src":srcDir,"dest":homeDir})
                    if not self.copyTemplateDir(srcDir, destDir):
                        return False
            if flagMovedUsers and not os.listdir(previousHome):
                os.rmdir(previousHome)
        return True

    def mountRemote(self):
        """
        Mount remote domain resource for domain workstation.

        Alsa add to domain if found hostname and password
        """
        domain = self.clVars.Get("cl_remote_host")
        if domain:
            foundMountRemote = isMount("/var/calculate/remote")
        else:
            self.printWARNING(_("This computer is not in the domain"))
            if not self.applyRelevanceTemplates():
                return False
            return True
        if foundMountRemote:
            self.printWARNING(_("Samba resource [%s] mounted")%"remote" + \
                              " ...")
            # apply domain templates
            if domain:
                self.clVars.flIniFile()
                if not self.applyRelevanceTemplates(domain):
                    return False
                # Delete LDAP users from system and synchronize cache
                if not self.cDelLdapSysUsersAndSyncCache():
                    return False
            return True
        else:
            pathRemote = "/var/calculate/remote"
            pwdRemote = self.clVars.Get("cl_remote_pw")
            if not (domain and pwdRemote):
                self.printERROR(_("Variable not found")+\
                        ": cl_remote_pw ...")
                return False
            if not os.path.exists(pathRemote):
                os.makedirs(pathRemote)
            if self.mountSambaRes(domain,"client",pwdRemote,
                               0,0,"remote",pathRemote) is False:
                self.printWARNING(_("Failed to mount Samba resource [%s]")%\
                                    "remote" + " ...")
                beforeRemoteAuth = self.clVars.Get('os_remote_auth')
                # apply templates if current up-to-date
                if not self.applyRelevanceTemplates():
                    return False
                if not self.cAddCacheUsersFromSystem():
                    return False
                if not self.moveHomeDirs():
                    return False
                if beforeRemoteAuth != self.clVars.Get('os_remote_auth'):
                    self.restartDBus()
                return True
            self.printSUCCESS(_("Samba resource [%s] mounted") % "remote" +\
                                " ...")
        # apply domain templates
        if domain:
            self.clVars.flIniFile()
            beforeRemoteAuth = self.clVars.Get('os_remote_auth')
            if not self.applyRelevanceTemplates(domain):
                return False
            # Delete LDAP users from system and synchronize cache
            if not self.cDelLdapSysUsersAndSyncCache():
                return False
            if beforeRemoteAuth != self.clVars.Get('os_remote_auth'):
                self.restartDBus()
        return True

    @safetyWrapper(native_errors=(TemplatesError,ClientError),
                   man_int=__("Manually interrupted"))
    def clientPasswd(self,dv):
        """
        Change user password on server
        """
        self.initVars(dv)
        self.initEnv()

        # check on root
        if self.uid == 0:
            self.printERROR(_("The user is root"))
            self.printWARNING(\
                _("The program can be executed for a non-root user only"))
            return False

        password = self.clVars.Get('ur_user_new_pw')
        curPassword = self.clVars.Get('ur_user_pw')
        varsConfig = {"unix_hash":self.getHashPasswd(password,"ssha"),
                      "samba_lm_hash":self.getHashPasswd(password,"lm"),
                      "samba_nt_hash":self.getHashPasswd(password,"nt"),
                      "samba_nt_hash_old":self.getHashPasswd(curPassword,"nt")}
        if filter(lambda x: not x, varsConfig.values()):
            raise ClientError(_("Failed to change password"))
        # ~/.calculate/server.env
        fileConfig = os.path.join(self.homeDir, self.configFileServer)
        if not self.setServerCommand(["passwd_samba"], varsConfig, fileConfig,
                                    self.uid,self.gid):
            raise ClientError(_("Failed to change password"))
        self.printSUCCESS(_("%s's password changed")%
                            self.clVars.Get('ur_login'))
        self.printSUCCESS(_("The password will be changed when you log "
                            "out from the X session"))
        return True
