diff --git a/data/setbg b/data/setbg index 86b411a..224069e 100755 --- a/data/setbg +++ b/data/setbg @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright 2008-2012 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2008-2015 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. @@ -15,7 +15,9 @@ # limitations under the License. from PySide import QtCore, QtGui -import os,sys +import os +import re + class ImageViewer(QtGui.QMainWindow): def __init__(self): @@ -28,25 +30,27 @@ class ImageViewer(QtGui.QMainWindow): screen = QtGui.QDesktopWidget().screenGeometry() self.setGeometry(screen) - self.open('/usr/share/wallpapers/dm-background.png') or \ - self.open('/usr/share/apps/ksplash/Themes/CalculateSplashEn/' - '400x300/background.png') or \ - self.setBackground() + if not any(self.open(x) for x in + ('/usr/share/wallpapers/dm-background.png', + '/usr/share/apps/ksplash/Themes/CalculateSplashEn/' + '400x300/background.png')): + self.set_background() - def selectColor(self): + @staticmethod + def select_color(): try: if filter(re.compile(r"(cld|cldx|cldg|cmc|cls)-themes-12").search, - os.listdir('/var/db/pkg/media-gfx')): + os.listdir('/var/db/pkg/media-gfx')): return "#73a363" - except: + except OSError: pass return '#30648b' - def setBackground(self): - self.setStyleSheet("background-color: %s"%self.selectColor()) + def set_background(self): + self.setStyleSheet("background-color: %s" % self.select_color()) - def open(self,fileName): - image = QtGui.QImage(fileName) + def open(self, fn): + image = QtGui.QImage(fn) if image.isNull(): return False @@ -55,8 +59,10 @@ class ImageViewer(QtGui.QMainWindow): self.imageLabel.adjustSize() return True + if __name__ == '__main__': import sys + app = QtGui.QApplication(sys.argv) imageViewer = ImageViewer() imageViewer.show() diff --git a/pym/desktop/datavars.py b/pym/desktop/datavars.py index f322aba..16abcc8 100644 --- a/pym/desktop/datavars.py +++ b/pym/desktop/datavars.py @@ -1,6 +1,6 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -# Copyright 2012-2013 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2012-2015 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. @@ -15,14 +15,15 @@ # limitations under the License. __app__ = 'calculate-desktop' -__version__ = '3.1.8' +__version__ = '3.4.2' -import os import sys from calculate.lib.datavars import DataVars from calculate.lib.cl_lang import setLocalTranslate -setLocalTranslate('cl_desktop3',sys.modules[__name__]) + +setLocalTranslate('cl_desktop3', sys.modules[__name__]) + class DataVarsDesktop(DataVars): """Variable class for desktop package""" diff --git a/pym/desktop/desktop.py b/pym/desktop/desktop.py index 8af9cb7..c93ee5c 100644 --- a/pym/desktop/desktop.py +++ b/pym/desktop/desktop.py @@ -1,6 +1,6 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -# Copyright 2010-2013 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2010-2015 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. @@ -16,51 +16,49 @@ import os from os import path -import re import sys -import pwd import time -import traceback +from calculate.core.server.core_interfaces import MethodsInterface -from calculate.desktop._cl_keys import getKey, clearKey -from datavars import DataVarsDesktop, DataVars, __version__,__app__ +from calculate.desktop._cl_keys import getKey -from calculate.lib.cl_template import (Template, ProgressTemplate, - TemplatesError,templateFunction,iniParser) -from calculate.lib.utils.files import (runOsCommand, isMount,process, - getRunCommands,STDOUT,childMounts,getLoopFromPath, - getMdRaidDevices,listDirectory,removeDir, - makeDirectory) -from calculate.lib.utils.common import (getpathenv,appendProgramToEnvFile, - removeProgramToEnvFile,mountEcryptfs, - CommonError, isBootstrapDataOnly) +from calculate.lib.utils.files import (isMount, process, + getRunCommands, STDOUT, childMounts, + getLoopFromPath, + getMdRaidDevices, listDirectory, + removeDir, + makeDirectory, getProgPath) +from calculate.lib.utils.common import (mountEcryptfs, + CommonError, isBootstrapDataOnly) -from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate -setLocalTranslate('cl_desktop3',sys.modules[__name__]) +from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate + +_ = lambda x: x +setLocalTranslate('cl_desktop3', sys.modules[__name__]) __ = getLazyLocalTranslate(_) -from itertools import ifilter + import tarfile import tempfile import shutil -from itertools import count class DesktopError(Exception): """Desktop Error""" -class Desktop: +class Desktop(MethodsInterface): """ Модуль для настройки пользовательского сеанса и выполнения принудительного выхода из X сессии пользователя """ - + def __init__(self): self.homeDir = "" self.clTempl = None self.clVars = None - def createCryptDir(self,userName,uid,gid,userDir,recreateOnError=False): + def createCryptDir(self, userName, uid, gid, userDir, + recreateOnError=False): """ Создать шифрование домашней директории, или подключить существующую @@ -73,20 +71,21 @@ class Desktop: # проверить наличие пароля в ключах ядра if not userPwd or userPwd == "XXXXXXXX": raise DesktopError(_("User password not found")) - ecryptfsPath = path.join('/home/.ecryptfs',userName) + ecryptfsPath = path.join('/home/.ecryptfs', userName) # если шифрование уже используется if path.exists(ecryptfsPath): - for d in (".ecryptfs",".Private"): - source,target = path.join(ecryptfsPath,d),path.join(userDir,d) + for d in (".ecryptfs", ".Private"): + source, target = path.join(ecryptfsPath, d), path.join(userDir, + d) if not path.lexists(target): - os.symlink(source,target) + os.symlink(source, target) # попытаться подключить шифрованные данные try: - if not mountEcryptfs(userName,userPwd,userDir): + if not mountEcryptfs(userName, userPwd, userDir): error = _("Failed to mount ecrypted data") except CommonError as e: - error = (_("Failed to mount ecrypted data")+ - _(": ")+'":%s"'%str(e)) + error = (_("Failed to mount ecrypted data") + + _(": ") + '":%s"' % str(e)) # если при подключении произошли ошибки if error: # заархивировать текущий профиль и удалить его @@ -94,17 +93,17 @@ class Desktop: self.printSUCCESS(_("Recovering encrypted data")) if self.getMountUserPaths(userDir): raise DesktopError(_("Failed to encrypt the directory")) - for source in (userDir,ecryptfsPath): + for source in (userDir, ecryptfsPath): if path.exists(source): if listDirectory(source): - target = source+".bak" + target = source + ".bak" newtarget = target if path.exists(target): removeDir(target) - os.rename(source,newtarget) + os.rename(source, newtarget) else: os.rmdir(source) - self.createUserDir(userName,uid,gid,userDir) + self.createUserDir(userName, uid, gid, userDir) # ошибка создания шифрования else: raise DesktopError(error) @@ -116,39 +115,40 @@ class Desktop: if isBootstrapDataOnly(userDir): if childMounts(userDir): raise DesktopError( - _("Failed to create an encrypted user profile")+ - _(": ")+ + _("Failed to create an encrypted user profile") + + _(": ") + _("The home directory contains mount points")) # поместить данные во временный tarfile calculateName = ".calculate" - calculatePath = path.join(userDir,calculateName) + calculatePath = path.join(userDir, calculateName) tf = tempfile.TemporaryFile() - with tarfile.open(fileobj=tf,mode='w:') as tarf: - tarf.add(calculatePath,calculateName) + with tarfile.open(fileobj=tf, mode='w:') as tarf: + tarf.add(calculatePath, calculateName) tf.flush() tf.seek(0) # удалить эти данные shutil.rmtree(calculatePath) # создать шифрованные данные - e = process('/usr/bin/ecryptfs-setup-private','-u',userName, - '-b','-l',userPwd,stderr=STDOUT) + e = process('/usr/bin/ecryptfs-setup-private', '-u', userName, + '-b', '-l', userPwd, stderr=STDOUT) if e.failed(): raise DesktopError(e.read()) # если были данные от бутстрапа, то распаковать их if tf: - with tarfile.open(fileobj=tf,mode='r:') as tarf: + with tarfile.open(fileobj=tf, mode='r:') as tarf: tarf.extractall(userDir) except Exception as e: # в случае ошибки сохраняем архив (с данными bootstrap) # из памяти в файловую систему if tf: tf.seek(0) - bakArchName = path.join(userDir,".calculate.tar.bz2") - with open(bakArchName,'w') as f: + bakArchName = path.join(userDir, ".calculate.tar.bz2") + with open(bakArchName, 'w') as f: f.write(tf.read()) - raise DesktopError(str(e)+ - _("Failed to create an encrypted user profile")) + raise DesktopError(str(e) + + _( + "Failed to create an encrypted user profile")) finally: if tf: tf.close() @@ -161,11 +161,11 @@ class Desktop: if not path.exists(userDir): os.makedirs(userDir) if mode: - os.chmod(userDir,mode) - os.chown(userDir,uid,gid) + os.chmod(userDir, mode) + os.chown(userDir, uid, gid) return True else: - raise DesktopError(_("Path %s exists") %userDir) + raise DesktopError(_("Path %s exists") % userDir) def umountUserRes(self, *umountPaths): """ @@ -183,66 +183,53 @@ class Desktop: Found user resources """ if not homeDir: - userName = self.clVars.Get("ur_login") + self.clVars.Get("ur_login") homeDir = self.clVars.Get("ur_home_path") if not homeDir: raise DesktopError(_("Failed to determine the home directory")) dirStart, dirEnd = path.split(homeDir) - mountProfileDir = path.join(dirStart, ".%s" %dirEnd) - mountRemoteProfileDir = path.join(dirStart, ".%s.remote" %dirEnd) - return filter(lambda x: x.startswith(homeDir) or\ - x.startswith(mountProfileDir) or\ - x.startswith(mountRemoteProfileDir), - map(lambda x: x.split(" ")[1],\ + mountProfileDir = path.join(dirStart, ".%s" % dirEnd) + mountRemoteProfileDir = path.join(dirStart, ".%s.remote" % dirEnd) + return filter(lambda x: x.startswith(homeDir) or + x.startswith(mountProfileDir) or + x.startswith(mountRemoteProfileDir), + map(lambda x: x.split(" ")[1], open("/proc/mounts").readlines())) - def execProg(self, cmdStrProg, inStr=False, envProg={}): - """ - Exec 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): """ Отмонтировать указанный путь, а также отключить используемые в этом пути loop устройства и raid """ # check for mount + umount_cmd = getProgPath('/bin/umount') + fuser_cmd = getProgPath("/bin/fuser") loops = getLoopFromPath(rpath) if loops: - setLoops = set(map(lambda x:x.partition('/dev/')[2],loops)) + setLoops = set(map(lambda x: x.partition('/dev/')[2], loops)) mdInfo = getMdRaidDevices() - for k,v in mdInfo.items(): + for k, v in mdInfo.items(): if setLoops & set(v): - self.umountSleepPath('/dev/%s'%k) - process('/sbin/mdadm','--stop','/dev/%s'%k).success() + self.umountSleepPath('/dev/%s' % k) + process('/sbin/mdadm', '--stop', '/dev/%s' % k).success() for loop in loops: self.umountSleepPath(loop) - process('/sbin/losetup','-d',loop).success() + process('/sbin/losetup', '-d', loop).success() if isMount(rpath): - for waittime in [0,0.5,1,2]: + 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) + process(umount_cmd, rpath).success() + if not isMount(rpath): + return True + process(fuser_cmd, "-km", rpath).success() 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 - self.execProg("umount -l %s"%rpath) + if not isMount(rpath): + return True + process(umount_cmd, "-l", rpath).success() else: if isMount(rpath): - self.printERROR(_("Failed to unmount directory %s")%rpath) + self.printERROR(_("Failed to unmount directory %s") % rpath) return False return True @@ -257,9 +244,9 @@ class Desktop: fastlogin_user = path.join(fastlogin, urLogin) if not path.exists(fastlogin_user): try: - open(fastlogin_user,'w').close() + open(fastlogin_user, 'w').close() return True - except: + except IOError: self.printWARNING(_("Failed to create the fastlogin mark file")) return False @@ -268,24 +255,25 @@ class Desktop: Выполнить logout пользователя через dbus """ display = self.clVars.Select('cl_desktop_online_display', - where='cl_desktop_online_user',eq=urLogin,limit=1) + where='cl_desktop_online_user', eq=urLogin, + limit=1) session = self.clVars.Get('cl_desktop_xsession') if session == 'xfce': logoutCommand = "/usr/bin/qdbus org.xfce.SessionManager " \ - "/org/xfce/SessionManager Logout False False" + "/org/xfce/SessionManager Logout False False" elif session == 'kde': logoutCommand = "/usr/bin/kquitapp ksmserver" elif session == 'gnome': logoutCommand = "/usr/bin/qdbus org.gnome.SessionManager " \ - "/org/gnome/SessionManager Logout 1" + "/org/gnome/SessionManager Logout 1" else: raise DesktopError(_("Unable to detect the X session")) - if process("su",urLogin,"-c", - ("DISPLAY=:%s "%display)+logoutCommand).failed(): + if process("su", urLogin, "-c", + ("DISPLAY=:%s " % display) + logoutCommand).failed(): raise DesktopError(_("Unable to send the logout command")) return True - def waitLogout(self,urLogin,waitTime,postWaitTime=5): + def waitLogout(self, urLogin, waitTime, postWaitTime=5): """ Ожидать завершения пользовательского сеанса @@ -294,24 +282,24 @@ class Desktop: waitTime: время ожидания завершения сеанса """ if filter(lambda x: "xdm/xdm\x00--logout" in x, - getRunCommands()): - for i in range(0,waitTime): + getRunCommands()): + for i in range(0, waitTime): if not filter(lambda x: "xdm/xdm\x00--logout" in x, - getRunCommands()): + getRunCommands()): return True time.sleep(1) else: raise DesktopError(_("Unable to wait for completion " "of the user logout")) - for wait in range(0,5): + for wait in range(0, 5): self.clVars.Invalidate('cl_desktop_online_data') - if not urLogin in self.clVars.Get('cl_desktop_online_user'): + if urLogin not in self.clVars.Get('cl_desktop_online_user'): return True time.sleep(1) else: return False - - def prepareFace(self,ur_home_path): + + def prepareFace(self, ur_home_path): """Подготовить каталог пользователя с шифрованием для работы с .face Для шифрованных профилей в корне домашней директории @@ -325,7 +313,7 @@ class Desktop: True/False в зависимости от успешности """ if path.exists(ur_home_path): - symlink_path = path.join(ur_home_path,'.face') + symlink_path = path.join(ur_home_path, '.face') if not path.lexists(symlink_path): os.symlink('.ecryptfs/.face', symlink_path) return True diff --git a/pym/desktop/utils/cl_desktop.py b/pym/desktop/utils/cl_desktop.py index 82ad2ef..b3bc271 100644 --- a/pym/desktop/utils/cl_desktop.py +++ b/pym/desktop/utils/cl_desktop.py @@ -1,6 +1,6 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -# Copyright 2010-2013 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2010-2015 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. @@ -16,21 +16,23 @@ import sys from os import path -from calculate.core.server.func import Action,Tasks -from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate +from calculate.core.server.func import Action, Tasks +from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate from calculate.lib.utils.files import FilesError, isMount from calculate.desktop.desktop import DesktopError from calculate.lib.cl_template import TemplatesError -setLocalTranslate('cl_desktop3',sys.modules[__name__]) +_ = lambda x: x +setLocalTranslate('cl_desktop3', sys.modules[__name__]) __ = getLazyLocalTranslate(_) + class ClDesktopLogoutAction(Action): """ Вывести пользователя из X сессии """ # ошибки, которые отображаются без подробностей - native_error = (FilesError,DesktopError,TemplatesError) + native_error = (FilesError, DesktopError, TemplatesError) successMessage = __("User logged out!") failedMessage = __("Unable to log out") @@ -38,20 +40,21 @@ class ClDesktopLogoutAction(Action): # список задач для действия tasks = [ - {'name':'user_logout', - 'method':'Desktop.userLogout(cl_desktop_login)', + {'name': 'user_logout', + 'method': 'Desktop.userLogout(cl_desktop_login)', }, - {'name':'wait_logout', - 'message':__("Waiting for the logout"), - 'method':'Desktop.waitLogout(cl_desktop_login,300)'} - ] + {'name': 'wait_logout', + 'message': __("Waiting for the logout"), + 'method': 'Desktop.waitLogout(cl_desktop_login,300)'} + ] + class ClDesktopAction(Action): """ Настроить пользовательский профиль """ # ошибки, которые отображаются без подробностей - native_error = (FilesError,DesktopError,TemplatesError) + native_error = (FilesError, DesktopError, TemplatesError) successMessage = __("User account {ur_login} has been " "successfully configured") @@ -60,50 +63,51 @@ class ClDesktopAction(Action): # список задач для действия tasks = [ - # создать домашниюю директорию - {'name':'create_home', - 'message':__("Creating the home directory for {ur_login}"), - 'method':'Desktop.createUserDir(ur_login,ur_uid,ur_gid,ur_home_path)', - 'condition':lambda dv:not path.exists(dv.Get('ur_home_path')) + # создать домашниюю директорию + {'name': 'create_home', + 'message': __("Creating the home directory for {ur_login}"), + 'method': 'Desktop.createUserDir(ur_login,ur_uid,ur_gid,ur_home_path)', + 'condition': lambda dv: not path.exists(dv.Get('ur_home_path')) }, - # используется ли шифрование - {'name':'crypt', - 'condition':lambda dv:(not isMount(dv.Get('ur_home_path')) and \ - dv.Get('ur_home_crypt_set') == 'on' and - (not dv.isModuleInstalled("install") or - dv.Get('install.cl_autologin') != dv.Get('ur_login'))) + # используется ли шифрование + {'name': 'crypt', + 'condition': lambda dv: (not isMount(dv.Get('ur_home_path')) and + dv.Get('ur_home_crypt_set') == 'on' and + (not dv.isModuleInstalled("install") or + dv.Get('install.cl_autologin') != dv.Get( + 'ur_login'))) }, - # подготовить шифрованный профиль пользователя для работы с .icon - {'name':'crypt:prepare_icon', - 'method':'Desktop.prepareFace(ur_home_path)', - 'condition':lambda Get:Get('ur_domain_set') == 'off' + # подготовить шифрованный профиль пользователя для работы с .icon + {'name': 'crypt:prepare_icon', + 'method': 'Desktop.prepareFace(ur_home_path)', + 'condition': lambda Get: Get('ur_domain_set') == 'off' }, - # подключить шифрованные данные - {'name':'crypt:ecryptfs', - 'message':__("Mounting encrypted data"), - 'method':'Desktop.createCryptDir(ur_login,ur_uid,ur_gid,' - 'ur_home_path,False)' + # подключить шифрованные данные + {'name': 'crypt:ecryptfs', + 'message': __("Mounting encrypted data"), + 'method': 'Desktop.createCryptDir(ur_login,ur_uid,ur_gid,' + 'ur_home_path,False)' }, - # настроить пользовательских профиль шаблонами - {'name':'user_profile', - 'message':__("Setting up the user profile"), - 'method':'Desktop.applyTemplates(None,False,'\ + # настроить пользовательских профиль шаблонами + {'name': 'user_profile', + 'message': __("Setting up the user profile"), + 'method': 'Desktop.applyTemplates(None,False,' 'False,None)', - 'condition':lambda Get: Get('cl_desktop_force_setup_set') == 'on' or \ - Get('cl_desktop_update_profile_set') == 'on' + 'condition': lambda Get: (Get('cl_desktop_force_setup_set') == 'on' or + Get('cl_desktop_update_profile_set') == 'on') }, - {'name':'fast_login', - 'method':'Desktop.setFastlogin(ur_login)', - 'essential':False, - 'condition':lambda Get: Get('ur_domain_set') == 'off' + {'name': 'fast_login', + 'method': 'Desktop.setFastlogin(ur_login)', + 'essential': False, + 'condition': lambda Get: Get('ur_domain_set') == 'off' }, - # отключить ресурсы подключенные в каталоге пользователя - {'name':'umount_userres', - 'message': _("Unmouning user resources"), - 'method':'Desktop.umountUserRes(ur_mount_dirs)', - 'condition': lambda dv:dv.Get('ur_mount_dirs'), - 'depend': Tasks.failed()}, - {'name':'ecryptfs:umount_homedir', - 'method':'Desktop.umountUserRes(ur_home_path)', - 'depend': Tasks.failed()} - ] + # отключить ресурсы подключенные в каталоге пользователя + {'name': 'umount_userres', + 'message': _("Unmouning user resources"), + 'method': 'Desktop.umountUserRes(ur_mount_dirs)', + 'condition': lambda dv: dv.Get('ur_mount_dirs'), + 'depend': Tasks.failed()}, + {'name': 'ecryptfs:umount_homedir', + 'method': 'Desktop.umountUserRes(ur_home_path)', + 'depend': Tasks.failed()} + ] diff --git a/pym/desktop/variables/action.py b/pym/desktop/variables/action.py index 11ade65..de2fa42 100644 --- a/pym/desktop/variables/action.py +++ b/pym/desktop/variables/action.py @@ -1,6 +1,6 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -# Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2008-2015 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. @@ -14,14 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import sys -from os import path -from calculate.lib.datavars import (Variable,VariableError,ReadonlyVariable, - ActionVariable) +from calculate.lib.datavars import ActionVariable from calculate.lib.cl_lang import setLocalTranslate -setLocalTranslate('cl_desktop3',sys.modules[__name__]) + +setLocalTranslate('cl_desktop3', sys.modules[__name__]) + class VariableAcDesktopMerge(ActionVariable): """ @@ -30,18 +29,19 @@ class VariableAcDesktopMerge(ActionVariable): """ nonchroot = True - def action(self,cl_action): + def action(self, cl_action): if cl_action == "merge": return "on" return "off" + class VariableAcDesktopProfile(ActionVariable): """ Action variable which has value "on" on user profile setup """ nonchroot = True - def action(self,cl_action): + def action(self, cl_action): if cl_action in ("desktop",): return "on" return "off" diff --git a/pym/desktop/variables/desktop.py b/pym/desktop/variables/desktop.py index 67b82d7..3ad64d0 100644 --- a/pym/desktop/variables/desktop.py +++ b/pym/desktop/variables/desktop.py @@ -1,6 +1,6 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -# Copyright 2010-2013 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2010-2015 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. @@ -19,50 +19,60 @@ import sys import re from os import path import pwd -from calculate.lib.datavars import Variable,VariableError,ReadonlyVariable, \ - ReadonlyTableVariable,FieldValue +from calculate.lib.datavars import (Variable, VariableError, ReadonlyVariable, + ReadonlyTableVariable, FieldValue, + VariableInterface) +from calculate.lib.utils.common import getValueFromConfig from calculate.lib.variables.user import VariableUrLogin -from calculate.lib.utils.files import (readLinesFile,process,isMount, - listDirectory) +from calculate.lib.utils.files import (readLinesFile, process, + listDirectory) from calculate.desktop._cl_keys import getKey from itertools import * -from calculate.lib.cl_template import (templateFunction,iniParser) +from calculate.lib.cl_template import templateFunction +from calculate.lib.cl_ini_parser import iniParser import hashlib from calculate.lib.cl_lang import setLocalTranslate -setLocalTranslate('cl_desktop3',sys.modules[__name__]) + +_ = lambda x: x +setLocalTranslate('cl_desktop3', sys.modules[__name__]) + class VariableUrJidHost(ReadonlyVariable): """ Jabber host for user """ + def get(self): userJid = self.Get("ur_jid") if userJid: return userJid.partition('@')[2] return "" -class DomainInfoHelper: + +class DomainInfoHelper(VariableInterface): """ Вспомогательный класс для определения доменный ли пользователь """ + def getUserDataInFile(self, login, filePasswd): - return filter(lambda x: x[0]==login, - map(lambda x: x.strip().split(':'), - readLinesFile(filePasswd))) + return filter(lambda x: x[0] == login, + map(lambda x: x.strip().split(':'), + readLinesFile(filePasswd))) - def isDomainUser(self,userName): + def isDomainUser(self, userName): if userName: try: passwdUserData = self.getUserDataInFile(userName, "/etc/passwd") - except: + except Exception: return False if passwdUserData: passwdUserData = passwdUserData[0] try: - cacheUserData = self.getUserDataInFile(userName, + cacheUserData = self.getUserDataInFile( + userName, "/var/lib/calculate/calculate-client/cache/passwd") - except: + except Exception: return False if cacheUserData: cacheUserData = cacheUserData[0] @@ -73,70 +83,65 @@ class DomainInfoHelper: return False -class VariableUrDomainSet(ReadonlyVariable,DomainInfoHelper): +class VariableUrDomainSet(ReadonlyVariable, DomainInfoHelper): """ Flag for determining domain user or local """ type = "bool" - def getUserDataInFile(self, login, filePasswd): - return filter(lambda x: x[0]==login, - map(lambda x: x.strip().split(':'), - readLinesFile(filePasswd))) + def getUserDataInFile(self, login, file_passwd): + return filter(lambda x: x[0] == login, + map(lambda x: x.strip().split(':'), + readLinesFile(file_passwd))) def get(self): return "on" if self.isDomainUser(self.Get('ur_login')) else "off" + class VariableClDesktopXsession(ReadonlyVariable): """ User current X session """ + session_list = ("kde", "gnome", "mate", "xfce", "plasma") + def get(self): envXsessionFile = "/etc/env.d/90xsession" - xsession = os.environ.get("XSESSION",None) - desktopSession = os.environ.get("DESKTOP_SESSION",None) + xsession = os.environ.get("XSESSION", None) + desktopSession = os.environ.get("DESKTOP_SESSION", None) if not xsession: - if os.path.exists(envXsessionFile): - xsession = \ - map(lambda x:x.partition("=")[2].strip("'\""), - filter(lambda x:x.startswith("XSESSION="), - filter(lambda x:not x.startswith("#"), - open(envXsessionFile,"r")))) - if xsession: - xsession = xsession[-1] + xsession = getValueFromConfig(envXsessionFile, "XSESSION") if xsession: - if desktopSession and \ - any(x in desktopSession.lower() - for x in ("kde","xfce","gnome")): + if (desktopSession and any(x in desktopSession.lower() + for x in self.session_list)): xsession = desktopSession - if "kde" in xsession.lower(): - return "kde" - elif "gnome" in xsession.lower(): - return "gnome" - elif "xfce" in xsession.lower(): - return "xfce" + xsession = xsession.lower() + for session in self.session_list: + if session in xsession: + return session else: - return xsession.lower() + return xsession return "" + class VariableClDesktopGstData(ReadonlyVariable): """ GStreamer data """ + def get(self): # try import gst + copyargv = sys.argv + sys.argv = [] + olderr = os.dup(sys.stderr.fileno()) try: - copyargv = sys.argv - sys.argv = [] - olderr = os.dup(sys.stderr.fileno()) os.close(sys.stderr.fileno()) import gst import gst.interfaces except ImportError: gst = None finally: - sys.argv= copyargv - os.dup2(olderr,sys.stderr.fileno()) + sys.argv = copyargv + os.dup2(olderr, sys.stderr.fileno()) if gst is None: return {} @@ -149,48 +154,55 @@ class VariableClDesktopGstData(ReadonlyVariable): outdata['device_name'] = alsamixer.get_property("device-name") outdata['long_name'] = alsamixer.get_factory().get_longname() outdata['internal_name'] = filter(str.isalnum, - "%s (%s)"%(outdata['device_name'], - outdata['long_name'])) + "%s (%s)" % ( + outdata['device_name'], + outdata['long_name'])) outdata['channels'] = [] for t in alsamixer.list_tracks(): if t.flags & gst.interfaces.MIXER_TRACK_OUTPUT: if t.flags & gst.interfaces.MIXER_TRACK_MASTER or \ - any(x in t.label - for x in ("Wave","Front","LFE","Center", - "Head","Side","Speaker", - "Surround","PCM")): + any(x in t.label + for x in ("Wave", "Front", "LFE", "Center", + "Head", "Side", "Speaker", + "Surround", "PCM")): outdata['channels'].append(t.label) if t.flags & gst.interfaces.MIXER_TRACK_MASTER: outdata['master_channel'] = t.label - except: + except Exception: pass return outdata + class VariableClDesktopGstCard(ReadonlyVariable): """ Internal card name for xfce mixer """ + def get(self): - return self.Get('cl_desktop_gst_data').get('internal_name','') + return self.Get('cl_desktop_gst_data').get('internal_name', '') + class VariableClDesktopGstMasterchannel(ReadonlyVariable): """ Master track name """ + def get(self): - return self.Get('cl_desktop_gst_data').get('master_channel','') + return self.Get('cl_desktop_gst_data').get('master_channel', '') class VariableClDesktopXfceMixer(ReadonlyVariable): """ List of channel for xfce-perchannel mixer """ + def get(self): return "\n".join( - map(lambda x:' '%x, - self.Get('cl_desktop_gst_data').get('channels',[]))) + map(lambda x: ' ' % x, + self.Get('cl_desktop_gst_data').get('channels', []))) + -class VariableClDesktopOnlineData(ReadonlyTableVariable,DomainInfoHelper): +class VariableClDesktopOnlineData(ReadonlyTableVariable, DomainInfoHelper): """ Information about online users """ @@ -202,52 +214,53 @@ class VariableClDesktopOnlineData(ReadonlyTableVariable,DomainInfoHelper): reDisplay = re.compile(r"^\(?:(\d+\.?\d*)") - def _getDisplay(self,*args): + def _getDisplay(self, *args): """ Get DISPLAY from args """ - for arg in map(self.reDisplay.search,args): + for arg in map(self.reDisplay.search, args): if arg: return arg.group(1) return "" - def get_user_uid(self,username): + def get_user_uid(self, username): try: return str(pwd.getpwnam(username).pw_uid) - except: + except Exception: return "" - def get(self,hr=False): - xSession = 0 - foundTwoSession = False + def get(self, hr=False): + # TODO: need to KISS rewrite resWho = process("who") xData = [[]] if resWho.success(): listProcessing = lambda x: (x[0], x[1], x[-1]) \ - if len(x)>=5 else [] + if len(x) >= 5 else [] xData = groupby( - sorted( - filter(lambda x: x[0]!="root", - map(lambda x: (x[0],self._getDisplay(x[1],x[2])), - filter(lambda x: x and\ - (x[2].startswith("(:") or \ - x[1].startswith(":")), - map(lambda x: listProcessing(\ - filter(lambda y: y, x.split())), - resWho)))), - key=lambda x:x[0]), - lambda x:x[0]) - xData = map(lambda x:(x[0][0],x[0][1], - self.get_user_uid(x[0][0]), - "on" if self.isDomainUser(x[0][0]) else "off", - len(x)), - map(lambda x:list(x[1]), - xData)) + sorted( + filter(lambda x: x[0] != "root", + map(lambda x: (x[0], self._getDisplay(x[1], x[2])), + filter(lambda x: x and \ + (x[2].startswith("(:") or + x[1].startswith(":")), + map(lambda x: listProcessing( + filter(lambda y: y, x.split())), + resWho)))), + key=lambda x: x[0]), + lambda x: x[0]) + xData = map(lambda x: (x[0][0], x[0][1], + self.get_user_uid(x[0][0]), + "on" if self.isDomainUser( + x[0][0]) else "off", + len(x)), + map(lambda x: list(x[1]), + xData)) return xData setValue = Variable.setValue -class VariableClDesktopOnlineUser(FieldValue,ReadonlyVariable): + +class VariableClDesktopOnlineUser(FieldValue, ReadonlyVariable): """ Логин пользователя """ @@ -255,7 +268,8 @@ class VariableClDesktopOnlineUser(FieldValue,ReadonlyVariable): source_variable = "cl_desktop_online_data" column = 0 -class VariableClDesktopOnlineDisplay(FieldValue,ReadonlyVariable): + +class VariableClDesktopOnlineDisplay(FieldValue, ReadonlyVariable): """ Display пользователя """ @@ -263,7 +277,8 @@ class VariableClDesktopOnlineDisplay(FieldValue,ReadonlyVariable): source_variable = "cl_desktop_online_data" column = 1 -class VariableClDesktopOnlineUid(FieldValue,ReadonlyVariable): + +class VariableClDesktopOnlineUid(FieldValue, ReadonlyVariable): """ UID пользователя """ @@ -271,7 +286,8 @@ class VariableClDesktopOnlineUid(FieldValue,ReadonlyVariable): source_variable = "cl_desktop_online_data" column = 2 -class VariableClDesktopOnlineDomainSet(FieldValue,ReadonlyVariable): + +class VariableClDesktopOnlineDomainSet(FieldValue, ReadonlyVariable): """ Является ли пользователь доменным """ @@ -279,7 +295,8 @@ class VariableClDesktopOnlineDomainSet(FieldValue,ReadonlyVariable): source_variable = "cl_desktop_online_data" column = 3 -class VariableClDesktopOnlineCount(FieldValue,ReadonlyVariable): + +class VariableClDesktopOnlineCount(FieldValue, ReadonlyVariable): """ Количество сеансов пользователя """ @@ -287,6 +304,7 @@ class VariableClDesktopOnlineCount(FieldValue,ReadonlyVariable): source_variable = "cl_desktop_online_data" column = 4 + class VariableClDesktopLogin(VariableUrLogin): """ User Login @@ -299,20 +317,21 @@ class VariableClDesktopLogin(VariableUrLogin): else: return VariableUrLogin.choice(self) - def check(self,value): + def check(self, value): """Does user exist""" - if not value in self.choice() and self.Get('cl_action') == 'logout': + if value not in self.choice() and self.Get('cl_action') == 'logout': raise VariableError(_("No X session user found")) if value == "": raise VariableError(_("Please specify the user name")) try: pwd.getpwnam(value).pw_gid - except: - raise VariableError(_("User %s does not exist")%value) + except Exception: + raise VariableError(_("User %s does not exist") % value) def get(self): return "" + class VariableUrMountDirs(ReadonlyVariable): """ Примонтированные директории в профиле пользователя @@ -324,26 +343,30 @@ class VariableUrMountDirs(ReadonlyVariable): if not homeDir: return [] dirStart, dirEnd = path.split(homeDir) - mountProfileDir = path.join(dirStart, ".%s" %dirEnd) - mountRemoteProfileDir = path.join(dirStart, ".%s.remote" %dirEnd) - - directories = filter(lambda x:x != homeDir, - filter(lambda x: (x.startswith(homeDir) or - x.startswith(mountProfileDir) or - x.startswith(mountRemoteProfileDir)), - map(lambda x: x.split(" ")[1], - readLinesFile('/proc/mounts')))) - #if isMount(homeDir): + mountProfileDir = path.join(dirStart, ".%s" % dirEnd) + mountRemoteProfileDir = path.join(dirStart, ".%s.remote" % dirEnd) + + directories = filter(lambda x: x != homeDir, + filter(lambda x: (x.startswith(homeDir) or + x.startswith(mountProfileDir) or + x.startswith( + mountRemoteProfileDir)), + map(lambda x: x.split(" ")[1], + readLinesFile('/proc/mounts')))) + # if isMount(homeDir): # directories.append(homeDir) - return sorted(directories,reverse=True) + return sorted(directories, reverse=True) + class VariableUrPassword(ReadonlyVariable): """ Пароль пользователя, получаемый из ключей ядра """ + def get(self): return getKey(self.Get('ur_login')) or "" + class VariableClDesktopUpdateProfileSet(Variable): """ Нужно ли выполнять обновление профиля пользователя на основании @@ -353,19 +376,20 @@ class VariableClDesktopUpdateProfileSet(Variable): def get(self): lastTimestamp = templateFunction.getLastElog() - iniEnv = path.join(self.Get('ur_home_path'),'.calculate/ini.env') + iniEnv = path.join(self.Get('ur_home_path'), '.calculate/ini.env') userIni = iniParser(iniEnv) - userTimestamp = userIni.getVar('main','elog') + userTimestamp = userIni.getVar('main', 'elog') if userTimestamp: userTimestamp = userTimestamp.encode('utf-8') - profileSetup = userIni.getVar('main','profile_setup') + profileSetup = userIni.getVar('main', 'profile_setup') login_setup = profileSetup == 'on' if (self.Get('ur_domain_set') == 'on' or login_setup or - not path.exists(iniEnv) or userTimestamp != lastTimestamp): + not path.exists(iniEnv) or userTimestamp != lastTimestamp): return 'on' else: return 'off' + class VariableClDesktopForceSetupSet(Variable): """ Принудительно выполнить обновление пользовательского профиля @@ -379,37 +403,43 @@ class VariableClDesktopForceSetupSet(Variable): self.label = _("Force configuration") self.help = _("force configuration") + class VariableClDesktopFacePath(Variable): """Путь к стандартным иконкам пользователей""" value = "/usr/share/pixmaps/faces" + class VariableClDesktopFaceList(Variable): """Список доступных иконок по умолчанию для пользователей""" type = "list" def get(self): return sorted( - filter(lambda x:x.endswith('.png'), - listDirectory(self.Get('cl_desktop_face_path')))) + filter(lambda x: x.endswith('.png'), + listDirectory(self.Get('cl_desktop_face_path')))) + class VariableClDesktopHashFace(Variable): """Номер иконки пользователя Номер вычисляется по контрольной сумму md5 логина пользователя """ + def get(self): login = self.Get('ur_login') icon_list = self.Get('cl_desktop_face_list') if icon_list: return path.join(self.Get('cl_desktop_face_path'), - icon_list[sum(map(lambda x:ord(x), - hashlib.md5(login).digest()))%len(icon_list)]) + icon_list[sum(map(lambda x: ord(x), + hashlib.md5( + login).digest())) % len( + icon_list)]) else: return "" + class VariableClDesktopFastloginPath(ReadonlyVariable): """ Путь до каталога в котором указаны пользователи быстрого входа в сеанс """ value = "/var/lib/calculate/calculate-desktop/fastlogin" - diff --git a/pym/desktop/wsdl_desktop.py b/pym/desktop/wsdl_desktop.py index 1391503..e3b1350 100644 --- a/pym/desktop/wsdl_desktop.py +++ b/pym/desktop/wsdl_desktop.py @@ -1,6 +1,6 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -# Copyright 2012-2013 Calculate Ltd. http://www.calculate-linux.org +# Copyright 2012-2015 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. @@ -13,16 +13,18 @@ # 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, time, os +import sys -from calculate.lib.datavars import VariableError,DataVarsError,DataVars +from calculate.lib.datavars import VariableError, DataVarsError from calculate.core.server.func import WsdlBase from desktop import DesktopError -from utils.cl_desktop import ClDesktopLogoutAction,ClDesktopAction +from utils.cl_desktop import ClDesktopLogoutAction, ClDesktopAction import desktop -from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate -setLocalTranslate('cl_desktop3',sys.modules[__name__]) +from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate + +_ = lambda x: x +setLocalTranslate('cl_desktop3', sys.modules[__name__]) __ = getLazyLocalTranslate(_) @@ -32,69 +34,70 @@ class Wsdl(WsdlBase): # вывести пользователя из сеанса # { - # идентификатор метода - 'method_name':"desktop_logout", - # категория метода - 'category':__('Desktop'), - # заголовок метода - 'title':__("User Logout"), - # иконка для графической консоли - 'image':'system-log-out', - # метод присутствует в графической консоли - 'gui':True, - # консольная команда - 'command':'cl-desktop-logout', - # права для запуска метода - 'rights':['userconfigure'], - # объект содержащий модули для действия - 'logic':{'Desktop':desktop.Desktop}, - # описание действия - 'action':ClDesktopLogoutAction, - # объект переменных - 'datavars':"desktop", - 'native_error':(VariableError,DataVarsError, - DesktopError), - # значения по умолчанию для переменных этого метода - 'setvars':{'cl_action!':'logout'}, - # описание груп (список лямбда функций) - 'groups':[ - lambda group:group(_("User logout"), - normal=('cl_desktop_login',), - next_label=_("Execute"))]}, + # идентификатор метода + 'method_name': "desktop_logout", + # категория метода + 'category': __('Desktop'), + # заголовок метода + 'title': __("User Logout"), + # иконка для графической консоли + 'image': 'system-log-out', + # метод присутствует в графической консоли + 'gui': True, + # консольная команда + 'command': 'cl-desktop-logout', + # права для запуска метода + 'rights': ['userconfigure'], + # объект содержащий модули для действия + 'logic': {'Desktop': desktop.Desktop}, + # описание действия + 'action': ClDesktopLogoutAction, + # объект переменных + 'datavars': "desktop", + 'native_error': (VariableError, DataVarsError, + DesktopError), + # значения по умолчанию для переменных этого метода + 'setvars': {'cl_action!': 'logout'}, + # описание груп (список лямбда функций) + 'groups': [ + lambda group: group(_("User logout"), + normal=('cl_desktop_login',), + next_label=_("Execute"))]}, # # настроить пользовательский сеанс # { - # идентификатор метода - 'method_name':"desktop", - # категория метода - 'category':__('Desktop'), - # заголовок метода - 'title':__("User Account Configuration"), - # иконка для графической консоли - 'image':'user-desktop,preferences-desktop', - # метод присутствует в графической консоли - 'gui':True, - # консольная команда - 'command':'cl-desktop', - # права для запуска метода - 'rights':['userconfigure'], - # объект содержащий модули для действия - 'logic':{'Desktop':desktop.Desktop}, - # описание действия - 'action':ClDesktopAction, - # объект переменных - 'datavars':"desktop", - 'native_error':(VariableError,DataVarsError, - DesktopError), - # значения по умолчанию для переменных этого метода - 'setvars':{'cl_action!':'desktop', - 'cl_protect_use_set!':'off'}, - # описание груп (список лямбда функций) - 'groups':[ - lambda group:group(_("User account configuration"), - normal=('ur_login',), - expert=('cl_desktop_force_setup_set','cl_verbose_set', - 'cl_templates_locate'), - next_label=_("Execute"))]}, - ] + # идентификатор метода + 'method_name': "desktop", + # категория метода + 'category': __('Desktop'), + # заголовок метода + 'title': __("User Account Configuration"), + # иконка для графической консоли + 'image': 'user-desktop,preferences-desktop', + # метод присутствует в графической консоли + 'gui': True, + # консольная команда + 'command': 'cl-desktop', + # права для запуска метода + 'rights': ['userconfigure'], + # объект содержащий модули для действия + 'logic': {'Desktop': desktop.Desktop}, + # описание действия + 'action': ClDesktopAction, + # объект переменных + 'datavars': "desktop", + 'native_error': (VariableError, DataVarsError, + DesktopError), + # значения по умолчанию для переменных этого метода + 'setvars': {'cl_action!': 'desktop', + 'cl_protect_use_set!': 'off'}, + # описание груп (список лямбда функций) + 'groups': [ + lambda group: group(_("User account configuration"), + normal=('ur_login',), + expert=('cl_desktop_force_setup_set', + 'cl_verbose_set', + 'cl_templates_locate'), + next_label=_("Execute"))]}, + ]