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

# Copyright 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 sys
from os import path
from calculate.core.server.func import Action,Tasks
from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate
from calculate.lib.utils.files import FilesError
from calculate.desktop.desktop import DesktopError
from calculate.client.client import ClientError
from calculate.lib.cl_template import TemplatesError
from calculate.lib.utils.files import isMount

setLocalTranslate('cl_client3',sys.modules[__name__])
__ = getLazyLocalTranslate(_)

class ClClientSyncLoginAction(Action):
    """
    Синхронизировать локальный профиль с удаленным, подключить удаленные
    ресурсы пользователя
    """
    native_error = (FilesError,ClientError,DesktopError,TemplatesError)

    successMessage = None
    failedMessage = None
    interruptMessage = __("Synchronization manually interrupted")

    tasks = [
         # подключить удаленный ресурс домена
         {'name':'mount_remote',
          'method':'Client.mountRemoteRes(cl_remote_pw,cl_client_remote_path,'
                   'cl_remote_host)',
          'condition':lambda Get: (Get('cl_remote_host') and 
                                   Get('os_remote_auth') and 
                                  not isMount(Get('cl_client_remote_path'))),
         },
         # check on domain user
         {'name':'domain_user',
          'condition':lambda Get: (Get('os_remote_auth') and 
                                   Get('cl_remote_host') and
                                   Get('desktop.ur_domain_set') == 'on'),
          'else_message':__("The local profile will be used")
         },
         {'name':'domain_user:create_home',
          'message':__("Creating the home directory for {ur_login}"),
          'method':'Client.createUserDirectory(ur_home_path,ur_uid,'
                         'ur_gid)',
          'condition':lambda Get:not path.exists(Get('ur_home_path'))
         },
         # password in kernel key
         {'name':'domain_user:check_password',
          'condition':lambda Get:Get('desktop.ur_password'),
          'else_error':__("User password not found")
         },
         {'name':'ecryptfs',
          'message':__("Mounting encrypted data"),
          'method':'Desktop.createCryptDir(ur_login,ur_uid,ur_gid,'
                                          'ur_home_path,True)',
          'condition':lambda Get:(Get('desktop.ur_home_crypt_set') == 'on' and
                      Get('install.cl_autologin') != Get('ur_login'))
         },
         {'name':'domain_user:add_to_cache',
          'essential':False,
          'method':'Client.cAddUserToCache(ur_login,desktop.ur_password)',
          'failed_warning':__("Unable to cache user info")
         },
         # подключить удаленные ресурсы пользователя
         {'name':'domain_user:mount_resources',
          'message':__("Mounting user resources"),
          'method':'Client.mountUserDomainRes(ur_login,'
                       'desktop.ur_password,'
                       'ur_uid,ur_gid,"unix","share","homes","ftp")',
         },
         # проверка на попытку открыть вторую сессию для этого пользователя
         {'name':'two_session',
          'error':
            __("A second X session cannot be opened for user {ur_login}."),
          'condition':lambda dv: (dv.Get('ur_login') in 
                                  dv.Get('desktop.cl_desktop_online_user') and
                          int(dv.Select('desktop.cl_desktop_online_count',
                                    where='desktop.cl_desktop_online_user',
                                    eq=dv.Get('ur_login'),limit=1) > 1) and 
                                 dv.Get('cl_client_sync') == 'on')
         },
         {'name':'domain_user:domain_sync',
          'method':'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"process")',
          'condition':lambda Get:Get('cl_client_sync') == 'on'
         },
         # подключить профиль пользователя на удаленном домене
         # если на нем находится актуальный профиль
         {'name':'domain_sync:repl_profile',
          'method':'Client.mountUserDomainRes(ur_login,desktop.ur_password,'
                       'ur_uid,ur_gid,"unix","remote_profile")',
          'condition':lambda Get:Get('cl_replication_host')
         },
         # отправить команду архивирования профиля на удаленном домене
         # если он новее локального профиля
         {'name':'repl_profile:pack_remote',
          'method':'Client.packRemote("remote_profile",'
                      'cl_client_local_sync_time,cl_client_pack_time,'
                      'cl_client_profile_name,ur_uid,ur_gid)',
          'condition':lambda Get:Get('cl_client_sync_replication_set') == 'on',
          'else_message':__("The local user profile does not "
                            "need to be synchronized with remote domain")
         },
         # монтируем профиль локального домена, если локальный профиль
         # старее удаленного доменного или актуальный профиль 
         {'name':'domain_sync:mount_local',
          'method':'Client.mountUserDomainRes(ur_login,desktop.ur_password,'
                       'ur_uid,ur_gid,"unix")',
          # нет более ранних ошибок и локальный профиль нуждается 
          # в синхронизации с удаленным или профиль на локальном домене
          'depend':(Tasks.success_all('mount_resources') & 
                    (Tasks.hasnot('repl_profile') |
                     Tasks.has('pack_remote')))
         },
         # синхронизируем с профилем локального домена
         {'name':'mount_local!:sync_local',
          'method':'Client.syncLoginProfile(cl_remote_host,ur_uid,'
                   'ur_gid,ur_home_path,"unix",cl_client_profile_name)',
          'condition':lambda Get:Get('cl_client_sync_local_set') == 'on',
          'else_message':__("The local user profile does not "
                            "need to be synchronized with local domain")
         },
         # ошибка синхронизации с локальным доменом
         {'name':'local_sync_error',
          'warning':__("Error synchronizing with the local server {cl_remote_host}"),
          'depend':Tasks.failed_one_of("mount_local","sync_local")
         },
         # подключить удаленный профиль пользователя с "репликации"
         {'name':'repl_profile:repeat_repl_profile',
          'method':'Client.mountUserDomainRes(ur_login,desktop.ur_password,'
                       'ur_uid,ur_gid,"remote_profile")',
         },
         # ждать архив от удаленного домена
         {'name':'pack_remote:wait_archive',
          'message': __("Packing the archive on the server"),
          'method':'Client.waitingArchFile(cl_client_pack_time,'
                            'cl_client_profile_name,"remote_profile")',
          'failed_warning':__("Failed to find the profile "
                              "archive from {cl_replication_host}")
         },
         # распаковать архив из удаленного домена и удалить 
         # файлы которые отсутствуют в удаленном профиле
         {'name':'wait_archive:unpack_profile',
          'message':__("Unpacking the profile"),
          'method':'Client.unpackArch(ur_home_path,cl_client_pack_time,'
                          'cl_client_profile_name,"remote_profile")',
          'failed_warning':__("Failed to unpack")
         },
         # удалить временные архивы
         {'name':'clean_archfiles',
          'method':'Client.cleanArchs(cl_client_pack_time,'
                         'cl_client_profile_name,"remote_profile")',
          'failed_warning':__("Unable to remove useless files"),
          'essential':False,
          'depend': Tasks.has('pack_remote')
         },
         # синхронизировать профиль с удаленным доменом в случае ошибки
         {'name':'repl_profile:sync_remote',
          'method':'Client.syncLoginProfile(cl_replication_host,ur_uid,'
                   'ur_gid,ur_home_path,"remote_profile",'
                   'cl_client_profile_name)',
          'depend':Tasks.failed_one_of('pack_remote','mount_local','sync_local',
                    'wait_archive','unpack_profile'),
          'condition':lambda Select:isMount(
                Select('cl_client_user_mount_path',
                where='cl_client_user_mount_name',eq='remote_profile',
                limit=1))
         },
         # если синхронизация с удаленным доменом прошла с ошибкой
         # синхронизировать локальный профиль с локальным доменом
         # как запасной профиль
         {'name':'pack_remote:fallback_warning',
          'warning':__("Error synchronizing with the remote server "
                       "{cl_replication_host}"),
          'depend': ~Tasks.success_one_of('unpack_profile','sync_remote')
         },
         {'name':'pack_remote:fallback_sync',
          'method':'Client.syncLoginProfile(cl_remote_host,ur_uid,'
                   'ur_gid,ur_home_path,"unix",cl_client_profile_name)',
          'depend': ~Tasks.success_one_of('unpack_profile','sync_remote')
         },
         # отключить профиль на удаленном домене
         {'name':'repl_profile!:umount_remote_profile',
          'method':'Client.umountRemoteUserRes(True,"remote_profile")',
         },
         # сообщение о том, что будет использоваться запасной профиль
         # с локального домена
         {'name':'fallback_sync!:fallback_success',
          'message': __("Get a user fallback profile from the "
                        "{cl_remote_host} domain")
         },
         # ошибка синхронизации профиль не готов! к использованию
         {'name':'failed',
          'error':__("Failed to get the user profile from the domain"),
          'method':'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"error")',
          'depend':Tasks.failed_all('sync_remote','sync_local','fallback_sync') |
                   (Tasks.hasnot('domain_sync') & Tasks.failed()) |
                   Tasks.failed_one_of('mount_resources','two_session')
         },
         # распаковать ссылки
         {'name':'domain_sync:unpack_links',
          'method':'Client.unpackLinks(ur_home_path)',
          'failed_warning': __("Failed to unpack the links archive"),
          'depend':Tasks.hasnot('failed')
         },
         # синхронизация профиля завершилась успешно
         {'name':'domain_sync:success_sync',
          'message':__("User profile fetched from the domain"),
          'method':'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"success")',
          'depend': Tasks.success_all('sync_remote','unpack_links') |
                    Tasks.success()
         },
         # во время синхронизации профиля произошли ошибки, которые не
         # гарантируют целостность профиля
         {'name':'domain_sync:error_sync',
          'warning':__("Changings in the user profile will not "
                      "be saved to the domain"),
          'method':'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"error")',
          'depend': Tasks.hasnot('success_sync','failed')
         },
         # отключить ресурсы в случае ошибки
         {'name':'umount_remote_res',
          'message':__("Umounting user resources"),
          'method':'Client.umountUserRes(desktop.ur_mount_dirs)',
          'depend':Tasks.has('failed')
         }
        ]


class ClClientSyncLogoutAction(Action):
    """
    Синхронизировать локальный профиль с удаленным, отключить удаленные
    ресурсы пользователя
    """
    native_error = (FilesError,ClientError,DesktopError,TemplatesError)

    successMessage = None
    failedMessage = None
    interruptMessage = __("Synchronization manually interrupted")

    tasks = [
         # проверка доменный ли пользователь
         {'name':'domain_user',
          'condition':lambda Get: (Get('os_remote_auth') and 
                                   Get('cl_remote_host') and 
                                   Get('desktop.ur_domain_set') == 'on'),
          'else_message':__("The local profile will be used")
         },
         # проверка на попытку отключить ресурсы пользователя в X сессии
         {'name':'domain_user:in_xsession',
          'error':__("User {ur_login} is already in X session"),
          'condition':lambda Get:Get('ur_login') in \
                                 Get('desktop.cl_desktop_online_user'),
         },
         # проверить наличие домашней директории
         {'name':'domain_user:check_homedir',
          'condition':lambda Get:path.exists(Get('ur_home_path')),
          'else_error':__("User home directory {ur_home_path} not found"),
         },
         # проверить наличие подключенных ресурсов
         {'name':'domain_user:check_mount',
          'condition':lambda Get:any(x and isMount(x) 
                              for x in Get('cl_client_user_mount_path')),
          'else_error':__("Remote user resources not found")
         },
         # установить время выхода из сеанса
         {'name':'domain_user:set_logout_date',
          'method':'Client.setLogoutDate(ur_home_path,ur_uid,ur_gid)'
         },
         # выполнять ли синхронизацию
         {'name':'domain_user:domain_sync',
          'condition':lambda Get:Get('cl_client_sync_status') == 'success' and
                                 Get('cl_client_sync') == 'on',
          'else_warning':__("The profile will not be uploaded to domain")
         },
         # переместить файлы из профиля в Moved
         {'name':'domain_user:move_home_dir',
          'message':__("Moving non-profile files to Home/Moved directory"),
          'method':'Client.moveHomeDir(ur_home_path,"Moved","homes",'
                   'cl_moved_skip_path)',
         },
         # архивировать симлинки
         {'name':'domain_sync:tar_symlinks',
          'method':'Client.tarSymLinks(ur_uid,ur_gid,ur_home_path,'
                       'cl_sync_del_path,cl_sync_skip_path)',
          'failed_error':_("Failed to make a links archive")
         },
         # закачать профиль пользователя в домен
         {'name':'domain_sync:sync_logout',
          'method':'Client.syncLogoutProfile(cl_remote_host,ur_uid,'
                   'ur_gid,ur_home_path,"unix",cl_client_profile_name,'
                   'cl_client_symlinks)',
         },
         # удалить файлы, которые могут помешать следующему входу в сеанс
         {'name':'domain_sync:remove_noise_files',
          'message':__("Removing hindering files"),
          'method':'Client.removeNoiseFiles(ur_home_path)'
         },
         # удалить "личные" файлы
         {'name':'domain_sync:remove_private_files',
          'message':__("Removing user private files"),
          'method':'Client.removePrivateFiles(ur_home_path)'
         },
         # удалить пользовательские ключи ядра
         {'name':'domain_user!:clear_user_key',
          'message':__("Clearing user keys"),
          'method':'Client.clearUserKey(ur_login)'
         },
         # отключить пользовательские ресурсы
         {'name':'check_mount!:umount_user_res',
          'message':__("Umounting user resources"),
          'method':'Client.umountUserRes(desktop.ur_mount_dirs)',
         },
         # установить статус синхронизации
         {'name':'domain_sync:success_sync',
          'message':__("Modified user profile saved in the domain"),
          'method':'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,'
                           '"success_logout")',
          'depend': Tasks.success_all('sync_logout','check_mount')
         },
         {'name':'domain_sync:failed',
          'error':__("Modified user profile saved in the domain with errors"),
          'method':'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,'
                           '"error")',
          'depend': Tasks.hasnot('success_sync')
         },
         {'name':'umount_allres',
          'method':'Desktop.umountUserRes(ur_home_path)',
         }
        ]
