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

# Copyright 2010-2013 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 os import path

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 (getModeFile, removeDir,
                                       isMount, readFile, pathJoin, tarLinks,
                                       listDirectory,process,find,STDOUT,
                                       sambaPasswordCheck,checkUtils)
from calculate.lib.utils.common import (getpathenv, appendProgramToEnvFile,
                                        removeProgramToEnvFile)
from calculate.desktop._cl_keys import getKey, clearKey
from calculate.lib.convertenv import convertEnv
from calculate.lib.encrypt import encrypt
from client_cache import userCache
from shutil import copy2
from socket import gethostbyname
import tarfile
from collections import OrderedDict
from calculate.desktop.desktop import Desktop

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 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, Desktop):
    """
    Клиентский модуль работы с доменом
    """

    # 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 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)
        return True

    def mountSambaRes(self,host,userName,userPwd,res,rpath,uid=None,gid=None):
        """
        Подключить samba ресурс
        """
        mountCmd = checkUtils('/bin/mount')
        if not uid is None:
            # mount by uid
            p = process(mountCmd,"-t","cifs","-o",
                "cache=loose,user=%s,uid=%d,gid=%d,noperm"%(userName,uid,gid),
                "//%s/%s"%(host,res),rpath,
                envdict={"PASSWD":userPwd},stderr=STDOUT)
            return p.success()
        else:
            p = process(mountCmd,"-t","cifs","-o",
                "cache=loose,user=%s"%userName,
                "//%s/%s"%(host,res),rpath,
                envdict={"PASSWD":userPwd},stderr=STDOUT)
            return p.success()

    def mountSleepRes(self,host,userName,userPwd,res,rpath,uid=None,gid=None):
        """
        Подключить пользовательский ресурс
        """
        for waittime in [0.5,2,5]:
            if not self.mountSambaRes(host,userName,userPwd,res,rpath,
                                  uid,gid):
                # check on mount path
                if isMount(rpath):
                    if not self.umountSleepPath(rpath):
                        return False
                # wait
                time.sleep(waittime)
            else:
                return True
        return False

    def syncUser(self, uid, gid, userHome, sync, remoteProfile,
                 host="default",skipList=[]):
        """
        Синхронизация профиля пользователя
        """
        flagError = False
        execStr = ""
        skipPaths = self.clVars.Get("cl_sync_skip_path")
        if not skipPaths:
            self.printERROR(
                _("Variable 'cl_sync_skip_path' 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 + skipList))
        if sync == "login":
            if os.path.exists(userHome) and\
                os.path.exists(remoteProfile):
                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, remoteProfile, userHome)
        elif sync == "logout":
            if os.path.exists(userHome) and os.listdir(userHome) and\
                os.path.exists(remoteProfile):
                execStr = '/usr/bin/rsync --delete-excluded --delete %s \
-rlpt -x -v -v -v %s/ %s/'%(excludePaths, userHome, remoteProfile)
        else:
            raise ClientError(
                _("Method syncUser: option sync=%s is incorrect")%str(sync))
        if execStr:
            host = "<i>" + host +"</i>"
            if sync == "login":
                rsync = RsyncProgressBar(\
                        _("Receiving the file list from %s") % host,
                        _("Fetching 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(remoteProfile,
                                      self.pathConfig)
            # remove old ini file
            prevIniFile = os.path.join(remoteProfile,".calculate.ini")
            if os.path.exists(prevIniFile):
                os.remove(prevIniFile)
            # create home directory
            if not os.path.exists(pathConfig):
                self.createUserDirectory(pathConfig,uid,gid)
            numfiles = 0
            configFileName = os.path.join(remoteProfile, 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,uid,gid)
                except:
                    pass
            rsync.close()
            try:
                if iniParser(configFileName).setVar(\
                        'rsync',{'exitcode':rsync.getExitCode()}):
                    os.chmod(configFileName, 0600)
                    os.chown(configFileName,uid,gid)
            except:
                pass
            if rsync.getExitCode() != 0:
                self.printERROR(rsync.getErrMessage())
                self.printERROR(_("Failed to execute rsync") + " " + \
                            str(sync))
                return False
        # change permissions
        changeDirs = [userHome, remoteProfile]
        for changeDir in filter(path.exists,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):
        """
        Очистить домашнюю директорию
        """
        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)
        self.printSUCCESS(_("Cleaning the local user profile"))
        return True

    def syncLoginProfile(self,host,uid,gid,homeDir,resourceName,
                         profileName, clearHomeDir=True):
        """
        Получить профиль пользователя из домена
        """
        homeProfile = path.join(
            self.clVars.Select('cl_client_user_mount_path',
                               where='cl_client_user_mount_name',
                               eq=resourceName,limit=1),
            profileName)
        # if currect server has any files then sync it
        if any(not x in ('.calculate') for x in listDirectory(homeProfile)):
            if not self.syncUser(uid, gid, homeDir, "login",
                                 homeProfile, host=host):
                return False
        # remove home directory on workstation
        else:
            if clearHomeDir:
                # clean home directory
                if not self.clearHomeDir(homeDir):
                    return False
        return True

    def syncLogoutProfile(self,host,uid,gid,homeDir,resourceName,
                         profileName,skipList):
        """
        Закачать профиль пользователя в домен
        """
        homeProfile = path.join(
            self.clVars.Select('cl_client_user_mount_path',
                               where='cl_client_user_mount_name',
                               eq=resourceName,limit=1),
            profileName)
        # if currect server has any files then sync it
        if listDirectory(homeDir):
            if not path.exists(homeProfile):
                self.createUserDirectory(homeProfile,uid,gid)
            if not self.syncUser(uid, gid, homeDir, "logout",
                                 homeProfile, host=host,skipList=skipList):
                return False
        return True

    def strftime(self,timeobj,dateformat="%Y-%m-%d %H:%M:%S"):
        return time.strftime(dateformat,timeobj)

    def packRemote(self, resourceName, localTime, currentTime, profileName,
                   uid, gid):
        """
        Запаковать удаленный профиль
        """
        remoteProfilePath = path.join(
                    self.clVars.Select('cl_client_user_mount_path',
                    where='cl_client_user_mount_name', eq=resourceName,
                    limit=1), profileName)
        fileServer = path.join(remoteProfilePath,
                    Client.configFileServer)
        if not localTime:
            return False
        varsConfig = {"arch_date":self.strftime(localTime),
                      "curr_time":currentTime}

        self.setServerCommand(["pack"],varsConfig,fileServer)
        fileDesktop = path.join(remoteProfilePath,
                    Client.configFileDesktop)
        self.setVarToConfig(uid, gid, "main", {"status_sync":"success"},
                            fileDesktop)
        return self.umountRemoteUserRes(False, resourceName)

    def waitingArchFile(self, packTime, profileName, resourceName):
        """
        Ожидание архива профиля от домена
        """
        archPathSuccess,archPathProcess = \
            self._getArchNames(packTime,profileName,resourceName)
        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:
            return False

        # wait finish packing
        if path.exists(archPathProcess) or \
            path.exists(archPathSuccess):
            while path.exists(archPathProcess) or \
                path.exists(archPathSuccess):
                for waittime in [0.5,1,2]:
                    if path.exists(archPathSuccess):
                        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 path.exists(archPathSuccess):
                            return True
                else:
                    return False
        return False

    def unpackArch(self, homeDir, packTime, profileName, resourceName):
        """
        Распаковать архив с профилем
        """
        def fileReader(fileName, stdin, maxSize):
            """
            Прочитать файл по блокам
            """
            self.addProgress()
            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)

        archFile,drop = self._getArchNames(packTime,profileName,resourceName)
        archiveSize = os.stat(archFile).st_size
        #os.system('tar tf %s'%archFile)
        execStr = "tar -C '%s' -xzf -" %homeDir
        # запустить процесс распаковки
        pipe = subprocess.Popen(execStr,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                close_fds=True,
                                env=os.environ, shell=True)
        # прочитать в конвеер
        fileReader(archFile, pipe.stdin, archiveSize)
        ret = pipe.wait()
        pipe.stdout.close()
        pipe.stderr.close()
        if ret:
            return False
        removedFileList = path.join(
                self.clVars.Select('cl_client_user_mount_path',
                where='cl_client_user_mount_name',
                eq=resourceName,limit=1),profileName,
                self.listTemplFile)
        return self.removeFilesInProfile(homeDir,removedFileList)

    def cleanArchs(self, packTime, profileName, resourceName):
        """
        Удалить архивы использованные для синхронизации пользовательского
        профиля
        """
        for rmFile in filter(lambda x:x and path.exists(x),
                      self._getArchNames(packTime,profileName,resourceName)):
            os.remove(rmFile)
        return True

    def _getArchNames(self,packTime,profileName,resourceName):
        """
        Получить имена файлов архивов по времени, профилю и ресурсу
        """
        remoteProfile = self.clVars.Select('cl_client_user_mount_path',
                               where='cl_client_user_mount_name',
                               eq=resourceName,limit=1)
        extSuccess = "gz"
        extProcess = "process"
        packTime = packTime.replace(".","_")
        archPathTmp = os.path.join(remoteProfile, "profile.%s.%s.tar"
                       %(profileName, packTime))
        # result archive file name
        archPathSuccess = "%s.%s"%(archPathTmp,extSuccess)
        # archive in pack process
        archPathProcess = "%s.%s"%(archPathTmp,extProcess)
        return archPathSuccess,archPathProcess

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

    def createUserDirectory(self,userdir,uid,gid):
        """
        Создать директорию с правами пользователя
        """
        if not os.path.exists(userdir):
            try:
                os.mkdir(userdir)
                os.chown(userdir, uid, gid)
                os.chmod(userdir,0700)
            except OSError:
                raise ClientError(_("Error creating the directory")+". "+
                                  _("Permission denied: '%s'")%userdir)
        return True

    def mountUserDomainRes(self,userName,userPwd,uid,gid,*resources):
        """
        Mount all local samba resource
        """
        res = True
        for rpath,res,host in \
            self.clVars.Select(['cl_client_user_mount_path',
                'cl_client_user_mount_resource',
                'cl_client_user_mount_host'],
                where='cl_client_user_mount_name',
                _in=resources):
            if not rpath:
                continue
            self.createUserDirectory(rpath,uid,gid)
            if isMount(rpath):
                continue
            else:
                if not self.mountSleepRes(host, userName, userPwd,
                                      res, rpath,uid,gid):
                    raise ClientError(
                        _("Failed to mount Samba resource [%s]")%res)
        else:
            return True

    def setSyncStatus(self,userDir,uid,gid,status):
        """
        Установить статус синхронизации
        """
        # сохранение текущего статуса перед изменением
        self.clVars.Get('cl_client_sync_status')
        fileDesktop = path.join(userDir,Client.configFileDesktop)
        return self.setVarToConfig(uid, gid, "main",
                            {"status_sync":status}, fileDesktop)

    def tarSymLinks(self,uid,gid,userHome,delPath,skipPath):
        """
        Создать tar архив с симлинками
        """
        linkArch = pathJoin(userHome,".calculate/links.tar.bz2")
        symList = []
        try:
            tarLinks(userHome,linkArch, skip=delPath+skipPath)
            if path.exists(linkArch):
                os.chown(linkArch,uid,gid)
        except Exception as e:
            return False
        return True

    def unpackLinks(self,userHome):
        """
        Распаковать архив с симлинками
        """
        linksArch = path.join(userHome,".calculate/links.tar.bz2")
        try:
            if os.path.exists(linksArch):
                tf = tarfile.open(linksArch)
                tf.extractall(userHome)
                tf.close()
        except Exception as e:
            return False
        return True

    def moveHomeDir(self, userHome, movedDir, resourceName, skipPaths):
        """
        Переместить непрофильные файлы из корня домашней директории
        в Home/Moved
        """
        desktopDir = "Desktop"
        resourcePath = self.clVars.Select('cl_client_user_mount_path',
                       where='cl_client_user_mount_name', eq=resourceName,
                       limit=1)
        if not isMount(resourcePath):
            raise ClientError(_("Unable to mount %s")%resourcePath)
        movedPath = path.join(resourcePath, movedDir)
        movedLink = path.join(userHome,movedDir)
        dirs = []
        files = []
        if not skipPaths:
            skipPaths = ['Disks','Share','Home','Moved','FTP','Desktop']
        filesAndDir = filter(lambda x: not (
                             path.islink(x) or 
                             x[len(userHome)+1:].startswith('.') or 
                             x[len(userHome)+1:] in skipPaths),
                      listDirectory(userHome,fullPath=True))
        filesDir = []
        for fd in filesAndDir:
            if os.path.islink(fd):
                os.unlink(fd)
            else:
                filesDir.append(fd)
        # find files in Desktop
        pathDesktop = path.join(userHome,desktopDir)
        filesDirDesk = filter(lambda x: (not path.islink(x) and
            not path.split(x)[1].startswith('.') and
            not x.rpartition('.')[2]=='desktop'),
            listDirectory(pathDesktop,fullPath=True))
        movedPathDesk = os.path.join(movedPath, desktopDir)
        filesDir += filesDirDesk
        if not filesDir or not path.exists(movedPath):
            # remove empty Moved folder
            if path.exists(movedPath) and not listDirectory(movedPath):
                os.rmdir(movedPath)
            # remove link to Moved in home directory
            if path.islink(movedLink) and not path.exists(movedPath):
                os.unlink(movedLink)
            return True
        if not path.exists(movedPath):
            os.mkdir(movedPath)
        directFile = path.join(movedPath,".directory")
        if not path.exists(directFile):
            with open(directFile,'w') as f:
                f.write("[Desktop Entry]\nIcon=folder-development")
        if not path.exists(movedLink):
            os.symlink(movedPath[len(userHome)+1:], movedLink)
        cpCmd, rmCmd = checkUtils('/bin/cp','/bin/rm')
        for fd in filesDir:
            if process(cpCmd,'-xr',fd,movedPath).failed():
                self.printERROR(_("Failed to copy {ffrom} to {fto}")
                    .format(ffrom=fd,fto=movedPath))
                return False
            if process(rmCmd,'-rf','--one-file-system',fd).failed():
                self.printERROR(_("Failed to remove "%fd))
                return False
        return True

    def removeNoiseFiles(self, userHome):
        """
        Remove files which hinder dm
        """
        noiseFiles = []
        for nsFile in noiseFiles:
            rmFile = 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 getListFilesProfile(self, homeDir):
        """
        Generation file list in user home directory, exclude mount dirs
        """
        lHomeDir = len(homeDir)+1
        return map(lambda x:x[lHomeDir:],
               find(homeDir,onefilesystem=True))

    def removeFilesInProfile(self, homeDir, pathListFile):
        """
        Удалить файлы которые отсутствуют в пользовательском профиле
        """
        # read file list from config
        try:
            filesProfileTxt = open(pathListFile).read()
        except:
            return False
        listFilesProfile = filter(lambda x:  x.strip(),
                                  filesProfileTxt.split("\n"))
        filesProfile = set(listFilesProfile)
        # get files in home directory
        listFilesHome = self.getListFilesProfile(homeDir)
        if listFilesHome is False:
            return False
        filesHome = set(listFilesHome)
        filesRemove = list(filesHome - filesProfile)
        rmPath = ""
        try:
            for rmFile in sorted(filesRemove,reverse=True):
                rmPath = os.path.join(homeDir, rmFile)
                if path.ismount(rmPath):
                    continue
                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 Exception as e:
            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

    def setLogoutDate(self,homeDir,uid,gid):
        """
        Установить дату выхода из сеанса
        """
        configFileName = path.join(homeDir,self.configFileDesktop)
        currentDateStr = self.strftime(time.localtime())
        return self.setVarToConfig(uid, gid, "main",
                                  {"date_logout":currentDateStr},
                                  configFileName)

    def umountRemoteUserRes(self,removeEmpty,*resourceNames):
        """
        Отключить пользовательский ресурс
        """
        retRes = True
        for resourceName in resourceNames:
            resourcePath = self.clVars.Select('cl_client_user_mount_path',
                        where='cl_client_user_mount_name', eq=resourceName,
                        limit=1)
            if resourcePath:
                result = self.umountSleepPath(resourcePath)
                retRes &= result
                if (result and removeEmpty and path.exists(resourcePath) and
                    not listDirectory(resourcePath)):
                    os.rmdir(resourcePath)
        return retRes

    def umountUserRes(self,umountPaths):
        """
        Отключить пользовательские ресурсы
        """
        for umountPath in umountPaths:
            if not self.umountSleepPath(umountPath):
                return False
        self.umountRemoteUserRes(True,
            *self.clVars.Get('cl_client_user_mount_name'))
        return True

    def getDefaultRunlevelDaemons(self):
        """
        Получить все службы из default уровня загрузки
        """
        rcUpdateCmd = checkUtils("/sbin/rc-update")
        p = process(rcUpdateCmd,"show")
        if p.success():
            # получить список названии служб default уровня
            return map(lambda x:x[0].strip(),
                   # [\s*<название_службы>\s*,\s*default\s*]
                   filter(lambda x:len(x)>1 and "default" in x[1],
                   # [\s*<название_службы>\s*,\s*уровень запуска\s*]
                   map(lambda x:x.split('|'),
                   # \s*<название_службы>\s*|\s*<уроверь запуска>?\s*
                   p.readlines())))
        else:
            raise ClientError(_("ERROR") + ": " + p.read())

    def delDaemonAutostart(self, daemon):
        """
        Удалить службу из автозапуска
        """
        rcUpdateCmd  = checkUtils('/sbin/rc-update')
        defaultDaemons = self.getDefaultRunlevelDaemons()
        if daemon in defaultDaemons:
            p = process(rcUpdateCmd,'del',daemon,'default')
            if p.success():
                return True
            else:
                self.printERROR(_("ERROR") + ": " + p.read())
                self.printERROR(_("Failed to delete from the default runlevel"))
                return False
        return True

    def addDaemonAutostart(self, daemon):
        """
        Добавить службу в автозагрузку
        """
        rcUpdateCmd  = checkUtils('/sbin/rc-update')
        if daemon in self.getDefaultRunlevelDaemons():
            return True
        p = process(rcUpdateCmd,'add',daemon,'default')
        if p.success():
            return True
        else:
            self.printERROR(_("ERROR") + ": " + p.read())
            self.printERROR(_("Failed to add to the default runlevel"))
            return False

    def getInfoService(self, service, option, envFile=False):
        """
        Получить параметр севирса из удаленного 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

    def checkDomainServer(self,domainName,netDomain):
        """
        Проверить указанный адрес на то, что он может быть доменом
        """
        # get domain name
        if "." in domainName:
            domain = domainName
        else:
            import socket
            domain = "%s.%s" %(domainName,netDomain)
            try:
                gethostbyname(domain)
            except socket.gaierror as e:
                domain = domainName
        # 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.+\]")
        smbClientCmd = checkUtils('/usr/bin/smbclient')
        p = process(smbClientCmd,"-N","-L",domain,stderr=STDOUT)
        if p.success() and any(reFoundHostSamba.search(x) for x in p):
            return True
        else:
            self.printERROR(_("Samba server not found in %s")%domain)
            return False

    def getDomainPassword(self,domain):
        """
        Получить пароль для ввода в домен
        """
        def passwdQueue():
            remotePw = self.clVars.Get('cl_remote_pw')
            if remotePw:
                yield remotePw
            if remotePw:
                self.printERROR(_("Wrong password"))
            # не запрашвать пароль при загрузке системы
            if not path.exists('/proc/self/fd/0') or \
                os.readlink('/proc/self/fd/0') != '/dev/console':
                yield self.askPassword(\
                        _("Domain password for the desktop"),False)
                self.printERROR(_("Wrong password"))

        pathRemote = "/var/calculate/remote"
        for pwdRemote in passwdQueue():
            pwdRemote = pwdRemote or ""
            if sambaPasswordCheck("client",pwdRemote,domain,"remote"):
                self.clVars.Set('cl_remote_pw',pwdRemote)
                return True
        return False

    def writeClientVars(self,domain,currentVersion,pwdRemote):
        """
        Записать переменные переменные клиента
        """
        # write domain to config
        self.clVars.Write("os_remote_auth", domain, True)
        # write current program version
        self.clVars.Write("os_remote_client", currentVersion, True)
        self.clVars.Write("cl_remote_host", domain, True, "local")
        self.clVars.Write("cl_remote_pw", pwdRemote, True, "local")
        return True

    def checkDomainInfo(self,domain):
        """
        Проверить наличие настроек на сервере для конфигурации LDAP
        """
        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):
            raise ClientError(_("Info not found on the server") + ": " +\
                _("services DN or unix DN or bind DN or bind password"))
        self.clVars.Set("os_remote_auth", domain)
        return True

    def applyClientTemplates(self, hostAuth=""):
        """
        Применить шаблоны клиента

        Установка события домен/не домен по удаленному хосту
        Установка переменной os_remote_auth, необходимой для корректного
        выполнения шаблонов
        Выполнение шаблонов
        Установка необходимых переменных в calculate.env
        """
        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
        self.applyTemplates(None,False)
        if hostAuth:
            self.printSUCCESS(_("The workstation is configured for work "
                              "in the domain"))
            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(_("The workstation is configured for work "
                              "outside the domain"))
            self.clVars.Delete("os_remote_auth")
            self.clVars.Delete("os_remote_client")
        return True

    def cDelLdapSysUsersAndSyncCache(self):
        """
        Удалить LDAP пользователей из системы (/etc/passwd и т.д.) и
        синхронизировать кэш
        """
        cacheObj = userCache()
        if not cacheObj.deleteCacheUsersFromSystem():
            return False
        if not cacheObj.syncCacheToLdap():
            return False
        return True

    def cAddCacheUsersFromSystem(self):
        """
        Добавить кэш пользователей из системы
        """
        cacheObj = userCache()
        if not cacheObj.addCacheUsersFromSystem():
            return False
        return True

    def cAddUserToCache(self, userName, userPwd):
        """
        Добавить пользователя в кэш
        """
        cacheObj = userCache()
        pwdHash = self.getHashPasswd(userPwd, "shadow_ssha256")
        if not cacheObj.addUserToCache(userName, pwdHash):
            return False
        return True

    def cDelLdapSysUsersAndClearCache(self):
        """
        Удалить LDAP пользователей из системы и очистить кэш
        """
        cacheObj = userCache()
        if not cacheObj.deleteCacheUsersFromSystem():
            return False
        if not cacheObj.clearCache():
            return False
        return True

    def mountRemoteRes(self,pwdRemote,pathRemote,*domains):
        """
        Подключить удаленный ресурс remote
        """
        # первый домен из списка
        domain = reduce(lambda x,y:x if x else y,domains,"")
        foundMountRemote = isMount(pathRemote)
        if foundMountRemote:
            self.printWARNING(_("Samba resource [%s] mounted")%"remote")
            return True
        else:
            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 not self.mountSambaRes(domain,"client",pwdRemote,
                               "remote",pathRemote):
                self.printERROR(
                    _("Failed to mount Samba resource [%s]")%"remote")
                return False
            self.printSUCCESS(_("Samba resource [%s] mounted") % "remote")
            return True

    def clientPasswd(self,userLogin,uid,gid,homeDir,password,curPassword):
        """
        Изменить пароль пользователя. Пароль пользователя меняется на сервере
        после выхода из сеанса
        """
        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()):
            return False
        # ~/.calculate/server.env
        fileConfig = os.path.join(homeDir, self.configFileServer)
        res = self.setServerCommand(["passwd_samba"], varsConfig, fileConfig,
                                    uid,gid)
        return res
