diff --git a/build/lib/calculate-lib/pym/__init__.py b/build/lib/calculate-lib/pym/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/calculate-lib/pym/cl_base.py b/build/lib/calculate-lib/pym/cl_base.py new file mode 100644 index 0000000..4a8626e --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_base.py @@ -0,0 +1,1156 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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 gettext +import os +import getopt +import sys +############################################################################## +import re +import copy +import types +import string +#import os +import filecmp +import ConfigParser +import time +import socket +#import sys +import random +import string +import cl_utils +############################################################################## + +_expand_lang = gettext._expand_lang + +def exit(codeExit): + """Метод выхода из программы""" + sys.exit(codeExit) + +def __findFileMO(domain, localedir=None, languages=None, all=0): + # Модифицинрованный метод модуля gettext ищет файл перевода + if localedir is None: + localedir = _default_localedir + if languages is None: + languages = [] + for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + val = os.environ.get(envar) + if val: + languages = val.split(':') + break + if 'C' not in languages: + languages.append('C') + # now normalize and expand the languages + nelangs = [] + for lang in languages: + for nelang in _expand_lang(lang): + if nelang not in nelangs: + nelangs.append(nelang) + # select a language + if all: + result = [] + else: + result = None + for lang in nelangs: + if lang == 'C': + break + mofile = os.path.join(localedir, '%s_%s.mo' % (domain,lang)) + if os.path.exists(mofile): + if all: + result.append(mofile) + else: + return mofile + return result + +gettext.find = __findFileMO + +class GlobalParam(type): + """ Метакласс для глобальных параметров + """ + def __init__(cls, *args): + cls.GP = [] + cls.GP.append("") + +class lang: + """Класс многоязыковой поддержки lang для перевода сообщений программ на +другие языки. + +Типичное использование: + import sys + import lang + + # язык сообщений английский + tr = lang.lang(en) + # язык определяется системой + #tr = lang.lang() + + #Установка домена переводаldap + # в последующем можно не использовать - задается глобально + tr.setGlobalDomain('calc') + # задается локально для одного файла + #tr.setLocalDomain('calc') + + # Установка метода перевода для текущего модуля + tr.setLanguage(sys.modules[__name__]) + +Где: + tr -- объект перевода + 'en' -- язык выводимых сообщений + sys.modules[__name__] -- модуль сообщения которого переводятся + calc - домен перевода - имя файла перевода без расширения + +Если файл перевода не найден то сообщения не переводятся +Если в модуле сообщения которого переводим, экспортируются другие модули то +они тоже переводятся. + +По умолчанию директория в которой находятся переводы: 'lang/i18h' относительно +исполняемого файла названия файлов перевода совпадают с названиями модулей +если не определен методом setDomainTranslate() - домен перевода. + """ + __metaclass__ = GlobalParam + def __init__(self,l=''): + self.nameDomain = self.GP[0] + #self.nameDomain = '' + """ Название файла перевода (Домен) если используется 1 файл перевода + """ + self.__catalog = os.path.abspath('/usr/share/calculate/i18n') + """ Путь к каталогу переводов (в этом каталоге + ru_RU/LC_MESSAGES в котором файл перевода) + """ + env = os.environ + if l == "" and env.has_key('LANG'): + l = env['LANG'].split('.')[0].split("_")[0] + """ Определение языка """ + self.__modnames = {} + """ Словарь переведенных модулей + ключ --- имя модуля + значение --- был ли модуль переведен (1 или 0) + """ + self.__l = l + """Язык перевода для всех модулей""" + + def __translate(self,message): + """Метод translate возвращает полученное значение без +изменений""" + return message + + def setLanguage(self,module): + """ Установка языка перевода для модуля module. + + параметр --- экспортируемый модуль python + если в этом модуле экспортируются другие модули + то язык устанавливается и для них + Метод запускается после экспорта модуля который будем переводить + """ + t = vars(module) + for i in dir(module): + q = str(t[i]) + if 'module' in q and not '__' in i and not '/usr/lib' in q\ + and not 'built-in' in q : + mod = vars(module)[i] + self.__setLang(mod) + return self.__setLang(module) + + def __setLang(self,module): + """ Установка языка перевода для модуля module. + + В случае нахождения файла перевода возвращает истину. + Во всех случаях устанавливает метод перевода для модуля. + Если нет файла перевода метод перевода возвращает то же + значение что получает + """ + if module.__name__ in self.__modnames.keys(): + return True + + if self.nameDomain == '': + if module.__name__ == "__main__": + nameDomain = module.__file__.split('.')[0] + else: + nameDomain = module.__name__ + else: + nameDomain = self.nameDomain + + if self.__l == 'en': + module._ = self.__translate + ret = 1 + else: + la = [] + la.append(self.__l) + if gettext.find(nameDomain,self.__catalog,la): + """Если найден словарь то инициализируем переводчик""" + transl = gettext.translation(nameDomain\ + ,self.__catalog,la) + + #module._ = transl.ugettext + module._ = transl.gettext + ret = 1 + else: + module._ = self.__translate + ret = 0 + self.__modnames[module.__name__] = ret + return ret + + def getTranslatorByName(self,namemodule): + """ Метод который по имени модуля определяет, был ли модуль с этим + именем переведен + """ + if self.__modnames.has_key(namemodule): + return self.__modnames[namemodule] + return 0 + + def setGlobalDomain(self, nameDomain): + """ Метод для установки домена перевода (глобально для всех файлов) + """ + self.GP[0] = nameDomain + self.nameDomain = self.GP[0] + return True + + def setLocalDomain(self, nameDomain): + """ Метод для установки домена перевода (локально для одного файла) + """ + self.nameDomain = nameDomain + return True + +############################################################################## +# Перевод модуля на другой язык +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +############################################################################## +class opt: + def __init__(self,shortOpt,longOpt = []): + """ Длинные и короткие опции командной строки допустимые в программе + a - короткая опция + >program -a + + a: - короткая опциия со значением + >program -a 10 + + a:: - короткая опциия у которой может быть или не быть значение + >program -a + >program -a 15 + + "ha:" - значение параметра shortOpt + две опции h - без значения, a - со значением + + help - длинная опция без значения + test= - длинная опция со значением + + ["help","test="] - значение парамера longOpt + >program -a + две опции help - без значения, test - со значением + """ + self.shortOpt = shortOpt + self.longOpt = longOpt + self.sysArgv = sys.argv[1:] + + def getopt(self): + try: + opts, args = getopt.getopt(self.sysArgv,self.shortOpt,self.longOpt) + except getopt.GetoptError: + self.handlerErrOpt() + sys.exit(1) + for option, value in opts: + if len(option) == 2: + option = option[1:] + else: + option = option[2:] + self.handlerOpt(option,value) + for param in args: + self.handlerParam(param) + + def handlerErrOpt(self): + # Обработчик в случае неправильных параметров + pass + + def handlerOpt(self,option,value): + # Обработчик (параметр значение) + pass + + def handlerParam(self,param): + # Обработчик хвостов (значение) + pass + +############################################################################### +import cl_profile +class iniParser(cl_profile._error): + """Класс для работы с ini файлами + + """ + def __init__(self, iniFile): + # название ini файла + self.iniFile = iniFile + # права создаваемого ini-файла + self.mode = 0640 + # Cоответствует ли формат файла нужному + self.checkIni = None + + def setMode(self, mode): + """установка прав создаваемого ini-файла""" + self.mode = mode + + def openIniFile(self): + if not os.access(self.iniFile, os.R_OK): + return "" + FD = open(self.iniFile, "r") + textIni = FD.read() + FD.close() + return textIni + + def writeIniFile(self, txtConfig): + if not os.path.exists(self.iniFile): + fd = os.open(self.iniFile, os.O_CREAT) + os.close(fd) + os.chmod(self.iniFile, self.mode) + if not os.path.exists(self.iniFile): + self.setError(_("Unable to create file") + ": " + self.iniFile) + return False + FD = open(self.iniFile, "r+") + FD.truncate(0) + FD.seek(0) + FD.write(txtConfig) + FD.close() + + def setVar(self, strHeader, dictVar): + """Заменяет или добавляет область и переменные + + Добавляет область в ini-файл или объединяет с существующей + strHeader - имя области + dictVar - словарь переменных + """ + textIni = self.openIniFile() + nameFomat = self.checkIniFile(textIni) + if not nameFomat: + return False + if type(strHeader) in (tuple, list): + # формат plasma + classObj = cl_profile.plasma + else: + if nameFomat == "plasma": + self.setError(_("In the file %s (format - 'plasma'), \ + write the variable in the format 'samba'")\ + %self.iniFile) + return False + # формат samba + classObj = cl_profile.samba + # создаем объект + # и записываем в него содержимое ini-файла + objIni = classObj(textIni) + # создаем текст из строки заголовка и + # словаря переменных области + txtConfig = objIni.createTxtConfig(strHeader, dictVar) + # создаем объект и записываем в него текст + objIniAdd = classObj(txtConfig) + # объединяем объекты для получения результирующего текста + objIni.join(objIniAdd) + # получаем текст + txtConfig = objIni.getConfig().encode("UTF-8") + # записываем его в ini файл + self.writeIniFile(txtConfig) + return True + + def isEmptyFile(self, textIni): + """Является ли файл пустым""" + if not textIni.strip(): + return True + else: + return False + + def checkIniFile(self, textIni): + """Проверка на правильность формата файла""" + if self.checkIni == None: + # Ошибка + if textIni == False: + self.checkIni = False + return False + self.checkIni = "samba" + # В файле есть данные + if not self.isEmptyFile(textIni): + try: + objIni = cl_profile.plasma(textIni) + except: + self.setError(_("Incorrect format file") + ": " + \ + self.iniFile) + self.checkIni = False + return self.checkIni + allAreas = objIni.docObj.getAllAreas() + for xmlArea in allAreas: + parentNode = xmlArea.parentNode + if parentNode and parentNode.tagName == "area": + self.checkIni = "plasma" + break + if self.checkIni == "samba": + objIni = cl_profile.samba(textIni) + xmlBody = objIni.docObj.getNodeBody() + if not xmlBody.firstChild: + self.checkIni = False + return self.checkIni + + def delVar(self, strHeader, nameVar): + """Удаляем переменную из ini файла""" + delNameVar = "!%s" %(nameVar) + dictVar = {delNameVar:"del"} + res = self.setVar(strHeader, dictVar) + return res + + def delArea(self, strHeader): + """Удаляем область из ini файла""" + if type(strHeader) in (tuple, list): + # Формат plasma + delStrHeader = strHeader[:] + delStrHeader[-1] = "!%s"%delStrHeader[-1] + else: + # Формат samba + delStrHeader = "!%s" %(strHeader) + dictVar = {"del":"del"} + res = self.setVar(delStrHeader, dictVar) + return res + + def getVar(self, strHeader, nameVar): + """Получаем значение переменной из ini-файла""" + textIni = self.openIniFile() + nameFomat = self.checkIniFile(textIni) + if not nameFomat: + return False + formatPlasma = False + if type(strHeader) in (tuple, list): + # формат plasma + classObj = cl_profile.plasma + formatPlasma = True + else: + if nameFomat == "plasma": + self.setError(_("In the file %s (format - 'plasma'), \ + get the variable in the format 'samba'")\ + %self.iniFile) + return False + # формат samba + classObj = cl_profile.samba + # создаем объект и записываем в него содержимое ini-файла + objIni = classObj(textIni) + # получаем ноду body + xmlBody = objIni.docObj.getNodeBody() + flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader, + formatPlasma) + if flagFound and xmlBody: + if formatPlasma: + strHeader = strHeader[-1] + # находим в области переменную + res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody) + else: + res = False + if res is False: + return "" + else: + return res + + + def getLastNode(self, objIni, xmlBody, strHeader, formatPlasma): + """Ищет область в XML в которой область с переменными""" + flagFound = True + lenStrHeader = len(strHeader) + if formatPlasma and lenStrHeader>0: + xmlAreas = [xmlBody] + for i in xrange(lenStrHeader-1): + flagFound = False + for xmlArea in xmlAreas: + xmlAreas = objIni.docObj.getArea(strHeader[i], xmlArea) + if xmlAreas: + flagFound = True + break + if xmlAreas: + xmlBody = xmlAreas[0] + return flagFound,xmlBody + + def getAreaVars(self, strHeader): + """Получаем все переменнные области из ini-файла""" + textIni = self.openIniFile() + nameFomat = self.checkIniFile(textIni) + if not nameFomat: + return False + formatPlasma = False + if type(strHeader) in (tuple, list): + # формат plasma + classObj = cl_profile.plasma + formatPlasma = True + else: + if nameFomat == "plasma": + self.setError(_("In the file %s (format - 'plasma'), \ + get all variables in the format 'samba'")\ + %self.iniFile) + return False + # формат samba + classObj = cl_profile.samba + # создаем объект типа samba и записываем в него содержимое ini-файла + objIni = classObj(textIni) + # получаем ноду body + xmlBody = objIni.docObj.getNodeBody() + flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader, + formatPlasma) + if flagFound and xmlBody: + if formatPlasma: + strHeader = strHeader[-1] + # если находим область то выдаем словарем все переменные иначе False + res = objIni.docObj.getAreaFields(strHeader, xmlBody) + else: + res = False + if res is False: + return {} + else: + return res + + def getAllSectionNames(self): + """Получаем все имена секций определенных в ini файле + + Если формат ini файла plasma то имя секции - + имена нескольких секций через запятую + """ + textIni = self.openIniFile() + nameFomat = self.checkIniFile(textIni) + if not nameFomat: + return False + if nameFomat == "samba": + # создаем объект типа samba и записываем в него содержимое ini-файла + objIni = cl_profile.samba(textIni) + elif nameFomat == "plasma": + # создаем объект типа plasma и записываем в него содержимое + # ini-файла + objIni = cl_profile.plasma(textIni) + else: + return [] + xmlNodes = objIni.docObj.getAllAreas() + # Имена секций ini файла + namesSection = [] + if nameFomat == "plasma": + for xmlNode in xmlNodes: + nSect = objIni.docObj.getNameArea(xmlNode) + if nSect: + namesSect = [nSect] + parentNode = xmlNode.parentNode + while parentNode != objIni.docObj.body: + nameSect = objIni.docObj.getNameArea(parentNode) + if nameSect: + namesSect.append(nameSect) + parentNode = parentNode.parentNode + else: + return [] + namesSection.append(",".join(reversed(namesSect))) + elif nameFomat == "samba": + # получаем ноду body + for xmlNode in xmlNodes: + nSect = objIni.docObj.getNameArea(xmlNode) + if nSect: + namesSection.append(nSect) + return namesSection + +############################################################################## + +class var: + '''Объект "Переменная окружения"''' + # название сервиса которому принадлежит переменная + #(Global, Builder, Client, Server итд) + service = None + # значение переменной + value = "" + # режим записи (атрибут mode) + mode = "r" + # переменная для внутреннего использования (official) + official = False + # количество вызовов метода заполнения + countFill = 0 + # объект в котором создан этот объект + parentObj = None + # запускать или нет метод заполнения + fillStart = True + # dynamic = True то переменная динамическая при повтороном запуске + # запускается метод заполнения + # метод заполнения не запускается только если fillStart = False + # (осторожно возможно зацикливание программы если методы заполнения + # переменных используют методы друг друга) + dynamic = False + + def __init__(self, parentObj): + # словарь зависимых переменных {имя:значение} + self.dependValues = {} + # тип переменной (атрибут type) + self.type = ('default') + # список допустимых значений переменных (select) + self.select = () + # объект который создал этот объект + self.parentObj = parentObj + + def is_update(self): + #Нужно ли перезапускать метод заполнения (если зависимые переменные + #обновились то нужно) + upd = False + for depVarName in self.dependValues.keys(): + value = self.parentObj.__getattribute__(depVarName).Get() + if self.dependValues[depVarName] != value: + self.dependValues[depVarName] =\ + self.parentObj.__getattribute__(depVarName).value + upd = True + break + return upd + + def Get(self): + """Получение значения переменной""" + if not self.fillStart: + return self.value + if self.dynamic: + self.value = self.Fill() + return self.value + if not self.value: + if self.countFill>0: + return self.value + self.countFill += 1 + self.value = self.Fill() + if self.dependValues and self.is_update(): + self.countFill += 1 + self.value = self.Fill() + return self.value + + def Set(self, value): + """Запись значения переменной""" + self.value = value + return self.value + + def Fill(self): + """Заполнение переменной в далнейшем заменяем методом заполнения""" + return self.value + + +class DataVars(object): + class DataVarsError(Exception): + """Класс ошибок""" + pass + # добавляем пути к модулям если они не добавлены + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-server/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-server/pym')) + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-lib/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-lib/pym')) + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-builder/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-builder/pym')) + # Импортируемые модули - (раздел: модуль переменных, модуль заполнения + #переменных) + __modlist={'Global':('cl_vars','cl_fill'), + 'Server':('cl_vars_server','cl_fill_server'), + 'Builder':('cl_vars_builder','cl_fill_builder'), + 'Client':('cl_vars_client','cl_fill_client'), + } + def __init__(self): + #self.t1 = fillVars() + #self.t1.Get = self.Get + #self.t1.Set = self.Set + # Для нахождения зависимостей переменных + self.__levelNumber = 0 + self.__LevelsVar = [] + # Для хранения импортированных модулей и объектов + #[(cекция,импортированный модуль переменных, объект заполнения),] + self._importList = [] + self._importData("Global") + + def _importData(self, section): + """Импортирует модули с переменными и модули с функциями заполнения + + section секция раздела (Global, Server, Client итд) + создает необходимые структуры данных + """ + if not section in self.__modlist.keys(): + raise self.DataVarsError(_("Unsupported section %s")%section) + modVar = self.__modlist[section][0] + modFill = self.__modlist[section][1] + # Импортируем класс описания переменных и класс заполнения + try: + exec ("import %s" % (modVar)) + except ImportError, e: + err1 = _("Error in import module %s")%modVar + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + flagFindFillModule = True + try: + exec ("import %s" % (modFill)) + except ImportError, e: + if "No module named" in str(e): + flagFindFillModule = False + else: + err1 = _("Error in import module %s")%modFill + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + if flagFindFillModule: + # Создаем объект с методами заполнения переменных + exec("fillObj = %s.fillVars()" %modFill) + # Подключаем методы получения и записи переменных + fillObj.Get = self.Get + fillObj.Set = self.Set + else: + fillObj = False + # Заполняем self._importList + exec("self._importList.insert(0,(section,%s,fillObj))"%(modVar)) + + def __findVarData(self, nameVar): + """Находит данные для создания объекта переменная в модулях и + + объектах + """ + # Ищем переменную в модуле + dataVar = False + e = False + for section, moduleVar, fillobj in self._importList: + try: + exec("dataVar=moduleVar.Data.%s"%nameVar) + except AttributeError, e: + pass + if dataVar: + break + if dataVar == False: + err1 = _("Not found variable %s")%nameVar + err2 = "" + if e: + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + dataVar['service'] = section + # Ищем метод в объекте методов заполнения + nameMethod = "get_" + nameVar + flagFindMetod = False + for section, moduleVar, fillobj in self._importList: + if fillobj: + if nameMethod in dir(fillobj): + flagFindMetod = True + method = fillobj.__getattribute__(nameMethod) + break + if flagFindMetod: + return (dataVar,method) + else: + return (dataVar,False) + + def __setAttributesVar(self, var ,nameVar, dict): + """Установка аттрибутов для созданного объекта var + + название аттрибута и его значение берется из словаря dict + """ + dict['type'] = nameVar.split('_') + if not set(dict.keys()) <= set(dir(var)): + raise self.DataVarsError(\ + _("error initalize variable %s, incorrect data")%nameVar) + for nameAttr in dict.keys(): + setattr(var,nameAttr, dict[nameAttr]) + return True + + def __Get(self, nameVar): + ret = "" + self.__LevelsVar.append((self.__levelNumber, nameVar)) + self.__levelNumber += 1 + #nameMethod = "get_" + nameVar + if hasattr(self, nameVar): + ret = self.__getattribute__(nameVar).Get() + elif self.__findVarData(nameVar): + dictVar, methodFill =self.__findVarData(nameVar) + varobj = var(self) + # Устанавливаем аттрибуты + self.__setAttributesVar(varobj, nameVar, dictVar) + if methodFill: + varobj.Fill = methodFill + self.__setattr__(nameVar, varobj) + ret = self.__getattribute__(nameVar).Get() + self.__levelNumber -= 1 + if self.__levelNumber == 0 and\ + self.__getattribute__(nameVar).fillStart and\ + len(self.__LevelsVar)>1: + links = self.__getLinks(self.__LevelsVar) + for name in links.keys(): + for nameLink in links[name].keys(): + val = self.__getattribute__(nameLink).Get() + self.__getattribute__(name).dependValues[nameLink] = val + if self.__levelNumber == 0: + self.__LevelsVar = [] + return ret + + def Get(self, nameVar): + return self.__Get(nameVar) + + + def __Set(self, nameVar, value, force=False): + nameMethod = "get_" +nameVar + if not hasattr(self, nameVar) and self.__findVarData(nameVar): + dictVar, methodFill =self.__findVarData(nameVar) + varobj = var(self) + # Устанавливаем аттрибуты + self.__setAttributesVar(varobj, nameVar, dictVar) + if methodFill: + varobj.Fill = methodFill + self.__setattr__(nameVar, varobj) + if hasattr(self, nameVar): + if not force and "r" in getattr(self, nameVar).mode: + print _("Attempt to rewrite a variable for reading:%s")\ + %nameVar + return False + self.__getattribute__(nameVar).fillStart = False + return self.__getattribute__(nameVar).Set(value) + + def Set(self, nameVar, value, force=False): + return self.__Set(nameVar, value, force) + + def __frame(self, lVar): + """получить список областей зависимости переменных""" + data = [] + if not lVar: + return data + firstLevel = lVar[0][0] + for level, name in lVar[1:]: + if level> firstLevel: + data.append((level, name)) + else: + break + return data + + def __getLinks(self, lVar): + """Получить список переменных и от каких переменных они зависят + + на вход список [(уровень рекурсии, название переменной),] + """ + links = {} + frames = {} + levelLinks = {} + lVarFr = lVar + for level, name in lVar: + fr = self.__frame(lVarFr) + if not frames.has_key(name): + frames[name] = fr + levelLinks[name] = level+1 + lVarFr = lVarFr[1:] + for name in frames.keys(): + level = levelLinks[name] + fr = frames[name] + links[name] = {} + for lv, nm in fr: + if level == lv: + links[name][nm] = "" + return links + + def __getPathCalculateIni(self): + """Получить пути до ini файлов""" + return self.Get('cl_env_path') + + def __getSection(self, vname): + """секция для записи в ini файл переменной + + vname - имя переменной + """ + if hasattr(self, vname): + if getattr(self, vname).service == 'Global': + return 'calculate' + else: + return getattr(self, vname).service.lower() + + def __writeVarValue(self, vname, val, location, header): + '''Записать значение в calculate.ini + + Параметры: + vname имя переменной + val значение переменной + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + + Возвращаемые значение: + True запись успешна + False запись не удалсь + ''' + # получаем все пути до ini файлов + calculate_ini = self.__getPathCalculateIni() + # получаем полный путь до файла ini + if location == 'default': + name_calculate_ini = calculate_ini[2] + elif location == 'local': + name_calculate_ini = calculate_ini[1] + elif location == 'remote': + name_calculate_ini = calculate_ini[0] + else: + return False + # извлекаем из полного имени файла путь + onlydir = os.path.split(name_calculate_ini)[0] + try: + # проверяем чтобы путь до ини файла существовал + if not os.path.exists(onlydir): + # создаем его если отсутствует + os.makedirs(onlydir) + except OSError (nerr,msg): + print nerr, msg + return False + config = iniParser(name_calculate_ini) + # Получаем секцию конфигурационного файла + if not header: + header = self.__getSection(vname) + return config.setVar(header,{vname: cl_utils.convertStrListDict(val)}) + + def __deleteVarValue(self, vname, location, header): + '''Удалить переменную в calculate.ini + + Параметры: + vname имя переменной + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + + Возвращаемые значение: + True удалено успешно + False удаление не удалсь + ''' + # получаем все пути до ini файлов + calculate_ini = self.__getPathCalculateIni() + # получаем полный путь до файла ini + if location == 'default': + name_calculate_ini = calculate_ini[2] + elif location == 'local': + name_calculate_ini = calculate_ini[1] + elif location == 'remote': + name_calculate_ini = calculate_ini[0] + else: + return False + # извлекаем из полного имени файла путь + onlydir = os.path.split(name_calculate_ini)[0] + # проверяем чтобы путь до ини файла существовал + if not os.path.exists(onlydir): + return False + config = iniParser(name_calculate_ini) + # Получаем секцию конфигурационного файла + if not header: + header = self.__getSection(vname) + if not header: + self.Get(vname) + header = self.__getSection(vname) + # Удаляем переменную + retDelVar = config.delVar(header, vname) + retDelArea = True + if not config.getAreaVars(header): + retDelArea = config.delArea(header) + if retDelArea and retDelVar: + return True + else: + return False + + def Write(self, vname, val, force=False, location='default',header=False): + '''Установить и записать значение переменной в ini файл + + Параметры: + vname имя переменной + val значение переменной + force "принудительный режим" + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + ''' + if self.__Set(vname, val, force)!= False: + if not val.strip(): + self.__deleteVarValue(vname, location, header) + self.__writeVarValue(vname, val, location, header) + return True + return False + + def Delete(self, vname, location='default', header=False): + '''Удалить переменную в calculate.ini + + Параметры: + vname имя переменной + location расположение ini файла ('default', 'local', 'remote') + + Возвращаемые значение: + True удалено успешна + False удаление не удалсь + ''' + return self.__deleteVarValue(vname, location, header) + + def __getActiveSections(self): + """активные секции в ini файле""" + act_section = [] + for service,t,t in self._importList: + if service == "Global": + act_section.append('calculate') + else: + act_section.append(service.lower()) + return act_section + + def flIniFile(self): + '''Заместить значение переменных значениями из ини файлов + + Возвращаемые значения: + cловарь импортированных переменных - переменные считаны + False - файл не был обнаружен + ''' + #Cловарь переменных из ini файлов + importVars = {} + calculate_ini = self.__getPathCalculateIni() + # активные секции (секции из которых будут использованы переменные) + act_section = self.__getActiveSections() + set_act_section = set(act_section) + i = 0 + locations = ['remote','local','default'] + for name_calculate_ini in calculate_ini: + # проверить сущестование ini файла + if os.path.exists(name_calculate_ini): + # получить объект настроенный на ini + config = iniParser(name_calculate_ini) + # получаем все секции из конфигурационного файла + allsect = config.getAllSectionNames() + if not allsect: + continue + # находим встречающиеся у обоих секции + act_sect = tuple(set(allsect)& set_act_section) + # словарь переменных для ini - файла + importFileVars = {} + # получаем все переменные из всех секций + for section in act_sect: + allvars = config.getAreaVars(section) + if allvars == False: + return False + # словарь переменных для ini - файла + importFileVars = {} + # принудительно переписать все переменные окружения + # полученные из ini + for (k,v) in allvars.items(): + k = k.encode("UTF-8") + value = cl_utils.convertStrListDict(v.encode("UTF-8")) + self.Set(k, value, True) + importFileVars[k] = value + if i < 3: + importVars[locations[i]] = importFileVars + i += 1 + return importVars + + def flServer(self, **args): + '''Заполнить конфигурацию переменных, для ldap''' + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Server") + + def flClient(self, **args): + '''Заполнить конфигурацию переменных, для клиента''' + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Client") + + def flBuilder(self, **args): + '''Заполнить конфигурацию переменных, для билдера''' + self.Set('setup_pass','builder',True) + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Builder") + + def flInstall(self, **args): + '''Заполнить конфигурацию переменных для инсталятора''' + self.Set('setup_pass','install',True) + + + def defined(self, vname): + return True + + + + def exists(self, nameVar): + """ Определяет существует ли переменная с таким имененм + """ + if hasattr(self, nameVar): + return True + foundVar = False + # Ищем переменную в импортируемых модулях + for section, moduleVar, fillobj in self._importList: + if moduleVar.Data.__dict__.has_key(nameVar): + foundVar = True + break + return foundVar + + def getVars(self, type_names=None): + ret = {} + for section, moduleVar, fillobj in self._importList: + dataVar=moduleVar.Data + dictVars = dir(dataVar) + for nameVar in dictVars: + if not "__" in nameVar: + if not (getattr(dataVar,nameVar).has_key("official") and\ + getattr(dataVar,nameVar)['official']): + self.Get(nameVar) + if type_names: + #type_names.sort() + varType =list(getattr(dataVar,nameVar)['type']) + #varType.sort() + #print type_names + #print varType + #print + if not set(type_names)<=set(varType): + continue + ret[nameVar] = getattr(self,nameVar) + return ret + + #распечатать список переменных с значениями + def printVars(self,type_names=None): + var=None + var=self.getVars(type_names) + mlen_name=0; + mlen_type=0; + mlen_mode=0; + for i,j in var.items(): + if len(i)>mlen_name: + mlen_name=len(i) + #if len(str(j.type))>mlen_type: + #mlen_type=len(str(j.type)) + vtype=str(type(var[i].value)).split(" ")[1][1] + if not '[' in var[i].mode: + if vtype in ['d','l']: + mode="[%s%s]"%(var[i].mode.lower(),vtype) + else: + mode="[%s]"%(var[i].mode.lower()) + var[i].mode=mode + if len(mode)>mlen_mode: + mlen_mode=len(mode) + plist=var.keys() + plist.sort() + br = cl_utils.fillstr("-",mlen_name) + " " +\ + cl_utils.fillstr("-",mlen_mode) + " " + cl_utils.fillstr("-",10) + #cl_utils.fillstr("-",mlen_type) + " " +\ + + print "The list of variables:" + print "var name".center(mlen_name),\ + "Mode","Value" + #"Type".center(mlen_type),\ + + + print br + for i in plist: + #if var[i].value is None: + #continue + p_val=var[i].value + if var[i].official: + continue + cl_utils.columnWrite( i, mlen_name, var[i].mode.lower(), + mlen_mode, + #str(var[i].type), + #mlen_type, + p_val) + print br +############################################################################## +class glob_attr: + """Глобальные аттрибуты для методов заполнения переменных""" + + def _runos(self,cmd, ret_first=None, env={}): + """Вернуть результат выполнения команды ОС""" + if not env: + envDict = {} + env.update(os.environ.items() + [("PATH",cl_utils.getpathenv())] +\ + env.items()) + retCode, programOut = cl_utils.runOsCommand(cmd, None, ret_first, env) + if not retCode: + return programOut + return False diff --git a/build/lib/calculate-lib/pym/cl_fill.py b/build/lib/calculate-lib/pym/cl_fill.py new file mode 100644 index 0000000..c5dea91 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_fill.py @@ -0,0 +1,346 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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 re +import os +import types +import cl_utils +import cl_base + +class fillVars(object, cl_base.glob_attr): + + def get_os_net_domain(self): + ''' Определим домен''' + domain=self._runos("hostname -d 2>&1") + if not domain: + print _("Error:") + " " +_("Not found domain name") + print _("Command 'hostname -d' returns an empty value") + cl_base.exit(1) + elif re.search("^hostname: ",domain): + return "local" + else: + return domain + + def get_os_linux_shortname(self): + '''Получить переменную короткого названия системы''' + path = '/etc/calculate/calculate.ini' + if os.path.exists(path): + FD = open(path) + data = FD.readlines() + FD.close() + shortNameList = filter(lambda y:y, + map(lambda x:\ + len(x.split("="))==2 and\ + x.split("=")[0]=="calculate" and\ + x.split("=")[1].strip(), data)) + if shortNameList: + return shortNameList[0] + gentooFile = "/etc/gentoo-release" + shortName = "Linux" + if os.path.exists(gentooFile): + shortName = "Gentoo" + return shortName + + def get_os_linux_name(self): + """полное название системы""" + linuxShortName = self.Get("os_linux_shortname") + if linuxShortName: + dictLinuxName = {"CLD":"Calculate Linux Desktop", + "CLDX":"Calculate Linux Desktop", + "CLDG":"Calculate Linux Desktop", + "CDS":"Calculate Directory Server", + "Gentoo":"Gentoo"} + if linuxShortName in dictLinuxName.keys(): + return dictLinuxName[linuxShortName] + else: + return "Linux" + else: + return "Linux" + + def get_os_linux_subname(self): + """постфикс к названию системы""" + linuxShortName = self.Get("os_linux_shortname") + if linuxShortName: + dictLinuxSubName = {"CLD":"KDE", "CLDX":"XFCE", "CLDG":"GNOME"} + if linuxShortName in dictLinuxSubName.keys(): + return dictLinuxSubName[linuxShortName] + else: + return "" + else: + return "" + + def get_os_linux_ver(self): + '''Получить версию системы''' + path = '/etc/calculate/calculate.ini' + if os.path.exists(path): + FD = open(path) + data = FD.readlines() + FD.close() + shortNameList = filter(lambda y:y, + map(lambda x:\ + len(x.split("="))==2 and\ + x.split("=")[0]=="linuxver" and\ + x.split("=")[1].strip(), data)) + if shortNameList: + return shortNameList[0] + gentooFile = "/etc/gentoo-release" + systemVersion = "" + flagGentoo = False + if os.path.exists(gentooFile): + gentooLink = "/etc/make.profile" + if os.path.islink(gentooLink): + systemVersion = os.readlink(gentooLink).rpartition("/")[2] + flagGentoo = True + if not flagGentoo: + kernelVersion=self._runos("uname -r") + if kernelVersion: + systemVersion = kernelVersion.partition("-")[0] + return systemVersion + + def get_os_net_hostname(self): + '''Считать имя компьютера net_host''' + hostname=self._runos("hostname -s 2>&1") + if not hostname: + return "" + if re.search("^hostname: ",hostname): + hostname=self._runos("hostname 2>&1") + if not hostname: + return "" + if re.search("^hostname: ",hostname): + return self.Get('os_linux_shortname') + else: + if hostname=='livecd': + return self.Get('os_linux_shortname') + return hostname + + # все ip + def get_os_net_ip(self): + """все ip компьютера, разделитель запятая""" + IPs = [] + netInterfaces=cl_utils.getdirlist("/sys/class/net/") + for i in netInterfaces: + res = self._runos("/sbin/ifconfig %s"%i) + if not res: + break + for line in res: + searchIP = re.search('addr:([0-9\.]+).+Bcast:', line) + if searchIP: + # ip адрес + ip = searchIP.groups()[0] + IPs.append(ip) + return ",".join(IPs) + + # Разрешенные сети (в данном случае все сети) + def get_os_net_allow(self): + """Разрешенные сети разделитель запятая""" + + def getNet(ip, mask): + """По ip и маске получаем сеть""" + octetsMult = (0x1, 0x100, 0x10000, 0x1000000) + octetsIp = map(lambda x: int(x), ip.split(".")) + octetsMask = map(lambda x: int(x), mask.split(".")) + ipNumb = 0 + for i in octetsMult: + ipNumb += octetsIp.pop()*i + maskNumb = 0 + for i in octetsMult: + maskNumb += octetsMask.pop()*i + startIpNumber = maskNumb&ipNumb + x = startIpNumber + nMask = lambda y: len(filter(lambda x: y >> x &1 ,range(32))) + return "%s.%s.%s.%s/%s"\ + %(x>>24, x>>16&255, x>>8&255, x&255, nMask(maskNumb)) + + networks=[] + netInterfaces=cl_utils.getdirlist("/sys/class/net/") + flagError = False + for i in netInterfaces: + res = self._runos("/sbin/ifconfig %s"%i) + if not res: + flagError = True + break + for j in res: + s_ip=re.search('addr:([0-9\.]+).+Bcast:.+Mask:([0-9\.]+)' ,j) + if s_ip: + ip, mask = s_ip.groups() + networks.append(getNet(ip, mask)) + if flagError: + return "" + return ",".join(networks) + + def get_os_locale_locale(self): + """локаль (прим: ru_RU.UTF-8)""" + if os.environ.has_key("LANG"): + return os.environ["LANG"] + else: + return "en_US.UTF-8" + + def get_os_locale_lang(self): + """язык (прим: ru_RU)""" + locale = self.Get("os_locale_locale") + if locale: + return locale.split(".")[0] + return "" + + def get_os_locale_language(self): + """язык (прим: ru)""" + lang = self.Get("os_locale_lang") + if lang: + return lang.split("_")[0] + return "" + + def get_os_locale_xkb(self): + """раскладка клавиатуры для X""" + path = '/etc/conf.d/keymaps' + mapDict={"by":"us,by", + "be-latin1":"be,us", + "br-abnt2":"br,us", + "cf":"ca,us", + "dk-latin1":"dk,us", + "fr-latin9":"fr,us", + "de-latin1":"de,us", + "is-latin1":"is,us", + "it":"it,us", + "no-latin1":"no,us", + "pl":"pl,us", + "-u ru4":"us,ru(winkeys)", + "es euro2":"es,us", + "sv-latin1":"se,us", + "ua-utf":"us,ua(winkeys)", + "uk":"gb,us", + "us":"us"} + if os.path.exists(path): + FD = open(path) + data = FD.readlines() + FD.close() + shortNameList = filter(lambda y:y, + map(lambda x:\ + len(x.split("="))==2 and\ + x.split("=")[0]=="KEYMAP" and\ + x.split("=")[1].replace('"',"").strip(),\ + data)) + if shortNameList: + if shortNameList[0] in mapDict.keys(): + return mapDict[shortNameList[0]] + lang = self.Get("os_locale_lang") + # Языки: + # Португальский - pt_BR + # Французский - fr_FR + # Немецкий - de_DE + # Итальянский - it_IT + # Польский - pl_PL + # Русский - ru_RU + # Испанский - es_ES + # Украинский - uk_UA + # Английский - en_US + xkbDict = {'pt_BR':'br,us', + 'fr_FR':'fr,us', + 'de_DE':'de,us', + 'it_IT':'it,us', + 'pl_PL':'pl,us', + 'ru_RU':'us,ru(winkeys)', + 'es_ES':'es,us', + 'uk_UA':'us,ua(winkeys)', + 'en_US':'us'} + if lang: + if xkbDict.has_key(lang): + return xkbDict[lang] + return "" + + def get_os_locale_xkbname(self): + """названия используемых раскладок клавиатуры для X""" + localeXkb = self.Get("os_locale_xkb") + if localeXkb: + return localeXkb.split("(")[0] + return "" + + def get_os_arch_machine(self): + """архитектура процессора""" + march = self._runos("uname -m") + if not march: + return "" + return march + + def get_os_root_dev(self): + """корневой раздел файловой системы""" + for record in open('/proc/cmdline','rb').readlines(): + re_res=re.search('^root=(\/dev\/[a-z]+[0-9]).*',record.strip()) + if re_res: + return re_res.group(1) + else: + mountLunes = self._runos("mount") + if not mountLunes: + return "" + if type(mountLunes) == types.ListType: + root_dev = mountLunes[0].split("on / type")[0].strip() + if root_dev: + return root_dev + return "" + + def get_os_root_type(self): + """тип носителя (ram, hdd, livecd)""" + mountLunes = self._runos("mount") + if not mountLunes: + return "" + rootType = "hdd" + if type(mountLunes) == types.ListType: + flagCD = False + for line in mountLunes: + if "/dev/loop0 on / type" in line: + rootType = "ram" + break + elif "/dev/loop0 on /newroot/mnt/livecd type" in line: + rootType = "ram" + flagCD = True + break + if rootType == "ram": + if os.path.exists("/mnt/livecd") or flagCD: + rootType = "livecd" + return rootType + rootDev = self.Get("os_root_dev") + if rootType != "ram" and rootDev: + slpRootDev = rootDev.split("/dev/") + if len(slpRootDev) == 2: + rDev = slpRootDev[1] + devLines = self._runos("ls -la /dev/disk/by-id/", None, + {"LANG":"C"}) + if not devLines: + return "" + if type(devLines) == types.ListType: + for line in devLines: + if rDev in line and "usb-" in line: + rootType = "usb-hdd" + break + if rootType == "ram": + rootType = "hdd" + return rootType + else: + return "" + + def get_hr_virtual(self): + """Название виртуальной машины (virtualbox, vmware, qemu)""" + pciLines = self._runos("/usr/sbin/lspci") + if not pciLines: + return False + virtSysDict = {'VirtualBox':'virtualbox', + 'VMware':'vmware', + 'Qumranet':'qemu'} + virtName = '' + for vName in virtSysDict.keys(): + if filter(lambda x: vName in x, pciLines): + virtName = virtSysDict[vName] + break + return virtName diff --git a/build/lib/calculate-lib/pym/cl_log.py b/build/lib/calculate-lib/pym/cl_log.py new file mode 100644 index 0000000..46acdf7 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_log.py @@ -0,0 +1,59 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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 time + +class log(): + """Класс для записи в лог""" + # Директория хранения логов + logDir = "/var/log/calculate" + def __init__(self,fileName, addDataTime=True): + self.logFile = os.path.join(self.logDir,fileName) + if not os.path.exists(self.logDir): + os.makedirs(self.logDir) + self.addDataTime = addDataTime + + def addLog(self, textLog): + """Добавляет текст в лог файл""" + if not os.path.exists(self.logFile): + try: + fd = os.open(self.logFile, os.O_CREAT,0600) + os.close(fd) + except: + print "Error creating file %s"%self.logFile + return False + textWrite = textLog + if not textLog[-1:] == "\n" : + textWrite = "%s\n"%textLog + if self.addDataTime: + textWrite = "%s %s"%(time.strftime("%d/%m/%Y %H:%M:%S",\ + time.localtime()),textWrite) + try: + FD = open (self.logFile, "a") + FD.write(textWrite) + FD.close() + except: + print "Error writing to file %s"%self.logFile + return False + return True + + def writeError(self, textLog): + """Добавляет сообщение об ошибке в log""" + return self.addLog("ERROR: %s" %textLog) + + def writeSuccess(self, textLog): + """Добавляет сообщение об успехе в log""" + return self.addLog("SUCCESS: %s" %textLog) diff --git a/build/lib/calculate-lib/pym/cl_profile.py b/build/lib/calculate-lib/pym/cl_profile.py new file mode 100644 index 0000000..8d8ab64 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_profile.py @@ -0,0 +1,7136 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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 +import os +import cl_base +import stat +import re +import xml.dom.minidom +from xml import xpath +import subprocess +import types +import copy +import random +import string +import time + +tr = cl_base.lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + + +class _error: + # Здесь ошибки, если они есть + error = [] + def getError(self): + """Выдать ошибки""" + if not self.error: + return False + error = "" + for e in self.error: + error += e + "\n" + return error + + def setError(self, error): + """Установка ошибки""" + self.error.append(error) + return True + + +class _terms(_error): + """Вычисление условий применяемых в профилях + + """ + # регулярное выражение для не версии + _re_not_Version = re.compile("[^0-9\.]|^$") + # регулярное выражение для функции + _reFunction = re.compile(\ + "([a-zA-Z0-9\_\-]+)\([a-zA-Z0-9_\-\+\,\*\/\.\'\"~]+\)") + # Регулярное выражение для названия переменной + _reDenyName = re.compile("[^a-zA-Z0-9\_\-]") + # Регулярное выражение для сравниваемого значения + _reDenyValue = re.compile("[^0-9a-zA-Z_\.-]") + + def _convertVers(self, verA, verB): + """Конвертирование номеров версий для корректного сравнения + """ + elemA = verA.split(".") + elemB = verB.split(".") + if len(elemA) > len(elemB): + maxElemB = len(elemB)-1 + for i in range(len(elemA)): + if i > maxElemB: + elemB.append("0") + else: + maxElemA = len(elemA)-1 + for i in range(len(elemB)): + if i > maxElemA: + elemA.append("0") + for i in range(len(elemB)): + lenA = len(elemA[i]) + lenB = len(elemB[i]) + if lenA == lenB: + pass + elif lenA > lenB: + res = lenA - lenB + for z in range(res): + elemB[i] = "0" + elemB[i] + elif lenB > lenA: + res = lenB - lenA + for z in range(res): + elemA[i] = "0" + elemA[i] + return (".".join(elemA), ".".join(elemB)) + + def _equalTerm(self, term, textError, function=False): + """Вычисление логических выражений для условий + + Для корректной работы в классе который наследует этот класс + должен быть объявлен аттрибут self.objVar + (объект для работы с переменными) + function - функция для для обработки функций в заголовке блока + """ + trm = {"&":" and ","||":" or "} + rule = ["==", "!=", ">=", "<=", ">", "<"] + listEqual = [] + for k in trm.keys(): + if k in term: + term = term.replace(k,trm[k]) + trs = term.split(" ") + for t in trs: + flagRule = False + for sepF in rule: + if sepF in t: + flagRule = True + vals = t.split(sepF) + break + + if not flagRule: + flagLog = False + for k in trm.values(): + if k.strip() == t: + flagLog = True + break + if not flagLog: + self.setError("'%s'"%term + " " + _("incorrect")) + self.setError (textError) + return False + else: + listEqual.append(k) + else: + #проверка на допустимость названия переменной + flagFunction = False + if self._reDenyName.search(vals[0]): + #проверка на допустимость функции + flagError = True + if function: + searchFunct = self._reFunction.search(vals[0]) + if searchFunct: + flagError = False + flagFunction = True + if flagError: + self.setError("'%s'"%term + " " + _("incorrect")) + self.setError(textError) + return False + #проверка на допустимость значения + if self._reDenyValue.search(vals[1]): + self.setError("'%s'"%term + " " + _("incorrect")) + self.setError(textError) + return False + flagIntTypeVar = None + if flagFunction: + valVars = function("#-%s-#"%vals[0]) + if valVars is False: + self.setError("'%s'"%term + " " + _("incorrect")) + self.setError(textError) + return False + if "load" == searchFunct.group(1): + if re.search("\(\s*num\s*,",vals[0]): + if valVars: + try: + valVars = int(valVars) + except: + self.setError("'%s'"%term + " " + \ + _("incorrect")) + self.setError (textError) + return False + flagIntTypeVar = True + else: + flagIntTypeVar = False + else: + if valVars == "" and\ + not self._re_not_Version.search(vals[1]): + valVars = "0" + elif vals[1] == "" and\ + not self._re_not_Version.search(valVars): + vals[1] = "0" + else: + try: + valVars = self.objVar.Get(vals[0]) + except self.objVar.DataVarsError, e: + print textError + print e + cl_base.exit(1) + # Номера версий для ini + flagNotIniFunct = True + # Два значения не пусты + flagNotEmptyVals = not (valVars == "" and vals[1] == "") + if flagFunction and flagNotEmptyVals and\ + searchFunct.group(1) == "ini": + # Проверка значения на версию + if not self._re_not_Version.search(valVars) and\ + not self._re_not_Version.search(vals[1]): + verFile, verVar = self._convertVers(vals[1],valVars) + exec(\ + "res=("+"'"+verVar+"'"+sepF+"'"+verFile+"'"+")") + if res: + listEqual.append("1") + else: + listEqual.append("0") + flagNotIniFunct = False + # Cравниваем номера версий + if flagNotIniFunct: + if flagNotEmptyVals and\ + ("_ver" in vals[0] or\ + (flagFunction and searchFunct.group(1) == "pkg") or\ + (flagFunction and searchFunct.group(1) == "load" and\ + re.search("\(\s*ver\s*,",vals[0]))): + # Проверка значения на версию + if self._re_not_Version.search(vals[1]): + self.setError("'%s'"%term + " " + _("incorrect")) + self.setError (_("Value is not version")) + return False + # Проверка значения функции на версию + if self._re_not_Version.search(valVars): + self.setError("'%s'"%term + " " + _("incorrect")) + self.setError (_("Value function is not version")) + return False + verFile, verVar = self._convertVers(vals[1],valVars) + exec("res=("+"'"+verVar+"'"+sepF+"'"+verFile+"'"+")") + if res: + listEqual.append("1") + else: + listEqual.append("0") + flagNotIniFunct = False + else: + if flagIntTypeVar == None: + flagIntTypeVar = True + try: + valVars = int(valVars) + except: + flagIntTypeVar = False + if flagIntTypeVar: + if not vals[1].strip(): + vals[1] = 0 + try: + valFile = int(vals[1]) + except: + self.setError("'%s'"%term +" " +_("incorrect")) + self.setError (textError) + return False + valVar = valVars + exec("res=(%d%s%d)"%(valVar,sepF,valFile)) + if res: + listEqual.append("1") + else: + listEqual.append("0") + else: + if sepF == "!=" or sepF == "==": + if not vals[1].strip(): + vals[1] = "" + valFile = vals[1] + valVar = valVars + exec(\ + "res=("+'"""'+valVar+'"""'+sepF+"'"+valFile+\ + "'"+")") + if res: + listEqual.append("1") + else: + listEqual.append("0") + else: + if not flagNotEmptyVals: + listEqual.append("0") + else: + self.setError("'%s'"%term + " "\ + + _("incorrect")) + self.setError (textError) + return False + exec("res=(%s)"%("".join(listEqual))) + return res + +class fileHeader(_terms): + """Обработка заголовков профилей и конфигурационных файлов + + """ + # Допустимые параметры заголовка + allowParam = ["format", "format_conf", "comment", "append", "force", + "link", "mirror", "symbolic", "chmod", "chown", "name"] + # Корректность заголовка + headerCorrect = True + + # Тип профиля + fileType = "" + # Тип вставки профиля + typeAppend = "" + # Возможные типы вставки профилей + _fileAppend = "join", "before", "after", "replace", "remove" + # Интерпретатор (#!/bin/bash) (#!/usr/bin/python) + execStr = "" + # Символ комментария + comment = False + # Выражение для поиска строки интерпретатора + reExecStr = re.compile("^#!.+\s*",re.I) + # условные операторы + terms = ('>', '<', '==', '!=', '>=', '<=') + # параметры без значения + listParNotVal = ("symbolic", "force", "mirror") + # Результат вычисления условия в заголовке + headerTerm = True + # Сообщение о ошибке + errorMessage = "" + + def __init__(self, text, comment=False, fileType=False, objVar=False, + function=False): + self.body = text + # Объект с переменными + self.objVar=objVar + # Параметры описанные в заголовке файла профиля + self.params = {} + # некорректные параметры + incorrectParams = set([]) + # Удаление Заголовка Calculate + if comment: + # В случае текста XML + if type(comment) == types.TupleType and len(comment) == 2: + _titleList = (_("Modified"), _("File of a profile")) + reCalcHeader =\ + re.compile(u"\s*%s\s+%s.+Calculate.+\s+%s.+\s+%s\s?"%(\ + comment[0], + _titleList[0].decode("UTF-8"), + _titleList[1].decode("UTF-8"), + comment[1], + ), + re.M|re.I|re.U) + textUnicode = text.decode("UTF-8") + reS = reCalcHeader.search(textUnicode) + if reS: + textBody = textUnicode[:reS.start()]+textUnicode[reS.end():] + if textBody: + self.body = textBody.encode("UTF-8") + else: + reCalcHeader =\ + re.compile("\s*%s\-+\s+%s.+Calculate.+\s+%s.+\s+%s\-+\s?"%(\ + comment, + comment, + comment, + comment, + ), + re.M|re.I) + reS = reCalcHeader.search(text) + if reS: + self.body = text[reS.end():] + + if fileType != False: + if fileType=="bin": + self.params["format"] = fileType + self.fileType = self._getType() + self.typeAppend = self._getAppend() + else: + textLines = text.splitlines() + if textLines: + textLine = textLines[0] + rePar = re.compile("\s*#\s*calculate\s+",re.I) + reP = rePar.search(textLine) + if reP: + reL = False + reLns = re.compile(r"\A([^\\\n]*\\\n)+[^\n]*\n*",re.M) + reLs = reLns.search(text) + if reLs: + reL = reLs + paramLine = text[reP.end():reLs.end()] + paramLine = paramLine.replace("\\"," ") + else: + reLn = re.compile("\n") + reL = reLn.search(text) + paramLine = textLine[reP.end():] + if reL: + self.body = text[reL.end():] + else: + self.body = "" + paramList = re.split("\s+",paramLine) + if paramList: + for i in paramList: + foundTerm = False + for term in self.terms: + if term in i: + foundTerm = True + if function: + rezTerm = self._equalTerm(i,\ + _("header profile not valid: ") + \ + i,function) + else: + rezTerm = self._equalTerm(i,\ + _("header profile not valid: ") + \ + i) + if not rezTerm: + self.headerTerm = False + break + if not foundTerm: + par = i.split("=") + if len(par) == 1: + if i in self.listParNotVal: + self.params[i] = "True" + else: + if i.strip(): + incorrectParams = set([i]) + elif len(par) == 2: + self.params[par[0]] = par[1] + + self.comment = self._getComment() + self.fileType = self._getType() + self.typeAppend = self._getAppend() + reExecRes = self.reExecStr.search(self.body) + if reExecRes: + self.execStr = self.body[reExecRes.start():reExecRes.end()] + self.body = self.body[reExecRes.end():] + if not incorrectParams: + incorrectParams = set(self.params.keys()) - set(self.allowParam) + if incorrectParams: + self.headerTerm = False + self.headerCorrect = False + self.errorMessage = _("incorrect header parameters - '%s'")\ + % " ".join(list(incorrectParams)) + + def _getType(self): + """Выдать тип файла""" + if self.params.has_key("format"): + return self.params["format"] + else: + return "raw" + + def _getAppend(self): + """Выдать тип добавления файла""" + if self.params.has_key("append") and self.params["append"] in\ + self._fileAppend: + return self.params["append"] + else: + if self.fileType != "raw" and self.fileType != "bin" and\ + self.fileType != "": + self.params["append"] = "join" + return "join" + self.params["append"] = "replace" + return "replace" + + def _getComment(self): + """Выдать символ комментария файла""" + if self.params.has_key("comment"): + return self.params["comment"] + else: + return False + + +class dirHeader(_terms): + """Обработка заголовков профилей директорий + + """ + # Допустимые параметры заголовка + allowParam = ["append", "chmod", "chown", "name"] + # Корректность заголовка + headerCorrect = True + + # Тип вставки профиля + typeAppend = "" + # Возможные типы вставки профилей + _fileAppend = "join", "remove" + # условные операторы + terms = ('>', '<', '==', '!=', '>=', '<=') + # параметры без значения + listParNotVal = ("symbolic", "force") + # Результат вычисления условия в заголовке + headerTerm = True + # Сообщение о ошибке + errorMessage = "" + + def __init__(self, text, objVar=False, function=False): + self.body = text + # Объект с переменными + self.objVar=objVar + # Параметры описанные в заголовке файла профиля + self.params = {} + # некорректные параметры + incorrectParams = set([]) + + textLines = text.splitlines() + flagErrorBody = False + if textLines: + textLine = textLines[0] + rePar = re.compile("\s*#\s*calculate\s+",re.I) + reP = rePar.search(textLine) + if reP: + reL = False + reLns = re.compile(r"\A([^\\\n]*\\\n)+[^\n]*\n*",re.M) + reLs = reLns.search(text) + if reLs: + reL = reLs + paramLine = text[reP.end():reLs.end()] + paramLine = paramLine.replace("\\"," ") + else: + reLn = re.compile("\n") + reL = reLn.search(text) + paramLine = textLine[reP.end():] + if reL: + self.body = text[reL.end():] + else: + self.body = "" + if self.body.strip(): + self.headerTerm = False + self.headerCorrect = False + self.errorMessage = _("incorrect text in profile: '%s'")\ + %self.body + flagErrorBody = True + if not flagErrorBody: + paramList = re.split("\s+",paramLine) + if paramList: + for i in paramList: + foundTerm = False + for term in self.terms: + if term in i: + foundTerm = True + if function: + rezTerm = self._equalTerm(i,\ + _("header profile not valid: ") + \ + i,function) + else: + rezTerm = self._equalTerm(i,\ + _("header profile not valid: ") + \ + i) + if not rezTerm: + self.headerTerm = False + break + if not foundTerm: + par = i.split("=") + if len(par) == 1: + if i in self.listParNotVal: + self.params[i] = "True" + else: + if i.strip(): + incorrectParams = set([i]) + elif len(par) == 2: + self.params[par[0]] = par[1] + self.typeAppend = self._getAppend() + if not flagErrorBody: + if not incorrectParams: + incorrectParams = set(self.params.keys()) - set(self.allowParam) + if incorrectParams: + self.headerTerm = False + self.headerCorrect = False + self.errorMessage = _("incorrect header parameters - '%s'")\ + % " ".join(list(incorrectParams)) + + def _getAppend(self): + """Выдать тип добавления директории""" + if self.params.has_key("append") and self.params["append"] in\ + self._fileAppend: + return self.params["append"] + else: + return "join" + +class objShare: + """Общий клас для объектов, наследуем + + """ + + def createFieldTerm(self, name, value, quote, docObj): + """Создание поля переменная - значение + + при создании поля проверяется первый символ названия переменной + и добавляется тег action + "!" - drop удаляет + "+" - join добавляет + "-" - replace заменяет + """ + fieldAction = False + if name: + if name[0] == "!" or name[0] == "-" or name[0] == "+": + qnt = self.removeSymbolTerm(quote) + fieldXML = docObj.createField("var",[qnt], + name[1:], [value], + False, False) + if name[0] == "!": + fieldAction = "drop" + elif name[0] == "+": + fieldXML.setAttribute("type", "seplist") + fieldAction = "join" + else: + fieldXML = docObj.createField("var", + [quote.replace("\n","")], + name, [value], + False, False) + else: + fieldXML = docObj.createField("var", + [quote.replace("\n","")], + name, [value], + False, False) + if fieldAction: + docObj.setActionField(fieldXML, fieldAction) + return fieldXML + + def removeSymbolTerm(self, text): + """Удаляет первый символ названия переменной в строке + + Если первый встречающийся символ с начала строки + '+', '-', '!' то он из этой строки будет удален, + если перед этим символом были пробельные символы, + то они будут сохранены, так-же если в строке есть символ + перевода строки он будет удален. + """ + reTerm = re.compile("^[ \t]*(\!|\+|\-)") + textNS = text.replace("\n","") + res = reTerm.search(textNS) + if res: + textNS = textNS[res.start():res.end()-1] + textNS[res.end():] + return textNS + + def getConfig(self): + """Выдает конфигурационный файл""" + listConfigTxt = [] + childNodes = self.docObj.getNodeBody().childNodes + for node in childNodes: + if node.nodeType == node.ELEMENT_NODE: + if node.tagName == "field": + listConfigTxt.append(self.docObj.getQuoteField(node)) + elif node.tagName == "area": + self.docObj.xmlToText([node], listConfigTxt) + return "".join(listConfigTxt) + + + def splitToFields(self, txtBloc): + """Разбиваем текст на поля (объекты) которые имеют следующие аттрибуты + + self.name - если переменная то имя переменной + self.value - если у переменной есть значение то значение переменной + self.comment - если комментарий то текст комментария + self.br - если перевод строки то текст перед переводом из пробелов + + Результат список объектов полей + """ + finBloc = "\n" + if txtBloc[-1] != "\n": + finBloc = "" + + linesBlocTmp = txtBloc.splitlines() + linesBloc = [] + brBloc = [] + z = 0 + lenLines = len(linesBlocTmp) + for i in linesBlocTmp: + + if self.reComment.split(i)[0]: + findCooment = self.reComment.search(i) + comment = False + par = i + if findCooment: + par = i[:findCooment.start()] + comment = i[findCooment.start():] + fields = self.reSepFields.split(par) + lenFields = len(fields) + + if lenFields>1: + for fi in range(lenFields-1): + linesBloc.append(fields[fi]+ self.sepFields) + if fi == lenFields-2: + if comment: + brBloc.append("") + else: + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + else: + brBloc.append("") + if comment: + linesBloc.append(comment) + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + else: + linesBloc.append(i) + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + else: + linesBloc.append(i) + if (lenLines-1)== z: + brBloc.append(finBloc) + else: + brBloc.append("\n") + z +=1 + fields = self.setDataField(linesBloc, brBloc) + return fields + +class xmlShare: + """Общий класс для объектов XML, наследуем + + """ + def _createElement(self, doc, tagName, text="", attributes={}): + """Создание нового XML элемента""" + element = doc.createElement(tagName) + if text: + txtNode = doc.createTextNode(self._toUNICODE(text)) + element.appendChild(txtNode) + for attr in attributes.keys(): + attribute = doc.createAttribute(attr) + attribute.nodeValue = attributes[attr] + element.setAttributeNode(attribute) + return element + + def _toUNICODE(self,val): + """перевод текста в юникод""" + if 'unicode' in "%s" %(type(val)): + return val + else: + return val.decode('UTF-8') + + +class xmlNode(xmlShare): + """Класс для создания нод без аттрибутов + + """ + def __init__(self): + self.node = False + + + def createNode(self, doc, tagName, text=""): + """Создает XML элемент без аттрибутов""" + self.node=self._createElement(doc, tagName, text) + return self.node + + def getNode(self): + return self.node + + +class xmlCaption: + """Класс XML заголовок + + """ + def __init__(self): + #Заголовок области XML нода + self.caption = False + + + def createCaption(self, doc, name, quotes, action=False): + """Создание заголовка области""" + tmpNode = xmlNode() + self.caption = tmpNode.createNode(doc, "caption") + nameNode = tmpNode.createNode(doc, "name",name) + self.caption.appendChild(nameNode) + if action: + actNode = tmpNode.createNode(doc, "action", action) + self.caption.appendChild(actNode) + for q in quotes: + quoteNode = tmpNode.createNode(doc, "quote", q) + self.caption.appendChild(quoteNode) + return self.caption + + def getCaption(self): + """Выдает XML ноду заголовка области""" + return self.caption + +class xmlField(xmlShare): + """Класс для работы с XML полем + + """ + def __init__(self): + # XML нода поле + self.field = False + + + def createField(self, doc, typeField, quotes, name="", + values=[],action=False): + """Cоздание XML ноды поле""" + self.field = self._createElement(doc, "field", "", {"type":typeField}) + if name: + nameNode = self._createElement(doc, "name", name) + self.field.appendChild(nameNode) + for v in values: + valueNode = self._createElement(doc, "value", v) + self.field.appendChild(valueNode) + if action: + actNode = self._createElement(doc, "action", action) + self.field.appendChild(actNode) + for q in quotes: + quoteNode = self._createElement(doc, "quote", q) + self.field.appendChild(quoteNode) + return self.field + + + +class xmlFields: + """Класс, в котором находится список ХМL нод field + + """ + def __init__(self): + self.fields = [] + + def appendField(self, field): + """Добавить XML ноду field""" + self.fields.append(field) + return self.fields + + def getFields(self): + """Выдать список XML нод""" + return self.fields + + +class xmlArea: + """Класс для работы с XML областью + + """ + def __init__(self): + # Область + self.area = False + + def createArea(self, doc, xmlCaption, xmlFields): + """Создание XML области""" + tmpNode = xmlNode() + self.area = tmpNode.createNode(doc, "area") + if xmlCaption and xmlCaption.getCaption(): + self.area.appendChild(xmlCaption.getCaption()) + if xmlFields: + fields = xmlFields.getFields() + for field in fields: + self.area.appendChild(field) + return self.area + +class xmlDoc: + """Класс для работы с XML документом + + """ + def __init__(self): + # документ + self.doc = False + # главная нода + self.root = False + # тело документа + self.body = False + # Заголовок области - временный (в реальности один объект заголовок) + self.tmpCaption = False + # Поля - временные (в реальности один объект поля) + self.tmpFields = False + # Разделитель областей - по умолчанию перевод строки "\n" + self.sepAreas = False + # Разделитель разделенных списков - по умолчанию перевод строки "\n" + #self.sepSplitFields = False + + + def createDoc(self, typeDoc, version): + """Создание нового документа новый документ""" + docTxt = '' + docTxt += '%s'% version + docTxt += '%s' % typeDoc + docTxt += '' + self.doc = xml.dom.minidom.parseString(docTxt) + self.root = self.doc.documentElement + self.body = xpath.Evaluate('child::body',self.root)[0] + # установка разделителя областей + self.sepAreas = self.createField("br",[],"",[],False,False) + # установка разделителя областей разделенных списков + #self.sepSplitFields = self.createField("br",[],"",[],False,False) + return self.doc + + def addField(self, field): + """Добавляет поле во временный список + + Из этого списка в дальнейшем формируется XML область + """ + if not self.tmpFields: + self.tmpFields = xmlFields() + self.tmpFields.appendField(field) + + def createCaption(self, name, quotes, action=False): + """Cоздает заголовок области + + Помещает заголовок в временный артибут + Используется при создании области + """ + self.tmpCaption = xmlCaption() + return self.tmpCaption.createCaption(self.doc, name, quotes, action) + + def createField(self, typeField, quotes=[], name="", + values=[] ,action=False,addTmpField=True): + """Cоздает поле + + Если установлена переменнная addTmpField + добавляет поле во временный список + """ + fieldObj = xmlField() + field = fieldObj.createField(self.doc, typeField, quotes, name, + values, action) + if addTmpField: + self.addField(field) + return field + + def clearTmpFields(self): + """Очищает временный список""" + self.tmpFields = False + + def createArea(self): + """Cоздает область + + Область создается на основании временного атрибута и временного списка + """ + areaObj = xmlArea() + area = areaObj.createArea(self.doc, self.tmpCaption, self.tmpFields) + self.clearTmpCaptionAndFields() + return area + + def clearTmpCaptionAndFields(self): + """Очищает временный аттрибут и временный список""" + self.tmpCaption = False + self.tmpFields = False + + def getNodeRoot(self): + """Выдает корневую ноду""" + return self.root + + def getNodeBody(self): + """Выдает ноду body""" + return self.body + + def setActionField(self, xmlField, actionTxt): + """Устанавливает свойство action для XML поля""" + xmlActions = xpath.Evaluate('child::action',xmlField) + if xmlActions and xmlActions[0].firstChild: + xmlActions[0].firstChild.nodeValue = actionTxt + else: + nodeObj = xmlNode() + newNode = nodeObj.createNode(self.doc, "action", actionTxt) + xmlField.appendChild(newNode) + + + def setActionArea(self, xmlArea, actionTxt): + """Устанавливает свойство action для XML области""" + xmlActions = xpath.Evaluate('child::caption/action',xmlArea) + xmlCaptions = xpath.Evaluate('child::caption',xmlArea) + if xmlActions and xmlActions[0].firstChild: + xmlActions[0].firstChild.nodeValue = actionTxt + else: + if xmlCaptions: + nodeObj = xmlNode() + newNode = nodeObj.createNode(self.doc, "action", actionTxt) + xmlCaptions[0].appendChild(newNode) + + def joinField(self, xmlArea, xmlNewField): + """Объединяет XML ноду область и XML ноду поле""" + newNameField = self.getNameField(xmlNewField) + if not newNameField or not newNameField.strip(): + return False + fieldsOldComp = xpath.Evaluate("child::field[child::name='%s']"\ + %(newNameField), xmlArea) + # Если поле не найдено добавляем его + typeNewField = self.getTypeField(xmlNewField) + if not fieldsOldComp and typeNewField != "seplist": + if self.getActionField(xmlNewField) != "drop": + self.setActionField(xmlNewField, "append") + xmlArea.appendChild(xmlNewField) + return True + newFieldsAction = self.getActionField(xmlNewField) + newValues = self.getFieldValues(xmlNewField) + flagCompare = True + + for nodeFieldOld in fieldsOldComp: + if newFieldsAction == "drop": + if nodeFieldOld.nextSibling and\ + self.getTypeField(nodeFieldOld.nextSibling) == "br": + xmlArea.removeChild(nodeFieldOld.nextSibling) + elif nodeFieldOld.previousSibling and\ + self.getTypeField(nodeFieldOld.previousSibling) == "br": + xmlArea.removeChild(nodeFieldOld.previousSibling) + xmlArea.removeChild(nodeFieldOld) + continue + oldValues = self.getFieldValues(nodeFieldOld) + # Сравнение значений переменной профиля и файла + if set(newValues) != set(oldValues): + flagCompare = False + if self.getActionField(xmlNewField) == "drop": + return True + appSplLst = [] + insSplLst = [] + if typeNewField == "seplist": + if fieldsOldComp: + xmlOldField = fieldsOldComp[-1] + else: + xmlOldField = False + seplistNewXML = self.getSepListToField(xmlNewField) + if seplistNewXML: + for nodeSeplist in seplistNewXML: + if self.getActionField(nodeSeplist) != "drop": + if newFieldsAction == "join": + flagCompareSeplist = False + newValues = self.getFieldValues(nodeSeplist) + for nodeFieldOld in fieldsOldComp: + oldValues = self.getFieldValues(nodeFieldOld) + for newValue in newValues: + if newValue in oldValues: + flagCompareSeplist = True + break + if not flagCompareSeplist: + nextNode = xmlOldField.nextSibling + newInsNode = nodeSeplist.cloneNode(True) + self.setActionField(newInsNode,"append") + + if nextNode: + appSplLst.append((newInsNode, + nextNode, + "insert")) + else: + appSplLst.append((newInsNode, + False, + "append")) + else: + newInsNode = nodeSeplist.cloneNode(True) + if self.getActionField(newInsNode) == "join": + self.setActionField(newInsNode,"append") + if xmlOldField: + insSplLst.append((newInsNode, + xmlOldField, + "insert")) + else: + insSplLst.append((newInsNode, + False, + "append")) + + #xmlArea.insertBefore(\ + #nodeSeplist.cloneNode(True), + #xmlOldField) + + parentNode = nodeSeplist.parentNode + parentNode.removeChild(nodeSeplist) + + insNodesRepl = [] + for newNode, nxtNode, app in insSplLst: + flagCompareSeplist = False + newValues = self.getFieldValues(newNode) + for nodeRepl, nxtNode, app in insNodesRepl: + oldValues = self.getFieldValues(nodeRepl) + for newValue in newValues: + if newValue in oldValues: + flagCompareSeplist = True + break + if not flagCompareSeplist: + if xmlOldField: + insNodesRepl.append((newNode, nxtNode, app)) + + for newNode, nxtNode, app in insNodesRepl: + if app == "insert": + xmlArea.insertBefore(newNode,nxtNode) + elif app == "append": + xmlArea.appendChild(newNode) + if xmlOldField: + parentNode = xmlOldField.parentNode + if parentNode and newFieldsAction != "join": + parentNode.removeChild(xmlOldField) + + for newNode, nxtNode, app in appSplLst: + if app == "insert": + xmlArea.insertBefore(newNode,nxtNode) + elif app == "append": + xmlArea.appendChild(newNode) + + if not flagCompare and typeNewField != "seplist": + # Устанавливаем action=replace + self.setActionField(xmlNewField, "replace") + # Если параметры поля не сходятся заменяем поле + xmlArea.replaceChild(xmlNewField.cloneNode(True), + fieldsOldComp[-1]) + + if newFieldsAction == "join": + fieldsOldRemove = [] + else: + fieldsOldRemove = fieldsOldComp[:-1] + + for nodeFieldOld in fieldsOldRemove: + actionOldNode = self.getActionField(nodeFieldOld) + if actionOldNode == "insert" or actionOldNode == "append": + pass + else: + if nodeFieldOld.nextSibling and\ + self.getTypeField(nodeFieldOld.nextSibling) == "br": + xmlArea.removeChild(nodeFieldOld.nextSibling) + xmlArea.removeChild(nodeFieldOld) + return True + + + def getSepListToField(self, xmlField): + """Выдает элементы распределенного массива + + Область предок поля, в этой области ищутся + элементы распределенного массива + """ + nameField = self.getNameField(xmlField) + if not nameField: + return [] + parentNode = xmlField.parentNode + #print parentNode.toprettyxml() + if parentNode: + fieldsVal = xpath.Evaluate(\ + "child::field[attribute::type='seplist'][child::name='%s'] "\ + %(nameField), parentNode) + #print nameField + return fieldsVal + else: + return [] + + def removeComment(self, xmlArea): + """Удаляет комментарии в XML области""" + fieldNodes = xpath.Evaluate('descendant::field',xmlArea) + for fieldNode in fieldNodes: + if fieldNode.hasAttribute("type"): + if fieldNode.getAttribute("type") == "comment" or\ + fieldNode.getAttribute("type") == "br": + parentNode = fieldNode.parentNode + parentNode.removeChild(fieldNode) + else: + if self.getActionField(fieldNode) == "drop": + pass + elif self.getActionField(fieldNode) == "join": + pass + else: + self.setActionField(fieldNode,"append") + + + def joinBody(self, baseBody, newBody): + """Объединяет две области Body""" + newFields = xpath.Evaluate('child::field',newBody) + xmlNewAreas = xpath.Evaluate('child::area',newBody) + for xmlNewArea in xmlNewAreas: + self.joinArea(baseBody,xmlNewArea) + joinNewFields = xpath.Evaluate("child::field[child::action='join']" + ,newBody) + self.addNewFielsOldArea(newFields, joinNewFields, baseBody) + + + def getRemoveNodeSepList(self, removeNodesDict, baseNode, xmNewlField): + """Находит элементы разделенного списка + + Параметры: + removeNodesDict - Cловарь удаляемых полей разделенного списка + формируется программой + baseNode - Нода в которой идет поиск + xmNewlField - Нода field которая проверяется на принадлежность + к разделенному списку + """ + flagNewNodeSeplist = False + if self.getTypeField(xmNewlField) == "seplist": + flagNewNodeSeplist = True + nameNewField = self.getNameField(xmNewlField) + if nameNewField: + if removeNodesDict.has_key(nameNewField): + return removeNodesDict[nameNewField] + else: + oldFields = xpath.Evaluate('child::field', baseNode) + removeNodes = [] + lenOldFields = len(oldFields) + for i in range(lenOldFields): + oldNode = oldFields[i] + flagSep = self.getTypeField(oldNode) == "seplist" + if flagNewNodeSeplist: + flagSep = True + if flagSep and\ + nameNewField == self.getNameField(oldNode): + removeNodes.append(oldNode) + if i+1 1: + for node in listNodes: + node.setAttribute("type", "seplist") + + def insertBRtoBody(self, xmlArea): + """Добавляет необходимые переводы строк + """ + # Потомки + childNodes = self.getFieldsArea(xmlArea) + # нода BR + fieldXMLBr = self.createField("br",[],"",[],False, False) + # разделитель поля + fieldSplit = False + # Предыдущая нода + lastNode = False + # Cледующая нода + nextNode = False + lenChildNodes = len(childNodes) + for i in range(lenChildNodes): + node = childNodes[i] + lastTmpNode = node + # Нода area + if node.tagName == "area": + if self.getActionArea(node) == "append" or\ + self.getActionArea(node) == "join": + self.delActionNodeArea(node) + if lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "br" or\ + lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "comment": + indNext = i + 1 + if indNext == lenChildNodes: + xmlArea.appendChild(fieldXMLBr.cloneNode(True)) + else: + nextNode = childNodes[indNext] + lastTmpNode = xmlArea.insertBefore(\ + fieldXMLBr.cloneNode(True), + nextNode) + else: + xmlArea.insertBefore(fieldXMLBr.cloneNode(True), + node) + self.insertBRtoBody(node) + # Нода field + else: + if self.getActionField(node) == "append" or\ + self.getActionField(node) == "join": + self.delActionNodeField(node) + if lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "br" or\ + lastNode and lastNode.hasAttribute("type") and\ + lastNode.getAttribute("type") == "comment": + indNext = i + 1 + if indNext == lenChildNodes: + xmlArea.appendChild(fieldXMLBr.cloneNode(True)) + else: + nextNode = childNodes[indNext] + lastTmpNode = xmlArea.insertBefore(\ + fieldXMLBr.cloneNode(True), + nextNode) + else: + xmlArea.insertBefore(fieldXMLBr.cloneNode(True), + node) + lastNode = lastTmpNode + + + + def postParserList(self): + """Находит подходящие XML области и делаем из них поля-массивы""" + xmlAreas = xpath.Evaluate('descendant::area', self.body) + for xmlArea in xmlAreas: + flagListXml = True + fieldValues = [] + xmlFields = xpath.Evaluate('child::field',xmlArea) + if not xmlFields: + flagListXml = False + lenXmlFields = len(xmlFields) + lenBrArea = 0 + for xmlField in xmlFields: + xmlNames = xpath.Evaluate('child::name',xmlField) + xmlVals = xpath.Evaluate('child::value',xmlField) + if xmlField.hasAttribute("type") and\ + xmlField.getAttribute("type") == "br": + lenBrArea += 1 + continue + if not xmlNames and not xmlVals: + flagListXml = False + break + if xmlNames and xmlNames[0].firstChild and\ + xmlNames[0].firstChild.nodeValue: + flagListXml = False + break + if not (xmlVals and xmlVals[0].firstChild and\ + xmlVals[0].firstChild.nodeValue): + flagListXml = False + break + else: + fieldValues.append(xmlVals[0].firstChild.nodeValue) + + if lenXmlFields == lenBrArea: + flagListXml = False + if flagListXml: + nameNode = xpath.Evaluate('child::caption/name',xmlArea)[0] + fieldName = "" + if nameNode.firstChild: + fieldName = nameNode.firstChild.nodeValue + listArea = [] + self.xmlToText([xmlArea],listArea) + fieldQuote = "".join(listArea) + fieldXMLBr = False + if fieldQuote and fieldQuote[-1] == "\n": + fieldQuote = fieldQuote[:-1] + fieldXMLBr = self.createField("br",[],"",[],False, False) + fieldXML = self.createField("list", + [fieldQuote], + fieldName, fieldValues, + False, False) + areaAction = self.getActionArea(xmlArea) + if areaAction: + self.setActionField(fieldXML, areaAction) + parentNode = xmlArea.parentNode + parentNode.insertBefore(fieldXML,xmlArea) + if fieldXMLBr: + parentNode.insertBefore(fieldXMLBr,xmlArea) + parentNode.removeChild(xmlArea) + + +class blocText: + """Разбиваем текст на блоки""" + + def splitTxtToBloc(self, text ,openTxtBloc,closeTxtBloc, + commentTxtBloc, sepField): + """Делит текст на блоки (без заголовков) + + openTxtBloc - регулярное выражение для начала блока + closeTxtBloc - регулярное выражение для конца блока + commentTxtBloc - регулярное выражение - комментарий + возвращает блоки текста + """ + blocs = [] + level = 0 + # Нахождение нескольких блоков в строке + # разделители линий, разделителями могут быть ("","\n") + sepsLines = [] + # линии + txtLines = [] + # Исходные строки + txtLinesSrc = text.splitlines() + for line in txtLinesSrc: + lineBR = "" + lineTmpA = line + closeBl = False + txtLinesTmp = [] + commentSpl = commentTxtBloc.split(line) + flagCommentLine = False + if commentSpl[0].strip(): + closeBl = True + if len(commentSpl) > 1: + commentBl = commentTxtBloc.search(line) + textLine =commentSpl[0] + commentLine = line[commentBl.start(0):] + lineTmpA = textLine + flagCommentLine = True + + while (closeBl): + closeBl = sepField.search(lineTmpA) + if closeBl: + lineTmpB = lineTmpA[closeBl.end(0):] + txtLinesTmp.append(lineTmpA[:closeBl.end(0)]) + lineTmpA = lineTmpB + if lineTmpA.strip(): + txtLinesTmp.append(lineTmpA) + # Если есть значение и комментарий в строке + if flagCommentLine: + for l in txtLinesTmp: + txtLines.append(l) + sepsLines.append("") + if not txtLinesTmp: + txtLines.append(textLine) + sepsLines.append("") + txtLines.append(commentLine) + sepsLines.append("\n") + # Если есть несколько блоков в строке + elif len(txtLinesTmp)>1 and txtLinesTmp[1].strip(): + lenTmpLines = len(txtLinesTmp) + for l in range(lenTmpLines): + txtLines.append(txtLinesTmp[l]) + if l == lenTmpLines-1: + sepsLines.append("\n") + else: + sepsLines.append("") + # Cтрока не преобразована + else: + txtLines.append(line) + sepsLines.append("\n") + + # разбивание на блоки + z = 0 + bl = "" + for i in txtLines: + if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(i): + level += len(openTxtBloc.split(i)) - 1 + if commentTxtBloc.split(i)[0].strip() and closeTxtBloc.search(i): + level -= len(closeTxtBloc.split(i)) - 1 + bl += i + sepsLines[z] + if level == 0: + if bl: + blocs.append(bl) + bl = "" + z += 1 + # cоздание блоков с элементами не входящими в блоки + realBlocs = [] + z = 0 + bl = "" + for i in blocs: + txtLines = i.splitlines() + if len(txtLines) > 0: + line = txtLines[0] + else: + line = i + if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(line): + if bl: + realBlocs.append(bl) + bl = "" + realBlocs.append(i) + else: + bl += i + z += 1 + if bl: + realBlocs.append(bl) + bl = "" + if level == 0: + if text and text[-1] != "\n": + tmpBlocs = realBlocs.pop() + tmpBlocs = tmpBlocs[:-1] + realBlocs.append(tmpBlocs) + return realBlocs + else: + return [] + + def findArea(self, text, reTextHeader, reTextArea, numGroupArea=0): + """ Делит текст на области (с заголовками) + + reTextHeader - регулярное выражение для заголовка области + reTextArea - регулярное выражение для всей области + numGroupArea - номер групы результата поиска по регулярному выражению + по всей области + возвращает два списка: первый - заголовки, второй - тела областей без + заголоков + """ + # Заголовки областей + headersArea = [] + # Тексты областей без заголовков + textBodyArea = [] + r = reTextArea.search(text) + if not r: + headersArea.append("") + textBodyArea.append(text) + return (headersArea, textBodyArea) + + txtWr = text + while r: + textArea = r.group(numGroupArea) + txtSpl = txtWr.split(textArea) + area = txtSpl[0] + txtWr = txtSpl[1] + if area: + headersArea.append("") + textBodyArea.append(area) + res = reTextHeader.search(textArea) + header = textArea[:res.end()] + body = textArea[res.end():] + + headersArea.append(header) + textBodyArea.append(body) + + if txtWr: + r = reTextArea.search(txtWr) + else: + r = False + if txtWr: + headersArea.append("") + textBodyArea.append(txtWr) + return (headersArea, textBodyArea) + + + def findBloc(self, text, captionTxtBloc, bodyTxtBloc): + """ Делит текст на блоки (с заголовками) + + captionTxtBloc - регулярное выражение для заголовка блока + bodyTxtBloc - регулярное выражение для тела блока + возвращает два списка: первый - заголовки, второй - тела блоков + """ + # Заголовки блоков + headersTxt = [] + # Тексты блоков + blocsTxt = [] + r = captionTxtBloc.search(text) + if r: + headersTxt.append(r.group(0)) + txtSpl = text.split(r.group(0)) + blocTxt = txtSpl[0] + txtWr = txtSpl[1] + rb = bodyTxtBloc.search(blocTxt) + if not blocTxt: + blocsTxt.append(blocTxt) + if rb: + blocsTxt.append(rb.group(0)) + while (r): + r = captionTxtBloc.search(txtWr) + if r: + headersTxt.append(r.group(0)) + txtSpl = txtWr.split(r.group(0)) + blocTxt = txtSpl[0] + txtWr = txtSpl[1] + rb = bodyTxtBloc.search(blocTxt) + if rb: + blocsTxt.append(rb.group(0)) + else: + blocsTxt.append(txtWr) + if headersTxt and blocsTxt: + if len(headersTxt)>len(blocsTxt): + blocsTxt.insert(0,"") + elif len(headersTxt)= 4: + l = 4 + elif lenSymb >= 3: + l = 3 + elif lenSymb >= 2: + l = 2 + else: + if symbols[0] == '_ch_': + return (True,1) + else: + return (False,1) + result = False + for i in range(l): + if i == 0 and symbols[i] != '_fb_': + break + elif i > 0 and symbols[i] != '_nb_': + break + if lenTail>1 and lenTail != i: + return (False,1) + if i > 0: + result = True + return (result, i) + + + def _intToChar(self, x): + he = hex(x)[2:] + exec("ret = '\\x%s'" %he) + return ret + + def _hexToChar(self, he): + exec("ret = '\\x%s'" %he) + return ret + + def encode(self, text): + """Кодирует смешанный формат в UTF-8""" + ind = 0 + l = 0 + utf = [] + lenUtf = [] + indErr = [] + i = 0 + for ch in text: + r, l = self._retUTF(ch) + utf.append(r) + lenUtf.append(l) + i+=1 + while 1: + if utf[ind] == '_fb_': + res, l = self._sumbUtf(utf[ind:],lenUtf[ind]) + if res is False: + indErr.append(ind) + if l>0: + ind +=l + if ind >= len(utf): + break + else: + if utf[ind] != '_ch_': + indErr.append(ind) + ind +=1 + if ind >= len(utf): + break + if indErr: + lenIndErr = len(indErr) + block = [] + blocks = [] + if lenIndErr > 1: + i = 1 + while 1: + if i == 1: + block.append(indErr[i-1]) + if indErr[i] - indErr[i-1] == 1: + block.append(indErr[i]) + else: + if block: + blocks.append(block) + block = [] + block.append(indErr[i]) + i +=1 + if i >= lenIndErr: + break + else: + block.append(indErr[0]) + if block: + blocks.append(block) + listErr = [] + for block in blocks: + string = "" + for elem in block: + string += hex(ord(text[elem]))[-2:] + listErr.append((block[0],"__hex__?%s?__hex__" %string,elem)) + textOut = text + deltaInd = 0 + for erEl in listErr: + startInd = erEl[0] + deltaInd + endInd = erEl[2] + 1 + deltaInd + textOut = textOut[:startInd] + erEl[1] + textOut[endInd:] + deltaInd += len(erEl[1])-(erEl[2]-erEl[0]+1) + #if i == 1: + #break + #i += 1 + return textOut + + def decode(self, text): + """Декодирует UTF-8 в смешанный формат""" + varStart = "__hex__\?" + varEnd = "\?__hex__" + # -1 Это экранирование '?' которое тоже считается + deltVarStart = len(varStart)-1 + deltVarEnd = len(varEnd)-1 + reVar = re.compile(("%s[a-f0-9]+%s")%(varStart,varEnd),re.M) + resS = reVar.search(text) + textProfileTmp = text + while resS: + mark = textProfileTmp[resS.start():resS.end()] + hexString = mark[deltVarStart:-deltVarEnd] + i = 0 + stringInsert = "" + hexCode = "" + for ch in hexString: + if i>=1: + hexCode += ch + stringInsert += self._hexToChar(hexCode) + hexCode = "" + i = 0 + else: + hexCode += ch + i += 1 + textProfileTmp = textProfileTmp.replace(mark, stringInsert) + resS = reVar.search(textProfileTmp) + return textProfileTmp + + def getUserDataInLDAP(self, userName): + """Получаем домашнюю директорию пользователя из LDAP""" + if not self.conLdap: + import cl_utils2 + data = self.getLDAPDataInConfig() + if not data: + return "" + serverName, usersDN, bindDN, bindPW = data + # Подключаемся к LDAP + ldapObj = cl_utils2.ldapFun(bindDN, bindPW, serverName) + if self.getError(): + return "" + self.conLdap = ldapObj.conLdap + searchScope = ldap.SCOPE_ONELEVEL + searchFilter = "uid=%s" %(userName) + retrieveAttributes = ["uidNumber", + "gidNumber" + "homeDirectory"] + resSearch = ldapObj.ldapSearch(usersDN, searchScope, + searchFilter, retrieveAttributes) + if resSearch: + if resSearch[0][0][1].has_key('uidNumber') and\ + resSearch[0][0][1].has_key('gidNumber') and\ + resSearch[0][0][1].has_key('homeDirectory'): + uid = searchUser[0][0][1]['uidNumber'][0] + gid = searchUser[0][0][1]['gidNumber'][0] + homeDir = resSearch[0][0][1]['homeDirectory'][0] + return uid, gid, homeDir + return "" + + +class processingTemplates: + """Класс для обработки шаблонов""" + + def processingFile(self, path, prefix): + """Обработка в случае профиля файла""" + return True + + def processingDirectory(self, path, prefix): + """Обработка в случае директории если возвращаем None то пропуск дир.""" + return True + + def scanningTemplates(self, scanDir, skipFile=[], skipDir=[], + prefix=None, flagDir=False): + """Время последней модификации внутри директории scanDir""" + ret = True + if not prefix: + prefix = os.path.join(scanDir,"")[:-1] + if not flagDir: + # проверка корневой директории + retDir = self.processingDirectory(scanDir, scanDir) + if retDir is None: + return None + elif retDir is False: + return False + if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]): + for fileOrDir in sorted(os.listdir(scanDir)): + absPath = os.path.join(scanDir,fileOrDir) + relPath = absPath.split(prefix)[1] + stInfo = os.lstat(absPath) + statInfo = stInfo[stat.ST_MODE] + if stat.S_ISREG(statInfo): + # Обработка файла + if relPath in skipFile: + continue + if not self.processingFile(absPath, prefix): + ret = False + break + elif stat.S_ISDIR(statInfo): + # Обработка директории + if relPath in skipDir: + continue + retDir = self.processingDirectory(absPath, prefix) + if retDir is None: + continue + elif retDir is False: + ret = False + break + ret = self.scanningTemplates(absPath, skipFile, + skipDir, prefix, True) + if ret is False: + break + return ret + + +class profile(_file, _terms, xmlShare, processingTemplates): + """Класс для работы с профилями + + На вход 2 параметра: объект хранения переменных, имя сервиса - не + обязательный параметр + + """ + # Словарь установленных программ {"имя программы":[версии]} + installProg = {} + + # Cписок просканированных категорий установленных программ + installCategory = [] + + # Флаг сканирования всех установленных программ + flagAllPkgScan = False + + # Название файла профиля директории + profDirNameFile = ".calculate_directory" + + # стек глобальных переменных + stackGlobalVars = [] + + # директория установленных программ + basePkgDir = "/var/db/pkg" + + # регулярное выражение для поиска версии + reFindVer = re.compile("(?<=\-)\d+\.?\d*\.?\d*") + + def __init__(self, objVar, servDir=False, dirsFilter=[], filesFilter=[]): + # Необрабатываемые директории + self.dirsFilter = dirsFilter + # Необрабатываемые файлы + self.filesFilter = filesFilter + _file.__init__(self) + # Словарь для создания объектов новых классов по образцу + self.newObjProt = {'proftpd':(apache,),} + # Заголовок title + self.__titleHead = "--------------------------------------\ +----------------------------------------" + self._titleBody = "" + self._titleList = (_("Modified"), _("File of a profile")) + + # Метки + varStart = "#-" + varEnd = "-#" + self._reVar = re.compile(("%s[a-zA-Z0-9_-]+%s")%(varStart,varEnd),re.M) + self._reFunc = re.compile(("%s[a-zA-Z0-9_\-\+\(\)\, \*\/\.\'\"~]+%s")\ + %(varStart,varEnd),re.M) + self._deltVarStart = len(varStart) + self._deltVarEnd = len(varEnd) + # Условия + self._reTermBloc = re.compile("#\?(?P[a-zA-Z0-9\-_]+)\ +(?P\([a-zA-Z0-9_\-\+\,\*\/\.\'\"~]+\))?\ +(?P[\>\<\=\!\&\|]+\ +[a-zA-Z0-9\>\<\=\!\|\&\-\+\*\/_\.\,\(\)\'\"~]*)#\ +\n*(?P.+?)\n*#(?P=rTerm)#(?P[ ,\t]*\n?)",re.M|re.S) + # Объект с переменными + self.objVar = objVar + # Базовая директория переноса профилей "/mnt/calculate" или "/" и.т.д + baseDir = self.objVar.Get("cl_root_path") + #self._baseDir = os.path.split(baseDir)[0] + self._baseDir = baseDir + if self._baseDir == "/": + self._baseDir = "" + # Последняя часть директории профиля (имя сервиса: samba, mail) + self._servDir = servDir + if self._servDir: + if self._servDir[0] != "/": + self._servDir = "/" + self._servDir + if self._servDir[-1] != "/": + self._servDir += "/" + self._servDir = os.path.split(self._servDir)[0] + # Созданные директории + self.createdDirs = [] + # Примененные файлы + self.filesApply = [] + # номер обрабатываемого файла + self.numberProcessProf = 0 + # имя текущей программы + _nameProgram = self.objVar.Get("cl_name").capitalize() + # версия текущей программы + _versionProgram = self.objVar.Get("cl_ver") + # имя и версия текущей программы + self.programVersion = "%s %s"%(_nameProgram, _versionProgram) + # Словарь измененных директорий + self.changeDirs = {} + # Словарь директорий с количесвом файлов шаблонов + self.dictTemplates = {} + # Общее количество шаблонов + self.allTemplates = 0 + # Аттрибуты для функции шаблона ini() + # Первоначальный словарь переменных для ini() + self.prevDictIni = {} + # Текущий словарь переменных для ini() + self.currDictIni = {} + # Время модификации конфигурационного файла для ini() + self.timeIni = -1 + self.uid, self.gid, self.homeDir = self.getDataUser() + # Путь к конфигурационному файлу для ini() + self.pathConfigIni = os.path.join(self.homeDir, ".calculate") + self.fileConfigIni = os.path.join(self.pathConfigIni,"ini.env") + # Словарь времен модификации env файлов + self.timeConfigsIni = {} + + def getDataUser(self): + """Получить информацию о пользователе""" + userName = self.objVar.Get("ur_login") + if not userName: + userName = "root" + import pwd + try: + pwdObj = pwd.getpwnam(userName) + uid = pwdObj.pw_uid + gid = pwdObj.pw_gid + homeDir = pwdObj.pw_dir + except: + print _("Can not found user %s")%str(userName) + cl_base.exit(1) + return uid, gid, homeDir + + + # Преобразование восьмеричного в целое (ввод строка, вывод число) + def __octToInt(self, strOct): + if strOct: + try: + exec("res =" + "0" + strOct) + except: + self.setError (_("Not valid oct value: ") + str(strOct)) + return False + return res + else: + self.setError (_("Empty oct value")) + return False + + def removeDir(self, rmDir): + """Рекурсивное удаление директории + + входной параметр директория + Обязательно должен быть определен метод self.printERROR + """ + if not os.path.exists(rmDir): + self.printERROR(_("Not found remove dir %s") %rmDir) + return False + fileObj = _file() + # Сканируем директорию + scanObjs = fileObj.scanDirs([rmDir]) + for socketRm in scanObjs[0].sockets: + # Удаляем сокеты + if os.path.exists(socketRm): + os.remove(socketRm) + for linkRm in scanObjs[0].links: + # Удаляем ссылки + os.unlink(linkRm[1]) + for fileRm in scanObjs[0].files: + # Удаляем файлы + os.remove(fileRm) + scanObjs[0].dirs.sort(lambda x, y: cmp(len(y), len(x))) + for dirRm in scanObjs[0].dirs: + # Удаляем директории + os.rmdir(dirRm) + if rmDir: + os.rmdir(rmDir) + return True + + + def createDir(self, baseProfDir, profDir, baseDirMv, createDir): + """Создает директорию + + baseDirMv + результат вычитания из profDir baseProfDir + createDir - создаваемая директория + """ + if baseDirMv and not os.access(baseDirMv, os.F_OK): + os.makedirs(baseDirMv) + # Созданные директории + createDirs = [] + baseDir = baseProfDir + if baseProfDir[-1] == "/": + baseDir = baseProfDir[:-1] + # Директория в системе относительно baseDirMv без условий + if baseDirMv: + dirMvNoTerm = createDir.partition(baseDirMv)[2] + else: + dirMvNoTerm = createDir + # директория в системе + dirMv = profDir.partition(baseDir)[2] + if len(dirMv)>1 and dirMv[-1] == "/": + dirMv = dirMv[:-1] + ## директория в системе без условий + #dirMvNoTerm = "/".join(map(lambda x:x.split("?")[0],\ + #dirMv.split("/"))) + listDirMv = dirMv.split("/") + listDirMvNoTerm = dirMvNoTerm.split("/") + if len(listDirMv) != len(listDirMvNoTerm): + self.setError (_("Error in profile") + " :" + profDir) + return False + genIn = listDirMv.__iter__() + genOut = listDirMvNoTerm.__iter__() + inAndOutDirs = map(lambda x: [x,genOut.next()], genIn) + createDirs = [] + createDir = [] + while (len(inAndOutDirs)>1): + tmpDirIn = baseDir+"/".join(map(lambda x:x[0], inAndOutDirs)) + tmpDirOut = baseDirMv + "/".join(map(lambda x:x[1],\ + inAndOutDirs)) + if os.access(tmpDirOut, os.F_OK): + break + else: + createDir.append((tmpDirIn, tmpDirOut)) + inAndOutDirs.pop() + createDir.reverse() + for crDirIn,crDirOut in createDir: + try: + mode,uid,gid = self.getModeFile(crDirIn) + except OSError: + self.setError (_("not access dir:" ) + crDirIn) + return False + createDirs.append(crDirOut) + os.mkdir(crDirOut, mode) + os.chown(crDirOut, uid, gid) + return createDirs + + + def applyVarsProfile(self, textProfile, nameProfile): + """ Заменяет переменные на их значения + """ + resS = self._reVar.search(textProfile) + textProfileTmp = textProfile + while resS: + mark = textProfileTmp[resS.start():resS.end()] + varName = mark[self._deltVarStart:-self._deltVarEnd] + varValue = "" + try: + varValue = str(self.objVar.Get(varName)) + except self.objVar.DataVarsError, e: + print _("error in profile %s")%nameProfile + print e + cl_base.exit(1) + textProfileTmp = textProfileTmp.replace(mark, varValue) + resS = self._reVar.search(textProfileTmp) + return textProfileTmp + + + def applyFuncProfile(self, textProfile, nameProfile, nameSystemFile): + """ Применяет функции к тексту профиля + """ + def equalTerm(term, sNum, sMD, localVars): + """Локальная функция для вычисления выражения""" + terms = sNum.findall(term) + if terms: + strNumers = [] + for n in terms: + strNum = n.strip() + if "*" in strNum or "/" in strNum: + strNum = multAndDiv(strNum,sNum,sMD,localVars) + try: + num = int(strNum) + except: + minus = False + if strNum[:1] == "-": + minus = True + strNum = strNum[1:] + if localVars.has_key(strNum): + try: + num = int(localVars[strNum]) + except: + print _("error in profile %s")%nameProfile + print _("error local var %s not int")\ + %str(strNum) + cl_base.exit(1) + elif self.objVar.exists(strNum): + try: + num = int(self.objVar.Get(strNum)) + except: + print _("error in profile %s")%nameProfile + print _("error var %s not int")%str(strNum) + cl_base.exit(1) + else: + print _("error in profile %s")%nameProfile + print _("error local var %s not defined")\ + %str(strNum) + cl_base.exit(1) + if minus: + num =-num + strNumers.append(num) + return sum(strNumers) + print _("error in profile %s")%nameProfile + print _("error profile term %s, incorrect data")%str(term) + cl_base.exit(1) + + def multAndDiv(term,sNum,sMD,localVars): + """локальная функция для умножения и деления""" + termTmp = term + varsLocal = sMD.findall(term) + for var in varsLocal: + flagVarTxt = True + try: + int(var) + except: + flagVarTxt = False + if flagVarTxt: + continue + varReplace = str(equalTerm(var,sNum,sMD,localVars)) + termTmp = termTmp.replace(var,varReplace) + ret = eval(termTmp) + return ret + + def funcSum(funTxt,resS,localVars,textProfileTmp): + """локальная функция вычисляет первую функцию sum() в профиле""" + terms = funTxt[4:-1].replace(" ","").split(",") + # Название локальной переменной + nameLocVar = terms[0] + if not localVars.has_key(nameLocVar): + localVars[nameLocVar] = 0 + if len(terms) == 2: + if terms[1].strip(): + localVars[nameLocVar] = equalTerm(terms[1],sNum,sMD, + localVars) + replace = str(localVars[nameLocVar]) + else: + replace = "" + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + elif len(terms) == 3: + if terms[1].strip(): + replaceInt = equalTerm(terms[1],sNum,sMD,localVars) + replace = str(replaceInt) + else: + replace = "" + localVars[nameLocVar] = equalTerm(terms[2], + sNum,sMD,localVars) + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + return textProfileTmp + + + def funcExists(funTxt,resS,textProfileTmp): + """если файл существует читает из файла локальную переменную + + если один параметр - выводит значение локальной переменной + """ + terms = funTxt[7:-1].replace(" ","").split(",") + if len(terms) !=1: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + fileName = terms[0].strip() + if fileName[0] == "~": + # Получаем директорию пользователя + fileName = os.path.join(self.homeDir, + fileName.partition("/")[2],"")[:-1] + elif fileName[0] != "/": + path = os.path.split(nameSystemFile)[0] + fileName=os.path.join(path,fileName) + replace = "" + if os.path.exists(fileName): + replace = "1" + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def funcLoad(funTxt,resS,textProfileTmp): + """если файл существует читает из файла локальную переменную + + если один параметр - выводит значение локальной переменной + """ + terms = funTxt[5:-1].replace(" ","").split(",") + if not terms[0].strip() or\ + (len(terms)==2 and not terms[1].strip()) or\ + len(terms)>2: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + if len(terms) == 2: + if not terms[0] in ["ver","num","char","key"]: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("first argument function is not 'ver' or 'num' or\ + 'char'") + cl_base.exit(1) + if len(terms) == 1: + fileName = terms[0].strip() + # Если домашняя директория + if fileName[0] == "~": + # Получаем директорию пользователя + fileName = os.path.join(self.homeDir, + fileName.partition("/")[2],"")[:-1] + elif fileName[0] != "/": + path = os.path.split(nameSystemFile)[0] + fileName=os.path.join(path,fileName) + else: + fileName = terms[1].strip() + # Если домашняя директория + if fileName[0] == "~": + # Получаем директорию пользователя + fileName = os.path.join(self.homeDir, + fileName.partition("/")[2],"")[:-1] + elif fileName[1] != "/": + path = os.path.split(nameSystemFile)[0] + fileName=os.path.join(path,fileName) + replace = "" + if os.path.exists(fileName): + FD = open(fileName) + replace = FD.read().strip() + FD.close + if not replace and len(terms) == 2 and terms[0] in ["ver","num"]: + replace = "0" + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def getInstallPkgGentoo(): + """Выдает словарь инсталлированных программ и номеров версий""" + pkgs = [] + def getFilesDir(pkgs, dirname, names): + for nameFile in names: + absNameFile = os.path.join(dirname,nameFile) + if os.path.isdir(absNameFile): + tail = absNameFile.split(self.basePkgDir) + if len(tail)==2: + tail = tail[1].split('/') + if len(tail)==3 and tail[1]!='virtual': + pkgs.append(tail[2]) + return True + os.path.walk(self.basePkgDir,getFilesDir, pkgs) + return sharePkg(pkgs) + + def sharePkg(pkgs): + """Получение имен и номеров версий программ""" + pkgs.sort() + installProg = {} + for pkg in pkgs: + findVer = self.reFindVer.search(pkg) + if findVer: + version = findVer.group() + name = pkg.split(version)[0][:-1] + if name in installProg: + installProg[name].append(version) + else: + installProg[name] = [version] + return installProg + + def pkg(nameProg, installProg): + """Выдает установленные версии по имени программы""" + if nameProg in installProg: + return installProg[nameProg][-1] + else: + return "" + + def funcPkg(funTxt,resS,textProfileTmp): + """локальная функция выдает номер версии программы""" + terms = funTxt[4:-1].replace(" ","") + # Название программы + nameProg = terms + # Замена функции в тексте шаблона + replace = "" + if "/" in nameProg: + if nameProg in self.installProg: + replace = pkg(nameProg, self.installProg) + else: + category, spl, nProg = terms.partition("/") + if not category in self.installCategory: + self.installCategory.append(category) + pathCategory = os.path.join(self.basePkgDir, category) + if os.path.exists(pathCategory): + pkgs = os.listdir(pathCategory) + pkgs = map(lambda x: os.path.join(category,x), + pkgs) + installProg = sharePkg(pkgs) + replace = pkg(nameProg, installProg) + self.installProg.update(installProg) + else: + if not self.flagAllPkgScan: + installProg = getInstallPkgGentoo() + self.installProg.update(installProg) + profile.flagAllPkgScan = True + replace = pkg(nameProg, self.installProg) + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def funcRnd(funTxt,resS,textProfileTmp): + """локальная функция выдает строку случайных символов + + первый аргумент: + 'num' - числа, + 'pas' - цифры и буквы + второй аргумент: + количество символов + """ + terms = funTxt[4:-1].replace(" ","").split(",") + if not terms[0].strip() or\ + (len(terms)==2 and not terms[1].strip()) or\ + len(terms)!=2: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + fArgvNames = ['num','pas'] + if not terms[0] in fArgvNames: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("first argument function is not 'num' or 'pas'") + cl_base.exit(1) + try: + lenStr = int(terms[1]) + except: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("two argument function is not number") + cl_base.exit(1) + if terms[0] == fArgvNames[0]: + replace=''.join([random.choice(string.digits)\ + for i in xrange(lenStr)]) + elif terms[0] == fArgvNames[1]: + replace=''.join([random.choice(string.ascii_letters + \ + string.digits) for i in xrange(lenStr)]) + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def funcCase(funTxt,resS,textProfileTmp): + """локальная функция выдает переменную в определенном регистре + + первый аргумент: + 'upper' - верхний регистр, + 'lower' - нижний регистр, + 'capitalize' - первая буква в верхнем регистре + второй аргумент: + название переменной + """ + terms = funTxt[5:-1].replace(" ","").split(",") + if not terms[0].strip() or\ + (len(terms)==2 and not terms[1].strip()) or len(terms)!=2: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + fArgvNames = ['upper','lower','capitalize'] + if not terms[0] in fArgvNames: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("first argument function is not 'upper' or 'lower' or\ + 'capitalize'") + cl_base.exit(1) + try: + strValue = str(self.objVar.Get(terms[1])) + except: + print _("error in profile %s")%nameProfile + print _("error var %s not found")%str(terms[1]) + cl_base.exit(1) + replace = "" + strValue = self._toUNICODE(strValue) + if terms[0] == 'upper': + replace = strValue.upper() + elif terms[0] == 'lower': + replace = strValue.lower() + elif terms[0] == 'capitalize': + replace = strValue.capitalize() + if replace: + replace = replace.encode("UTF-8") + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def funcPush(funTxt,resS,localVars,textProfileTmp): + """локальная функция записывает значение переменной + + в стек глобальных переменных + """ + terms = funTxt[5:-1].replace(" ","").split(",") + # Название локальной переменной + nameLocVar = terms[0] + flagFoundVar = False + if nameLocVar in localVars.keys(): + flagFoundVar = True + value = localVars[nameLocVar] + else: + try: + value = self.objVar.Get(nameLocVar) + flagFoundVar = True + except: + pass + if flagFoundVar: + # Если переменная существует + if len(terms) == 1: + self.stackGlobalVars.append(str(value)) + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("error var %s exists")%str(nameLocVar) + cl_base.exit(1) + else: + # Если переменная не существует + if len(terms) == 1: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("error var %s not exists")%str(nameLocVar) + cl_base.exit(1) + elif len(terms) == 2: + value = terms[1].strip() + self.stackGlobalVars.append(str(value)) + localVars[nameLocVar] = value + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + replace = "" + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def funcPop(funTxt,resS,localVars,textProfileTmp): + """локальная функция получает значение + + из стека глобальных переменных и присвает локальной переменной + + """ + terms = funTxt[4:-1].replace(" ","").split(",") + # Название локальной переменной + nameLocVar = terms[0] + if len(terms) == 1: + if self.stackGlobalVars: + localVars[nameLocVar] = self.stackGlobalVars.pop() + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + print _("error, gloval variables stack is empty") + cl_base.exit(1) + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + replace = "" + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return textProfileTmp + + def loadVarsIni(iniFileName): + """ Читает файл fileName + создает и заполняет переменные на основе этого файла + Используеться совместно c funcIni + """ + localVarsIni = {} + # Выходим если есть предыдущие ошибки + if self.getError(): + return False + # получить объект ini файла + config = cl_base.iniParser(iniFileName) + # получаем все секции из конфигурационного файла + allsect = config.getAllSectionNames() + if not allsect: + if self.getError(): + # Очистка ошибки + _error.error = [] + return localVarsIni + # Заполняем переменные для funcIni + for sect in allsect: + sectVars = config.getAreaVars(sect) + for name in sectVars.keys(): + nameVar = "%s.%s"%(sect,name) + valueVar = sectVars[name] + localVarsIni[nameVar] = valueVar + return localVarsIni + + def getTimeFile(fileName): + # Получаем время модификации файла + if fileName in self.timeConfigsIni: + return self.timeConfigsIni[fileName] + return 0 + + def funcIni(funTxt, resS, textProfileTmp): + """локальная функция записывает и считывает значение переменной + + из ini файла ~./calculate/ini.env + """ + # Создаем директорию + if not os.path.exists(self.pathConfigIni): + os.makedirs(self.pathConfigIni) + os.chown(self.pathConfigIni, int(self.uid), int(self.gid)) + termsRaw = funTxt[4:-1].split(",") + flagFirst = True + terms = [] + for term in termsRaw: + if flagFirst: + terms.append(term.replace(" ","")) + flagFirst = False + else: + val = term.strip() + # Флаг (не найдены кавычки) + flagNotFoundQuote = True + for el in ('"',"'"): + if val.startswith(el) and val.endswith(el): + terms.append(val[1:-1]) + flagNotFoundQuote = False + break + if flagNotFoundQuote: + terms.append(val) + # Название локальной переменной + nameLocVar = terms[0] + namesVar = nameLocVar.split(".") + if len(namesVar) == 1: + nameLocVar = "main.%s"%nameLocVar + elif len(namesVar)>2: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + replace = "" + # Получаем время модификации конфигурационного файла + curTime = getTimeFile(self.fileConfigIni) + if len(terms) == 1: + if self.timeIni != curTime: + # читаем переменные из файла + self.prevDictIni = loadVarsIni(self.fileConfigIni) + self.currDictIni.update(self.prevDictIni) + self.timeIni = getTimeFile(self.fileConfigIni) + if nameLocVar in self.currDictIni.keys(): + replace = self.currDictIni[nameLocVar] + elif len(terms) == 2: + if self.timeIni != curTime: + # читаем переменные из файла + self.prevDictIni = loadVarsIni(self.fileConfigIni) + self.currDictIni.update(self.prevDictIni) + self.timeIni = getTimeFile(self.fileConfigIni) + # Значение локальной переменной + valueLocVar = terms[1] + self.currDictIni[nameLocVar] = valueLocVar + else: + print _("error in profile %s")%nameProfile + print _("error profile term %s")%str(funTxt) + cl_base.exit(1) + textProfileTmp = textProfileTmp[:resS.start()] + replace +\ + textProfileTmp[resS.end():] + return (textProfileTmp) + + # Локальные переменные + localVars = {} + # Регулярное выражние для сложения + sNum = re.compile("\-[^\-\+]+|[^\-\+]+") + # Регулярное выражение для умножениея и деления + sMD = re.compile("[^\-\+\*\/]+") + resS = self._reFunc.search(textProfile) + textProfileTmp = textProfile + flagIniFunc = False + while resS: + mark = textProfileTmp[resS.start():resS.end()] + funTxt = mark[self._deltVarStart:-self._deltVarEnd] + # Функция sum + if funTxt.startswith("sum("): + textProfileTmp = funcSum(funTxt,resS,localVars,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + # Функция load + elif funTxt.startswith("load("): + textProfileTmp = funcLoad(funTxt,resS,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("pkg("): + textProfileTmp = funcPkg(funTxt,resS,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("rnd("): + textProfileTmp = funcRnd(funTxt,resS,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("case("): + textProfileTmp = funcCase(funTxt,resS,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("pop("): + textProfileTmp = funcPop(funTxt,resS,localVars,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("push("): + textProfileTmp = funcPush(funTxt,resS,localVars,textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("ini("): + flagIniFunc = True + textProfileTmp = funcIni(funTxt, resS, textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + elif funTxt.startswith("exists("): + textProfileTmp = funcExists(funTxt, resS, textProfileTmp) + resS = self._reFunc.search(textProfileTmp) + else: + resS = False + if flagIniFunc: + # Очистка файла в случае его ошибочного чтения + if not self.prevDictIni and os.path.exists(self.fileConfigIni): + FD = open(self.fileConfigIni, "r+") + FD.truncate(0) + FD.seek(0) + FD.close() + # Если конф. файл модифицирован шаблоном + curTime = getTimeFile(self.fileConfigIni) + if curTime != self.timeIni: + # Считаем переменные из конф. файла + self.prevDictIni = loadVarsIni(self.fileConfigIni) + self.currDictIni.update(self.prevDictIni) + self.timeIni = curTime + # Если словари переменных не совпадают + if self.prevDictIni != self.currDictIni: + # Запишем переменные в конфигурационный файл + # Создание объекта парсера + config = cl_base.iniParser(self.fileConfigIni) + # секции будущего конфигурационного файла + sects = list(set(map(lambda x: x.split(".")[0],\ + self.currDictIni.keys()))) + # запись переменных в файл + for sect in sects: + dictVar = {} + for varName in self.currDictIni.keys(): + if varName.startswith("%s."%sect): + nameVar = varName.rpartition(".")[2] + valueVar = self.currDictIni[varName] + if valueVar: + dictVar[nameVar] = valueVar + if dictVar: + # Запись переменных в секцию + config.setVar(sect, dictVar) + # читаем переменные из файла + self.prevDictIni = loadVarsIni(self.fileConfigIni) + self.currDictIni.update(self.prevDictIni) + self.timeConfigsIni[self.fileConfigIni] = float(time.time()) + self.timeIni = getTimeFile(self.fileConfigIni) + # Меняем владельца в случае необходимости + if os.path.exists(self.fileConfigIni): + fd = os.open(self.fileConfigIni, os.O_RDONLY) + fst = os.fstat(fd) + uid = fst.st_uid + gid = fst.st_gid + os.close(fd) + if self.uid!=uid or self.gid!=gid: + os.chown(self.fileConfigIni, int(self.uid), int(self.gid)) + return textProfileTmp + + def applyTermsProfile(self, textProfile, nameProfile, nameSystemFile=False): + """ Применяет условия, к условным блокам текста + """ + textTerm = "" + resS = self._reTermBloc.search(textProfile) + textProfileTmp = textProfile + def function(text): + """Функция обработки функций в заголовке""" + return self.applyFuncProfile(text, nameProfile, nameSystemFile) + if nameSystemFile: + while resS: + mark = resS.group(0) + body = resS.group("body") + end = resS.group("end") + parent = resS.group("func") + if not parent: + parent = "" + term = resS.group("rTerm") + parent +\ + resS.group("lTerm") + if self._equalTerm(term, _("content profile not valid: ")+\ + nameProfile, function): + textProfileTmp = textProfileTmp.replace(mark, body+end) + else: + textProfileTmp = textProfileTmp.replace(mark, "") + resS = self._reTermBloc.search(textProfileTmp) + else: + while resS: + mark = resS.group(0) + body = resS.group("body") + end = resS.group("end") + term = resS.group("rTerm") + resS.group("lTerm") + if self._equalTerm(term, _("content profile not valid: ")+\ + nameProfile): + textProfileTmp = textProfileTmp.replace(mark, body+end) + else: + textProfileTmp = textProfileTmp.replace(mark, "") + resS = self._reTermBloc.search(textProfileTmp) + return textProfileTmp + + def getNeedProfile(self, fileProfile): + """Применяем правила к названию файла""" + dirP,fileP = os.path.split(fileProfile) + if fileP: + spFile = fileP.split("?") + realFileName = spFile[0] + if len(spFile)>1: + flagTrue = False + for term in spFile[1:]: + if self._equalTerm(term, _("name profile not valid: ")+\ + fileProfile): + flagTrue = True + break + if flagTrue: + return True + else: + return False + else: + return True + else: + self.setError (_("name profile not valid: ")+ str(fileProfile)) + return False + + def getTitle(self, comment, commentList): + """Выдает заголовок профиля ( версия и.т.д)""" + if comment: + commentFirst = comment + commentInsert = comment + commentLast = comment + flagList = False + # В случае открывающего и закрывающего комментария + if type(comment) == types.TupleType and len(comment) == 2: + commentFirst = comment[0] + commentInsert = "" + commentLast = comment[1] + flagList = True + if flagList: + self._titleBody = commentFirst + "\n" + else: + self._titleBody = commentFirst + self.__titleHead + "\n" + z = 0 + lenCommentList = len(commentList) - 1 + for com in self._titleList: + if lenCommentList < z: + self._titleBody += commentInsert + " " + com + "\n" + else: + self._titleBody += commentInsert + " " + com +\ + " " + commentList[z] + "\n" + z += 1 + if flagList: + self._titleBody += commentLast +"\n" + else: + self._titleBody += commentLast + self.__titleHead + "\n" + return self._titleBody + else: + return "" + + def numberAllProfiles(self, number): + """Количество профилей + + Вызов происходит перед наложением профилей + в момент вызова в number находится количество обрабатываемых файлов + Наследуемая функция + Используется для отображения прогресса при наложениии профилей + """ + return True + + def numberProcessProfiles(self, number): + """Номер текущего обрабатываемого профиля + + Вызов происходит при наложении профиля + в момент вызова в number находится номер обрабатываемого профиля + Наследуемая функция + Используется для отображения прогресса при наложениии профилей + """ + return True + + + def scanDirs(self, profilesDirs, objVar=False): + """Измененный метод сканирования директорий""" + dirs = [] + class dirProf: + def __init__(self): + self.baseDir = False + self.dirs = [] + self.files = [] + self.links = [] + self.sockets = [] + flagError = False + blockDirs = [] + def getFilesDir(dirP, dirname, names): + for nameFile in names: + absNameFile = dirname + "/" + nameFile + findBlock = False + for blDir in blockDirs: + st,mid,end = absNameFile.partition(blDir) + if (not st) and mid and end: + findBlock = True + break + if not findBlock: + if os.path.islink(absNameFile): + dest = absNameFile + src = os.readlink(absNameFile) + dirP.links.append((src,dest)) + elif os.path.isfile(absNameFile): + # Добавляем файлы кроме описаний директорий + if self.profDirNameFile != nameFile: + dirP.files.append(absNameFile) + elif os.path.isdir(absNameFile): + # Обработка условий в названии директории + if self.getNeedProfile(absNameFile): + if self.getError(): + blockDirs.append(absNameFile) + flagError = True + break + dirP.dirs.append(absNameFile) + else: + if self.getError(): + blockDirs.append(absNameFile) + flagError = True + break + blockDirs.append(absNameFile) + elif stat.S_ISSOCK(os.stat(absNameFile)[stat.ST_MODE]): + dirP.sockets.append(absNameFile) + for profileDir in profilesDirs: + if profileDir: + # Обработка условий в названии директории + if self.getNeedProfile(profileDir): + if self.getError(): + flagError = True + break + dirP = dirProf() + dirP.baseDir = profileDir + dirs.append(dirP) + os.path.walk(profileDir, getFilesDir, dirP) + else: + if self.getError(): + flagError = True + break + if flagError: + return [] + return dirs + + + def applyProfiles(self): + """Применяет профили к конфигурационным файлам""" + + def createDictTemplates(path, prefix, dictTemplates): + """Создает словарь {"директория":"кол-во шаблонов" ...} + + и считает общее количество шаблонов + """ + # Количество шаблонов + self.allTemplates += 1 + dirTemplate = os.path.split(path)[0] + while(True): + if dirTemplate in dictTemplates.keys(): + dictTemplates[dirTemplate] += 1 + else: + dictTemplates[dirTemplate] = 1 + if dirTemplate == prefix: + break + dirTemplate = os.path.split(dirTemplate)[0] + return dictTemplates + + if not self.objVar.defined("cl_profile_path"): + self.setError (_("not defined Var: ") + "cl_profile_path") + return False + dirsProfiles = self.objVar.Get("cl_profile_path") + dirsProfiles.sort() + # Словарь измененных директорий + self.changeDirs = {} + # Созданные директории + self.createdDirs = [] + # Примененные файлы + self.filesApply = [] + # Номер применяемого шаблона + self.numberProcessProf = 0 + # Словарь директорий с количеством файлов шаблонов + self.dictTemplates = {} + # Количество шаблонов + self.allTemplates = 0 + # Время доступа к конфигурационному файлу функции шаблона ini() + self.timeIni = -1 + # Первоначальный словарь переменных для ini() + self.prevDictIni = {} + # Текущий словарь переменных для ini() + self.currDictIni = {} + # Словарь времен модификации env файлов + self.timeConfigsIni = {} + if self._servDir: + tmpDirsProfiles = [] + for dirP in dirsProfiles: + dirProf = dirP + self._servDir + if os.access(dirProf, os.F_OK): + # Если директория существует + tmpDirsProfiles.append(dirProf) + dirsProfiles = tmpDirsProfiles + scanObj = processingTemplates() + scanObj.processingFile = lambda x,y: createDictTemplates(x, y,\ + self.dictTemplates) + # Считаем количество шаблонов + for dirTemplate in dirsProfiles: + scanObj.scanningTemplates(dirTemplate, + skipFile=self.filesFilter, + skipDir=self.dirsFilter) + self.numberAllProfiles(self.allTemplates) + for dirTemplate in dirsProfiles: + if self.scanningTemplates(dirTemplate, + skipFile=self.filesFilter, + skipDir=self.dirsFilter) is False: + return False + return (self.createdDirs, self.filesApply) + + + def processingFile(self, path, prefix): + """Обработка в случае профиля файла""" + self.numberProcessProf += 1 + self.numberProcessProfiles(self.numberProcessProf) + # Пропуск шаблонов директорий + if self.profDirNameFile == os.path.split(path)[1]: + return True + if self.getNeedProfile(path): + if self.getError(): + return False + fileProfileChange = path + findChangeDir = False + listD = self.changeDirs.items() + listD.reverse() + for dirChangeIn, dirChangeOut in listD: + st,mid,end = path.partition(dirChangeIn) + if (not st) and mid and end: + findChangeDir = True + break + if findChangeDir: + oldFile = dirChangeOut + end + else: + oldFile = path.partition(prefix)[2] + # файл в системе без условий + oldFile = "/".join(map(lambda x:x.split("?")[0],\ + oldFile.split("/"))) + if not findChangeDir: + oldFile = self._baseDir + oldFile + # Фильтрация профилей по названию файла + if oldFile in self.filesFilter: + return True + listProfTitle = prefix.split("/")[-2:] + profTitle = '"' + "/".join(listProfTitle) + '"' + # Записываем в переменную обрабатываемый файл + self.objVar.Set("cl_pass_file",oldFile) + # Пишем время модификации *.env файлов + if oldFile.endswith(".env"): + self.timeConfigsIni[oldFile] = float(time.time()) + filesApl = self.join(path, oldFile, + (self.programVersion,profTitle)) + if filesApl: + self.filesApply += filesApl + else: + if self.getError(): + #print self.getError() + return False + return True + + def processingDirectory(self, path, prefix): + """Обработка в случае директории если возвращаем None то пропуск дир.""" + # Файл шаблона директории + dirInfoFile = os.path.join(path, self.profDirNameFile) + # Проверяем заголовок + ret = self.__isApplyHeadDir(dirInfoFile) + if not ret: + if self.getError(): + self.setError(_("Incorrect profile: " ) + dirInfoFile) + return False + # Добавление количества файлов в пропущенной директории + if path in self.dictTemplates.keys(): + self.numberProcessProf += self.dictTemplates[path] + return None + newDir = self._baseDir + path.partition(prefix)[2] + newDir = "/".join(map(lambda x:x.split("?")[0],\ + newDir.split("/"))) + # Применяем шаблон + pathDir, objHeadDir = self.__getApplyHeadDir(newDir, + dirInfoFile, + self.changeDirs) + # Фильтрация профилей по названию директории + if pathDir in self.dirsFilter: + # Добавление количества файлов в пропущенной директории + if path in self.dictTemplates.keys(): + self.numberProcessProf += self.dictTemplates[path] + return None + if objHeadDir: + if isinstance(objHeadDir, dirHeader): + if not objHeadDir.headerCorrect: + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + self.setError(objHeadDir.errorMessage) + return False + if not objHeadDir.headerTerm: + if objHeadDir.getError(): + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + return False + if objHeadDir.params.has_key("name"): + self.changeDirs[path] = pathDir + crDirs = self.createDir(prefix, path, + self._baseDir, pathDir) + if crDirs is False: + return False + self.createdDirs += crDirs + else: + if self.getError(): + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + return False + # Добавление количества файлов в пропущенной директории + if path in self.dictTemplates.keys(): + self.numberProcessProf += self.dictTemplates[path] + return None + return True + + def __getGenHeadDir(self, newDir, profileDirFile, changeDirs): + """Определяет название создаваемой директории""" + + def function(text): + """Функция обработки функций в заголовке""" + return self.applyFuncProfile(text, newDir, profileDirFile) + + newDirMv = newDir + findChangeDir = False + #Меняем путь к директории + listD = changeDirs.items() + listD.reverse() + for dirChangeIn, dirChangeOut in listD: + st,mid,end = profileDirFile.partition(dirChangeIn) + if (not st) and mid: + findChangeDir = True + break + if findChangeDir: + pathRel = dirChangeOut + lenPathRel = len(pathRel.split("/")) + lenNewDir = len(newDir.split("/")) + lenEndDir = lenNewDir - lenPathRel + tmpDir = newDir + namesDirs = [] + for i in range(lenEndDir): + namesDirs.append(os.path.split(tmpDir)[1]) + tmpDir = os.path.split(tmpDir)[0] + namesDirs.reverse() + nameDir = "/".join(namesDirs) + newDirMv = os.path.join(pathRel, nameDir) + applyDir = newDirMv + if not os.path.exists(profileDirFile): + return (applyDir, True) + try: + FD = open(profileDirFile) + textProfile = FD.read() + FD.close() + except: + self.setError(_("Error open profile: " ) +\ + profileDirFile) + return (applyDir, False) + objHead = dirHeader(textProfile, self.objVar,function) + if not objHead.headerCorrect: + self.setError(_("Incorrect profile: " ) +\ + profileDirFile) + self.setError(objHead.errorMessage) + return (applyDir, False) + if not objHead.headerTerm: + if objHead.getError(): + self.setError(_("Incorrect profile: " ) +\ + profileDirFile) + return (applyDir, False) + # Изменяем название директории + if objHead.params.has_key("name"): + nameDir = objHead.params['name'] + if "/" in nameDir or nameDir == ".." or nameDir == ".": + self.setError (_("False value 'name' in profile: " ) +\ + profileDirFile) + return (applyDir, False) + if not findChangeDir: + pathRel = os.path.split(os.path.abspath(newDirMv))[0] + # Новый путь к оригинальному файлу + newDirMv = os.path.join(pathRel, nameDir) + applyDir = newDirMv + # При удаленнии директории + if objHead.typeAppend == "remove": + return (applyDir, False) + return (applyDir, objHead) + + def scanProfiles(self, serviceName=False, dirObjs=False): + """Сканирует директории профилей - выводит список файлов""" + if not self.objVar.defined("cl_profile_path"): + self.setError (_("not defined Var: ") + "cl_profile_path") + return False + if not dirObjs: + if serviceName : + self._servDir = serviceName + if self._servDir: + if self._servDir[0] != "/": + self._servDir = "/" + self._servDir + if self._servDir[-1] != "/": + self._servDir += "/" + self._servDir = os.path.split(self._servDir)[0] + dirsProfiles = self.objVar.Get("cl_profile_path") + if self._servDir: + tmpDirsProfiles = [] + for dirP in dirsProfiles: + dirProf = dirP + self._servDir + if os.access(dirProf, os.F_OK): + # Если директория существует + tmpDirsProfiles.append(dirProf) + else: + tmpDirsProfiles.append(False) + dirsProfiles = tmpDirsProfiles + dirObjs = self.scanDirs(dirsProfiles,self.objVar) + #файлы к которым были применены профили + filesApply = [] + # Словарь измененных директорий + changeDirs = {} + dirObjsApply = [] + for dirObj in dirObjs: + dirInfoFile = os.path.join(dirObj.baseDir, self.profDirNameFile) + ret = self.__isApplyHeadDir(dirInfoFile) + if ret: + dirObjsApply.append(dirObj) + else: + if self.getError(): + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + return False + for dirObj in dirObjsApply: + # сортируем файлы по названию + if dirObj.files: + dirObj.files.sort() + # сортируем директории по названию + if dirObj.dirs: + dirObj.dirs.sort() + blockDirs = [] + for dirProfile in dirObj.dirs: + newDir = self._baseDir + dirProfile.partition(dirObj.baseDir)[2] + newDir = "/".join(map(lambda x:x.split("?")[0],\ + newDir.split("/"))) + # Проверяем условие на директорию + dirInfoFile = os.path.join(dirProfile, self.profDirNameFile) + pathDir, objHeadDir = self.__getGenHeadDir(newDir, + dirInfoFile, + changeDirs) + # Фильтрация профилей по названию директории + if pathDir in self.dirsFilter: + blockDirs.append(dirProfile) + continue + if objHeadDir: + if isinstance(objHeadDir, dirHeader): + if not objHeadDir.headerCorrect: + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + self.setError(objHeadDir.errorMessage) + return False + if not objHeadDir.headerTerm: + if objHeadDir.getError(): + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + return False + if objHeadDir.params.has_key("name"): + changeDirs[dirProfile] = pathDir + else: + if self.getError(): + self.setError(_("Incorrect profile: " ) +\ + dirInfoFile) + return False + blockDirs.append(dirProfile) + for fileProfile in dirObj.files: + findBlock = False + for blDir in blockDirs: + st,mid,end = fileProfile.partition(blDir) + if (not st) and mid and end: + findBlock = True + break + if findBlock: + continue + if self.getNeedProfile(fileProfile): + if self.getError(): + #print self.getError() + return False + fileProfileChange = fileProfile + findChangeDir = False + listD = changeDirs.items() + listD.reverse() + for dirChangeIn, dirChangeOut in listD: + st,mid,end = fileProfile.partition(dirChangeIn) + if (not st) and mid and end: + findChangeDir = True + break + if findChangeDir: + oldFile = dirChangeOut + end + else: + oldFile = fileProfile.partition(dirObj.baseDir)[2] + # файл в системе без условий + oldFile = "/".join(map(lambda x:x.split("?")[0],\ + oldFile.split("/"))) + if not findChangeDir: + oldFile = self._baseDir + oldFile + # Фильтрация профилей по названию файла + if oldFile in self.filesFilter: + continue + filesApply.append(oldFile) + else: + if self.getError(): + return False + return filesApply + + def __isApplyHeadDir(self, profileDirFile): + """Будет ли применен профиль корневой директории + + Возвращает True, False + """ + + def function(text): + """Функция обработки функций в заголовке""" + return self.applyFuncProfile(text, "", profileDirFile) + + if not os.path.exists(profileDirFile): + return True + try: + FD = open(profileDirFile) + textProfile = FD.read() + FD.close() + except: + self.setError(_("Error open profile: " ) +\ + profileDirFile) + return False + # Заменяем переменные на их значения + textProfile = self.applyVarsProfile(textProfile, profileDirFile) + # Обработка заголовка + objHead = dirHeader(textProfile, self.objVar,function) + if not objHead.headerCorrect: + self.setError(_("Incorrect profile: " ) +\ + profileDirFile) + self.setError(objHead.errorMessage) + return False + if not objHead.headerTerm: + if objHead.getError(): + self.setError(_("Incorrect profile: " ) +\ + profileDirFile) + return False + + ## Изменяем название директории + #if objHead.params.has_key("name"): + #self.setError (_("Invalid name 'name' in profile: " ) +\ + #profileDirFile) + #return False + + ## Удаляем директорию + #if objHead.typeAppend == "remove": + #self.setError (_("Invalid name 'remove' in profile: " ) +\ + #profileDirFile) + #return False + + ## chmod - изменяем права + #if objHead.params.has_key("chmod"): + #self.setError (_("Invalid name 'chmod' in profile: " ) +\ + #profileDirFile) + #return False + + ## chown - изменяем владельца и группу + #if objHead.params.has_key("chown"): + #self.setError (_("Invalid name 'chown' in profile: " ) +\ + #profileDirFile) + #return False + return True + + def __getApplyHeadDir(self, newDir, profileDirFile, changeDirs): + """Применяет профиль к директории (права, владелец, и.т. д)""" + + def function(text): + """Функция обработки функций в заголовке""" + return self.applyFuncProfile(text, newDir, profileDirFile) + + newDirMv = newDir + findChangeDir = False + #Меняем путь к директории + listD = changeDirs.items() + listD.reverse() + for dirChangeIn, dirChangeOut in listD: + st,mid,end = profileDirFile.partition(dirChangeIn) + if (not st) and mid: + findChangeDir = True + break + if findChangeDir: + pathRel = dirChangeOut + lenPathRel = len(pathRel.split("/")) + lenNewDir = len(newDir.split("/")) + lenEndDir = lenNewDir - lenPathRel + tmpDir = newDir + namesDirs = [] + for i in range(lenEndDir): + namesDirs.append(os.path.split(tmpDir)[1]) + tmpDir = os.path.split(tmpDir)[0] + namesDirs.reverse() + nameDir = "/".join(namesDirs) + newDirMv = os.path.join(pathRel, nameDir) + applyDir = newDirMv + if not os.path.exists(profileDirFile): + return (applyDir, True) + try: + FD = open(profileDirFile) + textProfile = FD.read() + FD.close() + except: + self.setError(_("Error open profile: " ) +\ + profileDirFile) + return (applyDir, False) + # Заменяем переменные на их значения + textProfile = self.applyVarsProfile(textProfile, profileDirFile) + # Обработка заголовка + objHead = dirHeader(textProfile, self.objVar,function) + if not objHead.headerCorrect: + self.setError(_("Incorrect profile: " ) +\ + profileDirFile) + self.setError(objHead.errorMessage) + return (applyDir, False) + if not objHead.headerTerm: + if objHead.getError(): + self.setError(_("Incorrect profile: " ) +\ + profileDirFile) + return (applyDir, False) + + # Изменяем название директории + if objHead.params.has_key("name"): + nameDir = objHead.params['name'] + if "/" in nameDir or nameDir == ".." or nameDir == ".": + self.setError (_("False value 'name' in profile: " ) +\ + profileDirFile) + return (applyDir, False) + if not findChangeDir: + pathRel = os.path.split(os.path.abspath(newDirMv))[0] + # Новый путь к оригинальному файлу + newDirMv = os.path.join(pathRel, nameDir) + applyDir = newDirMv + + # Удаляем директорию + if objHead.typeAppend == "remove": + if os.path.isdir(newDirMv): + # удаляем директорию + try: + self.removeDir(newDirMv) + except: + self.setError(_("Can not delete dir: " ) +\ + newDirMv) + return (applyDir, False) + + # chmod - изменяем права + if objHead.params.has_key("chmod"): + mode = self.__octToInt(objHead.params['chmod']) + if mode: + if not os.path.exists(newDirMv): + os.mkdir(newDirMv, mode) + else: + os.chmod(newDirMv, mode) + else: + self.setError (_("False value 'chmod' in profile: " ) +\ + profileDirFile) + return (applyDir, False) + # chown - изменяем владельца и группу + if objHead.params.has_key("chown"): + owner = objHead.params['chown'] + if owner: + if ":" in owner: + strUid, strGid = owner.split(":") + import pwd + try: + uid = pwd.getpwnam(strUid)[2] + except: + self.setError (_("Not user in this system: ") + strUid) + self.setError (_("False value 'chown' in profile: " )+\ + profileDirFile) + return (applyDir, False) + try: + import grp + gid = grp.getgrnam(strGid)[2] + except: + self.setError (_("Not group in this system: ")+strGid) + self.setError (_("False value 'chown' in profile: " )+\ + profileDirFile) + return (applyDir, False) + + if not os.path.exists(newDirMv): + dirProfile = os.path.split(profileDirFile)[0] + try: + mode,uidTmp,gidTmp = self.getModeFile(dirProfile) + except OSError: + self.setError (_("not access dir:" ) + newDirMv) + return (applyDir, False) + os.mkdir(newDirMv, mode) + os.chown(newDirMv, uid, gid) + else: + self.setError (_("False value 'chown' in profile: " ) +\ + profileDirFile) + return (applyDir, False) + else: + self.setError (_("False value 'chown' in profile: " ) +\ + profileDirFile) + return (applyDir, False) + return (applyDir, objHead) + + def __getApplyHeadProfile(self ,newFile, oldFile, copyFile): + """Применяет заголовок к профилю (права, владелец, и.т. д)""" + + def function(text): + """Функция обработки функций в заголовке""" + return self.applyFuncProfile(text, newFile, oldFile) + + self.closeFiles() + # Файлы в системе к которым были применены профили + applyFiles = [oldFile] + if copyFile: + self.nameFileNew = self.absFileName(newFile) + self.FN = self.openNewFile(self.nameFileNew) + self.newProfile = self.FN.read() + self.closeNewFile() + objHeadNew = fileHeader(self.newProfile, False, + self.getFileType(),objVar=self.objVar, + function=function) + if not objHeadNew.headerCorrect: + self.setError(_("Incorrect profile: " ) +\ + newFile) + self.setError(objHeadNew.errorMessage) + return (applyFiles, False) + if not objHeadNew.headerTerm: + if objHeadNew.getError(): + self.setError(_("Incorrect profile: " ) +\ + newFile) + return (applyFiles, False) + + pathProg = "" + # Путь к оригинальному файлу + pathOldFile = oldFile + # Изменяем путь к оригинальному файлу + if objHeadNew.params.has_key("name"): + nameFile = objHeadNew.params['name'] + if "/" in nameFile or nameFile == ".." or nameFile == ".": + self.setError (_("False value 'name' in profile: " ) + newFile) + return False + pathRel = os.path.split(os.path.abspath(oldFile))[0] + # Новый путь к оригинальному файлу + pathOldFile = os.path.join(pathRel,nameFile) + + # В случае force + if objHeadNew.params.has_key("force"): + if os.path.islink(pathOldFile): + # удаляем ссылку + try: + os.unlink(pathOldFile) + except: + self.setError(_("Can not delete link: " ) +\ + pathOldFile) + return (applyFiles, False) + if os.path.isfile(pathOldFile): + # удаляем файл + try: + os.remove(pathOldFile) + except: + self.setError(_("Can not delete file: " ) +\ + pathOldFile) + return (applyFiles, False) + + # Удаляем оригинальный файл + if objHeadNew.typeAppend == "remove": + if os.path.islink(pathOldFile): + # удаляем ссылку + try: + os.unlink(pathOldFile) + except: + self.setError(_("Can not delete link: " ) +\ + pathOldFile) + if os.path.isfile(pathOldFile): + # удаляем файл + try: + os.remove(pathOldFile) + except: + self.setError(_("Can not delete file: " ) +\ + pathOldFile) + return (applyFiles, False) + + flagSymlink = False + flagForce = False + # Если есть параметр mirror + if objHeadNew.params.has_key("mirror"): + if objHeadNew.params.has_key("link"): + profileFile = objHeadNew.params['link'] + if not os.path.exists(profileFile): + if os.path.exists(pathOldFile): + os.remove(pathOldFile) + return (applyFiles, False) + elif not os.path.exists(pathOldFile): + return (applyFiles, False) + # Если есть указатель на файл профиля (link) + if objHeadNew.params.has_key("link") and\ + not objHeadNew.params.has_key("symbolic"): + profileFile = objHeadNew.params['link'] + foundProfileFile = os.path.exists(profileFile) + if foundProfileFile: + FO = self.openNewFile(profileFile) + buff = FO.read() + FO.close() + if os.path.exists(pathOldFile): + os.remove(pathOldFile) + if foundProfileFile: + fd = os.open(pathOldFile, os.O_CREAT) + os.close(fd) + os.chmod(pathOldFile, self._mode) + os.chown(pathOldFile, self._uid, self._gid) + FON = open (pathOldFile, "r+") + FON.write(buff) + FON.close() + + # Если символическая ссылка + if objHeadNew.params.has_key("symbolic"): + prevOldFile = pathOldFile + pathOldFile = objHeadNew.params['link'] + flagSymlink = True + if not "/" == pathOldFile[0]: + pathLink = os.path.split(os.path.abspath(prevOldFile))[0] + pathProg = os.getcwd() + os.chdir(pathLink) + + # chmod - изменяем права + if objHeadNew.params.has_key("chmod"): + mode = self.__octToInt(objHeadNew.params['chmod']) + if mode: + if not os.path.exists(pathOldFile): + fd = os.open(pathOldFile, os.O_CREAT) + os.close(fd) + os.chmod(pathOldFile, mode) + else: + self.setError (_("False value 'chmod' in profile: " ) +\ + newFile) + return (applyFiles, False) + + # chown - изменяем владельца и группу + if objHeadNew.params.has_key("chown"): + owner = objHeadNew.params['chown'] + if owner: + if ":" in owner: + strUid, strGid = owner.split(":") + import pwd + try: + uid = pwd.getpwnam(strUid)[2] + except: + self.setError (_("Not user in this system: ") + strUid) + self.setError (_("False value 'chown' in profile: " )+\ + newFile) + return (applyFiles, False) + try: + import grp + gid = grp.getgrnam(strGid)[2] + except: + self.setError (_("Not group in this system: ")+strGid) + self.setError (_("False value 'chown' in profile: " )+\ + newFile) + return (applyFiles, False) + + if not os.path.exists(pathOldFile): + FO = self.openNewFile(newFile) + FO.close() + fd = os.open(pathOldFile, os.O_CREAT) + os.close(fd) + os.chmod(pathOldFile, self._mode) + os.chown(pathOldFile, uid, gid) + else: + self.setError (_("False value 'chown' in profile: " ) +\ + newFile) + return (applyFiles, False) + else: + self.setError (_("False value 'chown' in profile: " ) +\ + newFile) + return (applyFiles, False) + + self.openFiles(newFile, pathOldFile) + if flagSymlink: + if os.path.exists(prevOldFile) or os.path.islink(prevOldFile): + if os.path.islink(prevOldFile): + # если ссылка то удаляем её + os.unlink(prevOldFile) + else: + # иначе удаляем файл + os.remove(prevOldFile) + if not "/" == pathOldFile[0]: + os.symlink(pathOldFile, prevOldFile) + applyFiles = [prevOldFile,os.path.join(pathLink,pathOldFile)] + else: + os.symlink(pathOldFile, prevOldFile) + applyFiles = [prevOldFile,pathOldFile] + if not objHeadNew.body.strip(): + return (applyFiles, False) + else: + applyFiles = [pathOldFile] + if pathProg: + os.chdir(pathProg) + # Если файлы заменяются не нужно их обрабатывать дальше + if objHeadNew.typeAppend == "replace" and\ + not objHeadNew.params.has_key("symbolic") and\ + objHeadNew.params.has_key("link"): + return (applyFiles, False) + return (applyFiles, objHeadNew) + + def createNewClass(self, name, bases, attrs={}): + """Создает объект нового класса + + createNewClass(self, name, bases, attrs) + name - имя класса - str, + bases - cписок наследуемых классов - (tuple), + attrs - аттрибуты класса - {dict} + """ + class newMethod: + #Объединяем конфигурации + def join(self, newObj): + if newObj.__class__.__name__ == self.__class__.__name__: + self.docObj.joinDoc(newObj.doc) + # Пост обработка + if 'postXML' in dir(self): + self.postXML() + attrsNew = {} + attrsNew["configName"] = name + if attrs: + for key in attrs.keys(): + attrsNew[key] = attrs[key] + newCl = type(name, bases + (newMethod, object,), attrsNew) + return newCl + + def fileIsUtf(self, fileName): + """Проверяет файл на кодировку UTF-8""" + if os.path.exists(fileName): + FD = open(self.absFileName(fileName)) + newProfile = FD.read() + FD.close() + try: + newProfile.decode("UTF-8") + except: + return False + return True + + def join(self, newFile, oldFile, ListOptTitle): + """Объединения профиля и конф. файла + + join(newFile, oldFile, ListOptTitle) + Объединение профиля newFile и конф. файла oldFile, + ListOptTitle - список строк которые добавятся в заголовок + """ + # Выполняем условия для блока текста а так-же заменяем переменные + self.nameFileNew = self.absFileName(newFile) + self.FN = self.openNewFile(self.nameFileNew) + self.newProfile = self.FN.read() + self.closeNewFile() + copyFile = True + if self.getFileType() != "bin": + # Вычисляем условные блоки + self.newProfile = self.applyTermsProfile(self.newProfile, + newFile, oldFile) + #print "|%s|" %(self.newProfile) + # Заменяем переменные на их значения + self.newProfile = self.applyVarsProfile(self.newProfile, + newFile) + # Вычисляем функции + self.newProfile = self.applyFuncProfile(self.newProfile, + newFile, oldFile) + copyFile = False + filesApply, objHeadNew = self.__getApplyHeadProfile(newFile, oldFile, + copyFile) + if not objHeadNew: + return filesApply + # Флаг - кодировка с бинарными примесями у файла профиля включаем при + # условии текстового файла и кодировки отличной от UTF-8 + flagNotUtf8New = False + # Флаг - кодировка с бинарными примесями у оригинального файла + flagNotUtf8Old = False + if not copyFile: + # проверяем кодировку профиля + if not self.fileIsUtf(newFile): + flagNotUtf8New = True + if not (objHeadNew.params.has_key("link") and\ + objHeadNew.params.has_key("symbolic")): + # проверяем кодировку оригинального файла + if not self.fileIsUtf(oldFile): + flagNotUtf8Old = True + self.newProfile = objHeadNew.body + #if objHeadNew.fileType != "bin": + #self.newProfile = self.applyTermsProfile(self.newProfile, + #newFile) + #self.newProfile = self.applyVarsProfile(self.newProfile) + + # Титл конфигурационного файла + title = "" + if ListOptTitle: + title = self.getTitle(objHeadNew.comment, + ListOptTitle) + title = title.encode("UTF-8") + + objHeadOld = False + if objHeadNew.comment: + objHeadOld = fileHeader(self.oldProfile, objHeadNew.comment) + # Тестирование + #print self.nameFileOld + #print objHeadNew.typeAppend + if objHeadNew.fileType: + # Создаем объект в случае параметра format в заголовке + if (objHeadNew.typeAppend == "replace" or\ + objHeadNew.typeAppend == "before" or\ + objHeadNew.typeAppend == "after") and\ + not (objHeadNew.fileType == "bin" or\ + objHeadNew.fileType == "raw"): + # Преобразовываем бинарные файлы + if flagNotUtf8New: + objTxtCoder = utfBin() + self.newProfile = objTxtCoder.encode(self.newProfile) + try: + exec ("objProfNew=%s(self.newProfile)"%\ + (objHeadNew.fileType)) + except NameError: + #Создаем объект из self.newObjProt с помощью + # метаклассов + if self.newObjProt.has_key(objHeadNew.fileType): + objProfNewCl = self.createNewClass(\ + objHeadNew.fileType, + self.newObjProt[objHeadNew.fileType]) + objProfNew = objProfNewCl(self.newProfile) + else: + self.setError (\ + _("False join profile for type profile: ")\ + + objHeadNew.fileType + " : " +\ + newFile) + return False + + if "xml_" in objHeadNew.fileType: + if objProfNew.getError(): + self.setError (_("False profile: " ) + newFile) + return False + nameRootNode = oldFile.rpartition("/")[2].split(".")[0] + objProfNew.setNameBodyNode(nameRootNode) + # Объект Документ + docObj = objProfNew.docObj + # Удаление комментариев из документа + docObj.removeComment(docObj.getNodeBody()) + # Добавление необходимых переводов строк + docObj.insertBRtoBody(docObj.getNodeBody()) + # Добавление необходимых разделителей между областями + docObj.insertBeforeSepAreas(docObj.getNodeBody()) + # Пост обработка + if 'postXML' in dir(objProfNew): + objProfNew.postXML() + # Получение текстового файла из XML документа + self.newProfile = objProfNew.getConfig().encode("UTF-8") + # Если не UTF-8 производим преобразование + if flagNotUtf8New: + self.newProfile = objTxtCoder.decode(self.newProfile) + # Титл для объединения + if ListOptTitle: + title = self.getTitle(objProfNew._comment, + ListOptTitle) + title = title.encode("UTF-8") + # Замена + if objHeadNew.typeAppend == "replace": + if "xml_" in objHeadNew.fileType: + data = self.newProfile.split("\n") + data.insert(1,title) + self.oldProfile = "\n".join(data) + else: + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr+title+\ + self.newProfile + else: + self.oldProfile = title + self.newProfile + self.saveOldFile() + return filesApply + # Впереди + elif objHeadNew.typeAppend == "before": + if "xml_" in objHeadNew.fileType: + self.setError (\ + _("False option append=before in profile %s") %newFile) + return False + if objHeadOld and objHeadOld.body: + self.oldProfile = objHeadOld.body + if self.newProfile[-1] == "\n": + tmpProfile = self.newProfile + self.oldProfile + else: + tmpProfile = self.newProfile + "\n" + self.oldProfile + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + title + tmpProfile + elif objHeadOld.execStr: + self.oldProfile = objHeadOld.execStr + title + tmpProfile + else: + self.oldProfile = title + tmpProfile + + #print self.oldProfile + self.saveOldFile() + return filesApply + # Cзади + elif objHeadNew.typeAppend == "after": + if "xml_" in objHeadNew.fileType: + self.setError (\ + _("False option append=after in profile %s") %newFile) + return False + if objHeadOld and objHeadOld.body: + self.oldProfile = objHeadOld.body + if self.newProfile[-1] == "\n": + tmpProfile = self.oldProfile + self.newProfile + else: + tmpProfile = self.oldProfile + "\n" + self.newProfile + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + title + tmpProfile + elif objHeadOld.execStr: + self.oldProfile = objHeadOld.execStr + title + tmpProfile + else: + self.oldProfile = title + tmpProfile + self.saveOldFile() + return filesApply + # Объединение + elif objHeadNew.typeAppend == "join": + if flagNotUtf8New: + objTxtCoder = utfBin() + self.newProfile = objTxtCoder.encode(self.newProfile) + try: + exec ("objProfNew=%s(self.newProfile)"%\ + (objHeadNew.fileType)) + except NameError: + #Создаем объект из self.newObjProt с помощью + # метаклассов + if self.newObjProt.has_key(objHeadNew.fileType): + objProfNewCl = self.createNewClass(\ + objHeadNew.fileType, + self.newObjProt[objHeadNew.fileType]) + objProfNew = objProfNewCl(self.newProfile) + else: + self.setError (\ + _("False join profile for type profile: ")\ + + objHeadNew.fileType + " : " +\ + newFile) + return False + if "xml_" in objHeadNew.fileType: + if objProfNew.getError(): + self.setError (_("False profile: " ) + newFile) + return False + nameRootNode = oldFile.rpartition("/")[2].split(".")[0] + objProfNew.setNameBodyNode(nameRootNode) + # Титл для объединения + if ListOptTitle: + title = self.getTitle(objProfNew._comment, + ListOptTitle) + title = title.encode("UTF-8") + + # В случае пустого конфигурационного файла + reNoClean = re.compile("[^\s]",re.M) + if not self.oldProfile or\ + not reNoClean.search(self.oldProfile): + self.oldProfile = "" + #if objHeadNew.execStr: + #self.oldProfile = objHeadNew.execStr + \ + #title + objProfNew.getConfig().encode("UTF-8") + #else: + #self.oldProfile = title +\ + #objProfNew.getConfig().encode("UTF-8") + #self.saveOldFile() + #return True + + objHeadOld = fileHeader(self.oldProfile, objProfNew._comment) + if objHeadOld.body: + self.oldProfile = objHeadOld.body + else: + self.oldProfile = "" + if flagNotUtf8Old: + objTxtCoder = utfBin() + self.oldProfile = objTxtCoder.encode(self.oldProfile) + if self.newObjProt.has_key(objHeadNew.fileType): + objProfOldCl = self.createNewClass(\ + objHeadNew.fileType, + self.newObjProt[objHeadNew.fileType]) + objProfOld = objProfOldCl(self.oldProfile) + else: + exec ("objProfOld=%s(self.oldProfile)"%\ + (objHeadNew.fileType)) + if "xml_" in objHeadNew.fileType: + if objProfOld.getError(): + self.setError (_("False profile: " ) + oldFile) + return False + nameRootNode = oldFile.rpartition("/")[2].split(".")[0] + objProfOld.setNameBodyNode(nameRootNode) + + #print "#%s#" %(objProfOld.docObj.body.toprettyxml()) + #print "#%s#" %(objProfNew.docObj.body.toprettyxml()) + objProfOld.join(objProfNew) + #print objProfOld.doc.toprettyxml() + #print objProfNew.doc.toprettyxml() + if "xml_" in objHeadNew.fileType: + if objProfOld.getError(): + self.setError (_("False profile: " ) + newFile) + return False + data = \ + objProfOld.getConfig().encode("UTF-8").split("\n") + data.insert(1,title) + self.oldProfile = "\n".join(data) + else: + if objHeadNew.execStr: + self.oldProfile = objHeadNew.execStr + title +\ + objProfOld.getConfig().encode("UTF-8") + elif objHeadOld.execStr: + self.oldProfile = objHeadOld.execStr + title +\ + objProfOld.getConfig().encode("UTF-8") + else: + self.oldProfile = title +\ + objProfOld.getConfig().encode("UTF-8") + # Декодируем если кодировка не UTF-8 + if flagNotUtf8New or flagNotUtf8Old: + self.newProfile = objTxtCoder.decode(self.newProfile) + self.oldProfile = objTxtCoder.decode(self.oldProfile) + self.saveOldFile() + return filesApply + else: + self.setError (_("False (type append) profile: " ) +\ + objHeadNew.typeAppend) + return False + else: + self.setError (_("Type profile not found: ") + newFile) + return False + return filesApply + +class samba(objShare): + """Класс для обработки конфигурационного файла типа samba + + """ + _comment = "#" + configName = "samba" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*|\s*;.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # XML документ + self.doc = self.docObj.doc + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + continue + firstArea = False + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment")): + if xmlArea.nextSibling: + parentNode = xmlArea.parentNode + nextNode = xmlArea.nextSibling + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + nextNode) + # Удаление лишних переводов строк + childNodes = self.docObj.getFieldsArea(self.docObj.body) + lenBr = 0 + removeBrNodes = [] + for node in childNodes: + if node.tagName == "field" and\ + self.docObj.getTypeField(node) == "br": + lenBr += 1 + if lenBr > 2: + removeBrNodes.append(node) + else: + lenBr = 0 + # Удаление + for rmNode in removeBrNodes: + self.docObj.body.removeChild(rmNode) + + + def join(self, sambaObj): + """Объединяем конфигурации""" + if isinstance(sambaObj, samba): + self.docObj.joinDoc(sambaObj.doc) + self.postXML() + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def splitCleanBloc(self, txtBloc): + """Делим блок на две части (переменные, пустые строки в конце)""" + txtLines = txtBloc.split("\n") + firstBloc = [] + nextBloc = [] + txtLines.reverse() + z = 0 + for txtLine in txtLines: + if not txtLine.strip(): + nextBloc.append(txtLine) + else: + break + z += 1 + txtLines.reverse() + firstBloc = txtLines[:-z] + nextBloc.reverse() + if nextBloc: + firstBloc.append("") + if nextBloc and "\n".join(nextBloc): + return ("\n".join(firstBloc), "\n".join(nextBloc)) + else: + return False + + def getFullAreas(self, blocs): + """Делит текст на области, (Заголовок, тело) + + Возвращает два списка: заголовки, тела + """ + headsAreas = [] + bodyAreas = [] + if not blocs: + return [] + lenBlocs = len(blocs[0]) + for i in range(lenBlocs): + txtBloc = blocs[1][i] + clean = self.splitCleanBloc(txtBloc) + if clean: + headsAreas.append(blocs[0][i]) + bodyAreas.append(clean[0]) + headsAreas.append("") + bodyAreas.append(clean[1]) + else: + headsAreas.append(blocs[0][i]) + bodyAreas.append(blocs[1][i]) + return (headsAreas, bodyAreas) + + def createTxtConfig(self, strHeader, dictVar): + """Cоздает область с заголовком + + создает текст конфигурационного файла в формате samba из + заголовка (строка) и словаря переменных + """ + if not strHeader: + return "" + outTxt = "[" + strHeader + "]\n" + for key in dictVar.keys(): + outTxt += "%s = %s\n" %(key,dictVar[key]) + return outTxt + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + listfinH = h.split("]") + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + headers.append(finH.replace("[","").replace("]","").strip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + +class compiz(samba): + """Класс для обработки конфигурационного файла типа compiz + + """ + _comment = "#" + configName = "compiz" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + def join(self, compizObj): + """Объединяем конфигурации""" + if isinstance(compizObj, compiz): + self.docObj.joinDoc(compizObj.doc) + self.postXML() + +class bind(objShare): + """Класс для обработки конфигурационного файла типа bind + + """ + _comment = "//" + configName = "bind" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*;[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]+%s|^%s|(?<=;)%s"%(_comment,_comment,_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # XML документ + self.doc = self.docObj.doc + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = text[:openBl.start()] + startBloc = text[openBl.start():openBl.end()] + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = first + start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + return areas + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header and i.start: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + else: + areaXML = rootNode + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header and f.start: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + if not f: + continue + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + areaXML.appendChild(fieldXML) + + if i.header and i.start: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + def textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def join(self, bindObj): + """Объединяем конфигурации""" + if isinstance(bindObj, bind): + self.docObj.joinDoc(bindObj.doc) + +class apache(bind): + """Класс для обработки конфигурационного файла типа apache + + """ + _comment = "#" + configName = "apache" + configVersion = "0.1" + __headerArea = "[^\<\> \t]+[ \t]+[^\<\> \t]+" + __openArea = "[ \t]*\<%s\>"%(__headerArea) + __closeArea = "[ \t]*\<\/[^\<\>]+\>" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + reHeader = re.compile(__headerArea) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки перед закрывающим тегом + # конфигурационного файла + xmlFields = xpath.Evaluate("child::fields", self.docObj.body) + if not (xmlFields and\ + self.docObj.getTypeField(xmlFields[-1]) == "br"): + self.docObj.body.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + self.docObj.getTypeField(xmlFields[-1]) == "br"): + xmlArea.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + + def join(self, apacheObj): + """Объединяем конфигурации""" + if isinstance(apacheObj, apache): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(apacheObj.doc) + self.postXML() + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment, reHeader): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = "" + startBloc = text[openBl.start():openBl.end()] + headBl = reHeader.search(startBloc) + if headBl: + firstBloc = headBl.group(0) + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields, reHeader): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment, reHeader) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields, reHeader) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) == 3: + valueList = nameValue[2:] + nameValue =["".join(nameValue[:2])," ".join(valueList)] + + if len(nameValue) > 3: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def textToXML(self): + """Преобразуем тект в XML""" + areas = [] + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields, + self.reHeader) + docObj = xmlDoc() + + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + +class postfix(apache): + """Класс для обработки конфигурационного файла типа postfix + + """ + _comment = "#" + configName = "postfix" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + def __init__(self,text): + self.text = text + # Объект документ + self.docObj = self.textToXML() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, postfixObj): + """Объединяем конфигурации""" + if isinstance(postfixObj, postfix): + self.docObj.joinDoc(postfixObj.doc) + + def textToXML(self): + """Преобразуем текст в XML""" + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + areas = [] + oneArea = area() + oneArea.header = "" + oneArea.start = "" + oneArea.fields = [self.text] + oneArea.end = "" + areas.append(oneArea) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def setDataField(self, txtLines, endtxtLines): + """Cоздаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + +class ldap(samba): + """Класс для обработки конфигурационного файла типа ldap + + """ + _comment = "#" + configName = "ldap" + configVersion = "0.1" + # Регулярное выражение для заголовка области + reHeader = re.compile("^[\t ]*(access|syncrepl)[^\n]+\n?") + # Регулярное выражения для области + reArea = re.compile("([\t ]*(access|syncrepl)[^\n]+\ +\n([\t ]+[^\n]+\n?)+)",re.M|re.S) + reComment = re.compile("\s*%s.*"%(_comment)) + # разделитель между переменной и значением переменной + reSeparator = re.compile("\s+|\s*=\s*") + # разделитель полей + sepFields = "\n" + # регулярное выражение для разделителя полей + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, ldapObj): + """Объединяем конфигурации""" + if isinstance(ldapObj, ldap): + self.docObj.joinDoc(ldapObj.doc) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[2:] + nameValue =[nameValue[0]+nameValue[1]," ".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findArea(self.text,self.reHeader,self.reArea) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + headers.append(h.rstrip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + header = self.removeSymbolTerm(h.strip()) + headerQuote = self.removeSymbolTerm(h) + docObj.createCaption(header,[headerQuote,""]) + areaAction = "drop" + elif h[0] == "-": + header = self.removeSymbolTerm(h.strip()) + headerQuote = self.removeSymbolTerm(h) + docObj.createCaption(header,[headerQuote,""]) + areaAction = "replace" + else: + docObj.createCaption(h.strip(), [h.rstrip(),""]) + else: + docObj.createCaption(h.strip(), [h.rstrip(),""]) + + if "\n" in blocs[0][z]: + resHead = self.reComment.search(h) + if resHead: + docObj.createField('comment', + blocs[0][z][resHead.start():]) + else: + docObj.createField('br') + + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + elif f.name[0] == "+": + # Добавляем уникальное поле + xmlField.setAttribute("type", "seplist") + docObj.setActionField(xmlField, "join") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + +class dovecot(bind): + """Класс для обработки конфигурационного файла типа dovecot + + """ + _comment = "#" + configName = "dovecot" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + + def __init__(self, text): + bind.__init__(self,text) + + def postXML(self, xmlArea=False): + """Последующая постобработка XML""" + # Добавляем перевод строки если его нет в конец области + if not xmlArea: + xmlArea = self.docObj.body + xmlFields = xpath.Evaluate("child::field", xmlArea) + if xmlFields and not (\ + self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment"): + xmlArea.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + xmlAreas = xpath.Evaluate("child::area", xmlArea) + for area in xmlAreas: + self.postXML(area) + + def join(self, dovecotObj): + """Объединяем конфигурации""" + if isinstance(dovecotObj, dovecot): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(dovecotObj.doc) + # Для добавления перевода строки перед закрывающим тегом + # конфигурационного файла + self.postXML() + + +class procmail(objShare): + """Класс для обработки конфигурационного файла типа procmail + + """ + _comment = "#" + configName = "procmail" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("=") + def __init__(self, text): + self.text = text + self.docObj = self.textToXML() + self.doc = self.docObj.doc + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def textToXML(self): + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + if self.text: + nodeBody = docObj.getNodeBody() + fields = self.splitToFields(self.text) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + nodeBody.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + nodeBody.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + nodeBody.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + nodeBody.appendChild(fieldXML) + return docObj + + def join(self, procmailObj): + """Объединяем конфигурации""" + if isinstance(procmailObj, procmail): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(procmailObj.doc) + +class kde(samba): + """Класс для обработки конфигурационного файла типа kde + + """ + _comment = "#" + configName = "kde" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("^\s*%s.*"%(_comment)) + reSeparator = re.compile("=") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + reH = re.compile("]\s*$") + #listfinH = h.split("]") + listfinH = reH.split(h) + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + head=finH.replace("][",".").replace("[","").replace("]","").strip() + headers.append(head) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + + def join(self, kdeObj): + """Объединяем конфигурации""" + if isinstance(kdeObj, kde): + self.docObj.joinDoc(kdeObj.doc) + self.postXML() + + +class xmlDocPlasma: + """Класс для замены метода joinArea в xmlDoc для plasma""" + # заменяемый метод для xmlDoc + def joinArea(self, baseNode, xmlNewArea): + """Объединяет область c областью Body (xmlNewArea c baseNode)""" + def appendArea(baseNode, xmlNewArea): + fieldsRemove = xpath.Evaluate(\ + "descendant::field[child::action='drop']", xmlNewArea) + for rmNode in fieldsRemove: + parentNode = rmNode.parentNode + parentNode.removeChild(rmNode) + captionAreasRemove = xpath.Evaluate(\ + "descendant::area/child::caption[child::action='drop']", + xmlNewArea) + for rmNodeCapt in captionAreasRemove: + rmNode = rmNodeCapt.parentNode + parentNode = rmNode.parentNode + parentNode.removeChild(rmNode) + self.setActionArea(xmlNewArea, "append") + # Добавляем разделитель областей во вложенные области + areaNodes = xpath.Evaluate('descendant::area',xmlNewArea) + for areaNode in areaNodes: + self.setActionArea(areaNode,"append") + parentNode = areaNode.parentNode + parentNode.insertBefore(self.sepAreas.cloneNode(True), + areaNode) + baseNode.appendChild(xmlNewArea) + # Добавляем разделитель областей + baseNode.insertBefore(self.sepAreas.cloneNode(True), xmlNewArea) + + nodesNames = xpath.Evaluate('child::area/caption/name',baseNode) + nodesNewArea = xpath.Evaluate('child::caption/name',xmlNewArea) + if not nodesNames: + # Добавляем область + if nodesNewArea: + newAreaAction = self.getActionArea(xmlNewArea) + if not (newAreaAction == "drop"): + appendArea(baseNode, xmlNewArea) + return True + if not nodesNames or not nodesNewArea: + return False + nameArea = "" + if nodesNewArea[0].firstChild: + nameArea = nodesNewArea[0].firstChild.nodeValue.strip() + flagFindArea = False + baseNodes = [] + for oName in nodesNames: + newAreaAction = self.getActionArea(xmlNewArea) + oArea = oName.parentNode.parentNode + oNameTxt = "" + if oName.firstChild: + oNameTxt = oName.firstChild.nodeValue + if nameArea == oNameTxt: + flagFindArea = True + # При использовании удаления + if newAreaAction == "drop": + prevNode = oName.parentNode.parentNode.previousSibling + removePrevNodes = [] + while (prevNode) and self.getTypeField(prevNode) == "br": + removePrevNodes.append(prevNode) + prevNode = prevNode.previousSibling + for removeNode in removePrevNodes: + baseNode.removeChild(removeNode) + baseNode.removeChild(oName.parentNode.parentNode) + continue + elif newAreaAction == "replace": + oldAreaNode = oName.parentNode.parentNode + newAreaCaption = xpath.Evaluate('child::caption', + xmlNewArea)[0] + oldAreaCaption = xpath.Evaluate('child::caption', + oldAreaNode)[0] + if newAreaCaption and oldAreaCaption: + xmlNewArea.replaceChild(oldAreaCaption,newAreaCaption) + self.setActionArea(xmlNewArea,"replace") + baseNode.replaceChild(xmlNewArea, + oldAreaNode) + continue + baseNodes.append(oName.parentNode.parentNode) + + # Заменяем QUOTE + oldAreaNode = oName.parentNode.parentNode + oldAreaQuote = xpath.Evaluate('child::caption/quote', + oldAreaNode)[0] + if oldAreaQuote and\ + not oldAreaQuote.firstChild: + newAreaQuote = xpath.Evaluate('child::caption/quote', + xmlNewArea)[0] + oldAreaCaption = xpath.Evaluate('child::caption', + oldAreaNode)[0] + if newAreaQuote and oldAreaCaption: + oldAreaCaption.replaceChild(newAreaQuote, oldAreaQuote) + + newFields = xpath.Evaluate('child::field',xmlNewArea) + + joinNewFields = xpath.Evaluate(\ + "child::field[child::action='join']" + ,xmlNewArea) + self.addNewFielsOldArea(newFields, joinNewFields, oArea) + + + if not flagFindArea: + # Добавляем область + if not (newAreaAction == "drop"): + appendArea(baseNode, xmlNewArea) + else: + tmpXmlNewAreas = xpath.Evaluate('child::area',xmlNewArea) + for na in tmpXmlNewAreas: + for bn in baseNodes: + self.joinArea(bn, na) + return True + +class plasma(samba): + """Класс для обработки конфигурационного файла типа kde + + """ + _comment = "#" + configName = "plasma" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("^\s*%s.*"%(_comment)) + reSeparator = re.compile("=") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + + def findPathArea(listPath, areaF): + """Ищет путь в области + + areaF - объект area + listPath - cписок названий областей + """ + ret = False + if not listPath: + return ret + flagList = False + if type(areaF) == types.ListType: + fields = areaF + flagList = True + else: + fields = areaF.fields + if areaF.header == listPath[0]: + ret = areaF + else: + return ret + for i in fields: + if str(i.__class__.__name__) == "area": + add = False + if not flagList: + add = listPath.pop(0) + if not listPath: + break + ret = False + if i.header == listPath[0]: + ret = findPathArea(listPath, i) + break + else: + if add: + listPath.insert(0,add) + if ret == areaF and len(listPath)>1: + ret = False + return ret + + + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + reH = re.compile("\[([^\[\]]+)\]") + # Список имен блоков + namesBlockList = [] + # Временные поля + fieldsTmp = [] + # Добавляем заголовки + z = 0 + for h in blocs[0]: + if not h: + if blocs[1][z] == "": + fieldsTmp.append("") + #fieldsTmp.append("\n") + else: + fieldsTmp.append(blocs[1][z]) + #print '"' + blocs[1][z] + '"' + z += 1 + continue + #print '"' + blocs[1][z] + '"' + z += 1 + slpNamesBlock = reH.split(h) + # Отступ слева для заголовка + indentionLeft = slpNamesBlock[0] + namesBlock = filter(lambda x: x.strip(), slpNamesBlock) + #namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock) + findArea = findPathArea(copy.copy(namesBlock), areas) + namesBlockList.append(namesBlock) + if findArea: + if len(namesBlock) > 1: + namesBlockView = map(lambda x: self.removeSymbolTerm(x), + namesBlock) + else: + namesBlockView = namesBlock + findArea.start = indentionLeft + "[" + \ + "][".join(namesBlockView) + "]" + else: + i = 0 + lenNamesBlock = len(namesBlock) + namesBlockTmp = [] + for nameB in namesBlock: + namesBlockTmp.append(nameB) + findArea = findPathArea(copy.copy(namesBlockTmp), areas) + i += 1 + if not findArea: + areaNew = area() + areaNew.header = nameB + if lenNamesBlock == i: + if len(namesBlock) > 1: + namesBlockView = map(\ + lambda x: self.removeSymbolTerm(x), + namesBlock) + else: + namesBlockView = namesBlock + areaNew.start = indentionLeft + "[" + \ + "][".join(namesBlockView) + "]" + else: + areaNew.start = "" + areaNew.end = "" + if i == 1: + if lenNamesBlock == i: + areas += fieldsTmp + areas.append(areaNew) + findAreaPrev = areas[-1] + else: + if lenNamesBlock == i: + findAreaPrev.fields += fieldsTmp + findAreaPrev.fields.append(areaNew) + findAreaPrev = findAreaPrev.fields[-1] + else: + findAreaPrev = findArea + fieldsTmp = [] + i = 0 + delt = 0 + # Добавляем тела + for body in blocs[1]: + #print "#" + body + "#" + #print + if not blocs[0][i]: + i += 1 + delt +=1 + continue + ## В случае последнего комментария не добавляем перевод строки + #if self.reComment.search(body.splitlines()[-1]): + body = "\n" + body + + namesBlock = namesBlockList[i-delt] + findArea = findPathArea(copy.copy(namesBlock), areas) + if findArea: + #if findArea.fields: + #if type(findArea.fields[0]) == types.StringType: + #findArea.fields.pop(0) + findArea.fields.insert(0, body) + i += 1 + + #eee = 1 + #def prAreas(ar, eee): + #for a in ar: + #if type(a) == types.StringType: + #print 'field', a + #else: + #print "--------------------" + #print "HEADER =", a.header + #print "START =", a.start + #print "FIELDS =", a.fields + #print "LEVEL", eee + #if type(a) != types.StringType: + #if a.fields: + #eee += 1 + #prAreas(a.fields, eee) + + #prAreas(areas, eee) + return areas + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + if not f: + continue + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + if areaXML: + areaXML.appendChild(fieldXML) + + if i.header: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + if not i: + continue + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + + def createTxtConfig(self, strHeader, dictVar): + """Cоздает область с заголовком + + создает текст конфигурационного файла в формате samba из + заголовка (строка) и словаря переменных + """ + if not strHeader: + return "" + if type(strHeader) in (tuple, list): + outTxt = "".join(map(lambda x: "["+x+"]",strHeader)) + if not outTxt: + return "" + outTxt += "\n" + else: + outTxt = "[" + strHeader + "]\n" + for key in dictVar.keys(): + outTxt += "%s=%s\n" %(key,dictVar[key]) + return outTxt + + def _textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas) + #docObj = xmlDoc() + # Создаем новый класс xmlDoc с измененным методом joinArea + newClass = type("newXmlDocPlalma",(xmlDocPlasma,xmlDoc,object),{}) + # Создаем экземпляр нового класса + docObj = newClass() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + def getQuotesArea(xmlArea): + quotes = [] + xmlQuotes = xpath.Evaluate('child::caption/quote',xmlArea) + for node in xmlQuotes: + if node.firstChild: + quotes.append(node.firstChild.nodeValue) + if len(quotes) == 0: + quotes.append("") + quotes.append("") + elif len(quotes) == 1: + quotes.append("") + return quotes + + xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body) + #print "-------------------------------------------------------" + #print xmlAreas + #if xmlAreas: + #prXmlArea = xmlAreas[0] + for xmlArea in xmlAreas: + # Перед пустой областью и после нее удаляем переводы строк + if getQuotesArea(xmlArea) == ["",""]: + #areaTXT = xpath.Evaluate("child::caption/name", xmlArea)[0] + #print "CL_AREA", areaTXT.firstChild + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + parentNode = xmlArea.previousSibling.parentNode + if xmlArea.previousSibling.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling.previousSibling) == "br": + parentNode.removeChild(\ + xmlArea.previousSibling.previousSibling) + parentNode.removeChild(xmlArea.previousSibling) + if xmlArea.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling) == "br": + parentNode = xmlArea.nextSibling.parentNode + if xmlArea.nextSibling.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling.nextSibling) == "br": + parentNode.removeChild(xmlArea.nextSibling.nextSibling) + parentNode.removeChild(xmlArea.nextSibling) + continue + + # Собираем поля в кучку + xmlChildAreas = xpath.Evaluate("child::area", xmlArea) + if xmlChildAreas: + childNodes = self.docObj.getFieldsArea(xmlArea) + firstChildArea = xmlChildAreas[0] + if firstChildArea.previousSibling and\ + self.docObj.getTypeField(firstChildArea.previousSibling)=="br": + if firstChildArea.previousSibling.previousSibling: + if self.docObj.getTypeField(\ + firstChildArea.previousSibling.previousSibling)=="br": + firstChildArea = firstChildArea.previousSibling + flagFoundArea = False + it = 0 + lenChild = len(childNodes) + for node in childNodes: + it += 1 + if node.tagName == "area": + flagFoundArea = True + continue + if flagFoundArea and node.tagName == "field": + if self.docObj.getTypeField(node) == "var": + xmlArea.insertBefore(node, firstChildArea) + if it < lenChild: + if self.docObj.getTypeField(childNodes[it])==\ + "br": + xmlArea.insertBefore(childNodes[it], + firstChildArea) + # Добавление перевода строк в если его нет между полями + if self.docObj.getTypeField(node) == "var" and\ + node.previousSibling and\ + not (self.docObj.getTypeField(node.previousSibling) in\ + ("br","comment")): + xmlArea.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + node) + + # Добавляем BR если его нет первым полем + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[0]) == "br" or\ + self.docObj.getTypeField(xmlFields[0]) == "comment")): + if xmlFields: + xmlArea.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlFields[0]) + # Если последним полем BR, удаляем его + if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br": + #print "DEL_BR", xmlFields[-1].nextSibling + #and\ + if not xmlFields[-1].nextSibling: + xmlArea.removeChild(xmlFields[-1]) + + # Если предыдущим полем не (BR или комментарий) - добавляем BR + if xmlArea.previousSibling and\ + not (self.docObj.getTypeField(xmlArea.previousSibling) == "br" or\ + self.docObj.getTypeField(xmlArea.previousSibling) == "comment"): + parentNode = xmlArea.parentNode + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlArea) + # Если есть предыдущее поле, и поле предыдущеее предыдущему + # не равно BR или комментарий то добавляем BR + if xmlArea.previousSibling: + prPrSibling = xmlArea.previousSibling.previousSibling + if prPrSibling and\ + not (self.docObj.getTypeField(prPrSibling) == "br" or\ + self.docObj.getTypeField(prPrSibling) == "comment"): + parentNode = xmlArea.parentNode + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlArea) + # Если после есть BR а за ним ничего нет, удаляем BR + if xmlArea.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling) == "br": + if not xmlArea.nextSibling.nextSibling: + parentNode = xmlArea.nextSibling.parentNode + parentNode.removeChild(xmlArea.nextSibling) + + + def join(self, kdeObj): + """Объединяем конфигурации""" + if isinstance(kdeObj, plasma): + self.docObj.joinDoc(kdeObj.doc) + self.postXML() + +class xml_xfce(_error): + """Класс для объединения xfce-xml файлов""" + # root нода + rootNode = False + # body нода + bodyNode = False + # Документ + doc = False + # Текст профиля + text = "" + # Комментарий + _comment = ("") + + def __init__(self, text): + self.text = text + # Создаем пустой объект + self.docObj = type("_empty_class", (object,), {})() + # Названия аттрибутов для пустого объекта + emptyMethods = ["getNodeBody","removeComment","insertBRtoBody", + "insertBeforeSepAreas"] + # Добавляем необходимые аттрибуты пустому объекту + for method in emptyMethods: + setattr(self.docObj, method, self.emptyMethod) + # Создаем XML документ + self.doc = self.textToXML() + + def emptyMethod(self, *arg , **argv): + """Пустой метод""" + return True + + def setNameBodyNode(self, name): + """Устанавливает название для корневой ноды документа""" + if not self.bodyNode: + return False + self.bodyNode.setAttribute("name", name) + return True + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = ''' + +''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text profile is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def join(self, xml_xfceObj): + """Объединяем конфигурации""" + if isinstance(xml_xfceObj, xml_xfce): + try: + self.joinDoc(xml_xfceObj.doc) + except: + self.setError(_("Can not join profile")) + return False + return True + + def _removeDropNodesAndAttrAction(self, xmlNode): + """Удаляет ноды с аттрибутом action='drop' + + Также удаляет аттрибут action у всех нод + """ + flagError = False + childNodes = xmlNode.childNodes + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + if xmlNode.hasAttribute("action"): + nAction = xmlNode.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML profile, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + xmlNode.removeAttribute("action") + if nAction == "drop": + parentNode = xmlNode.parentNode + if parentNode: + parentNode.removeChild(xmlNode) + if childNodes: + for node in childNodes: + if not self._removeDropNodesAndAttrAction(node): + flagError = True + break + if flagError: + return False + return True + + def postXML(self): + """Последующая постобработка XML""" + # Удаляем теги action и удаляемые ноды + self._removeDropNodesAndAttrAction(self.bodyNode) + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True): + """Объединение корневой ноды профиля и корневой ноды файла""" + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + n = xmlNode + path = u'' + nName = u'' + nType = u'' + nValue = u'' + nAction = u'' + attrName = '' + attrType = '' + path = n.tagName + if n.hasAttribute("name"): + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if n.hasAttribute("type"): + nType = n.getAttribute("type") + attrType = u"attribute::type='%s'"%nType + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML profile, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + strAttr = [attrName, attrType] + findAttr = filter(lambda x: x, strAttr) + findAttrStr = '' + if findAttr: + strAttr = u' and '.join(findAttr) + findAttrStr = "[%s]"%strAttr + findPath = u"child::%s%s"%(path,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + #print findPath + #print workNode + #print "----------------------------" + # Новая нода список + flagArray = False + if nType == "array": + flagArray = True + flagDrop = False + flagJoin = True + flagReplace = False + if nAction == "replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if oldNodes: + if len(oldNodes)>1: + textError = _("The uncertainty in this profile are \ +the same nodes at one level") + self.setError(textError) + return False + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + if not self._join(node, nextOldNode, False): + flagError = True + break + if flagError: + return False + return True + + def joinDoc(self, doc): + """Объединение документа профиля и документа файла""" + if not self.doc: + self.setError(_("Can not text file is XML")) + return False + if not doc: + self.setError(_("Can not text profile is XML")) + return False + # Импортируем корневую ноду нового документа в текущий документ + #newImportBodyNode = self.doc.importNode(doc.documentElement, True) + # Объединение корневой ноды профиля и корневой ноды файла + if not self._join(doc.documentElement, self.bodyNode): + return False + return True + + def getConfig(self): + """Получение текстового файла из XML документа""" + data = self.doc.toprettyxml(encoding='UTF-8').split("\n") + data = filter(lambda x: x.strip(), data) + return "\n".join(data).replace("\t"," ").decode("UTF-8") + +class squid(procmail): + """Класс для обработки конфигурационного файла типа squid + + """ + configName = "squid" + configVersion = "0.1" + # разделитель названия и значения переменной + reSeparator = re.compile(" ") + + def __init__(self, text): + procmail.__init__(self, text) + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + flagVariable = True + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + flagVariable = False + elif findComment: + if textLine[:findComment.start()].strip(): + field.comment = textLine[findComment.start():] + textLine = textLine[:findComment.start()] + else: + field.comment = textLine + flagVariable = False + fields.append(field) + field = fieldData() + if flagVariable: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def join(self, squidObj): + """Объединяем конфигурации""" + if isinstance(squidObj, squid): + #print squidObj.getConfig() + self.docObj.joinDoc(squidObj.doc) + +class xml_xfcepanel(xml_xfce): + """Класс для объединения xfce-panel файлов""" + def __init__(self, text): + xml_xfce.__init__(self, text) + self.panelNumbers = {} + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = ''' + + +''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text profile is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def setNameBodyNode(self, name): + """Пустой метод""" + return True + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0): + """Объединение корневой ноды профиля и корневой ноды файла""" + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + n = xmlNode + path = u'' + nName = u'' + flagArray = False + nValue = u'' + nAction = u'' + attrName = '' + attrType = '' + path = n.tagName + if path == "items": + flagArray = True + if not flagArray: + if n.hasAttribute("name"): + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML profile, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findAttrStr = "" + if attrName: + findAttrStr = "[%s]"%attrName + findPath = u"child::%s%s"%(path,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + flagDrop = False + flagJoin = True + flagReplace = False + flagAppend = False + if nAction == "replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if path == "panel": + flagJoin = False + if levelNumber in self.panelNumbers.keys(): + self.panelNumbers[levelNumber] += 1 + else: + self.panelNumbers[levelNumber] = 0 + if oldNodes: + if len(oldNodes)>1 and path != "panel": + textError = _("The uncertainty in this profile are \ +the same nodes at one level") + self.setError(textError) + return False + if path == "panel": + if len(oldNodes)<=self.panelNumbers[levelNumber]: + nextOldNode = oldNodes[-1] + # Добавляем ноду + if not flagDrop: + flagAppend = True + flagReplace = False + childNodes = False + else: + nextOldNode=oldNodes[self.panelNumbers[levelNumber]] + else: + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + flagAppend = True + flagDrop = False + if flagAppend and not flagDrop: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + levelNumber +=1 + if not self._join(node, nextOldNode, False, levelNumber): + flagError = True + break + levelNumber -= 1 + if flagError: + return False + return True + + def join(self, xml_xfceObj): + """Объединяем конфигурации""" + if isinstance(xml_xfceObj, xml_xfcepanel): + try: + self.joinDoc(xml_xfceObj.doc) + except: + self.setError(_("Can not join profile")) + return False + return True + + +class dhcp(bind): + """Класс для обработки конфигурационного файла типа dhcp + + """ + _comment = "#" + configName = "dhcp" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("^[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + bind.__init__(self,text) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = textLine.replace(self.sepFields,"").strip() + field.value = "" + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + nameCheck = nameValue[0] + if nameValue[0][:1] in ["+","-","!"]: + nameCheck = nameValue[0][1:] + if nameCheck == "option" or nameCheck == "hardware" or\ + nameCheck == "set": + valueList = nameValue[2:] + nameValue =[nameValue[0]+nameValue[1], + " ".join(valueList).replace(\ + self.sepFields,"")] + else: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def join(self, dhcpObj): + """Объединяем конфигурации""" + if isinstance(dhcpObj, dhcp): + self.docObj.joinDoc(dhcpObj.doc) + + +class xml_gconf(xml_xfce): + """Класс для объединения gconf-xml файлов""" + # root нода + rootNode = False + # body нода + bodyNode = False + # Документ + doc = False + # Текст профиля + text = "" + # Текущее время в секундах + currentTime = "" + # Комментарий + _comment = ("") + # поддерживаемые аттрибуты тега entry. Пример + supportEntryTypes = ("int", "bool", "float", "string", "list", "pair") + + def __init__(self, text): + self.text = text + # Создаем пустой объект + self.docObj = type("_empty_class", (object,), {})() + # Названия аттрибутов для пустого объекта + emptyMethods = ["getNodeBody","removeComment","insertBRtoBody", + "insertBeforeSepAreas"] + # Добавляем необходимые аттрибуты пустому объекту + for method in emptyMethods: + setattr(self.docObj, method, self.emptyMethod) + # Пустой метод (не нужно имя файла для корневой ноды) + setattr(self, "setNameBodyNode", self.emptyMethod) + # Создаем XML документ + self.doc = self.textToXML() + + def getCurrentTime(self): + """Получение текущего времени в секундах""" + return str(int(time.time())) + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = '''''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text profile is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def cmpListsNodesEntry(self, listXmlA, listXmlB): + """Сравнение содержимого двух списков XML нод""" + getTextsNodes = lambda y: map(lambda x:\ + x.toxml().replace(" ","").replace("\t","").replace("\n",""), + map(lambda x: x.hasAttribute("mtime") and\ + x.removeAttribute("mtime") or x, + map(lambda x: x.cloneNode(True), + filter(lambda x: x.nodeType==x.ELEMENT_NODE, y)))) + if set(getTextsNodes(listXmlA))==set(getTextsNodes(listXmlB)): + return True + return False + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0): + """Объединение корневой ноды профиля и корневой ноды файла""" + if levelNumber>1: + return True + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType == xmlNode.ELEMENT_NODE: + n = xmlNode + tagName = n.tagName + nAction = u'' + nName = u'' + nType = u'' + nValue = u'' + nSchema = u'' + attrName = '' + if flagRootNode: + if not tagName == "gconf": + self.setError(_("The text is not a valid gconf-XML format \ +(not found '...')")) + return False + flagType = False + flagValue = False + flagSchema = False + else: + if not tagName == "entry": + self.setError(_("The text is not a valid gconf-XML format \ +(found '<%s>..'")%(tagName,tagName)) + return False + if not n.hasAttribute("name"): + self.setError(_('Not found arrtibute "name" in tag entry')) + return False + flagType = n.hasAttribute("type") + flagValue = False + flagSchema = n.hasAttribute("schema") + if flagSchema: + nSchema = n.getAttribute("schema") + if not flagType and not flagSchema: + self.setError(_('Not found arrtibute "type" in tag entry')) + return False + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if flagType: + flagValue = n.hasAttribute("value") + nType = n.getAttribute("type") + # Проверка правильности аттрибута type + if not nType in self.supportEntryTypes: + self.setError(\ + _('Incorrect arrtibute "type" - ')\ + %nType) + return False + if flagValue: + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML profile, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findAttrStr = "" + if attrName: + findAttrStr = "[%s]"%attrName + findPath = u"child::%s%s"%(tagName,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + # По умолчанию - объединение + flagJoin = True + flagReplace = False + flagDrop = False + # Замещаем ноду + if nType=="string" or nAction=="replace": + flagJoin = False + flagReplace = True + # Замещаем ноду в случае массива + elif nType == "list" or nType == "pair": + flagJoin = False + flagReplace = True + # Удаляем ноду + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if oldNodes: + if len(oldNodes)>1: + textError = _("The uncertainty in this profile are \ +the same nodes at one level") + self.setError(textError) + return False + nextOldNode = oldNodes[0] + # Объединение нод + if flagJoin: + if flagType and flagValue: + flagChange = False + foundValue = nextOldNode.hasAttribute("value") + if foundValue: + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + flagChange = True + else: + flagChange = True + if flagChange: + nextOldNode.setAttribute("mtime", + self.currentTime) + nextOldNode.setAttribute("value",nValue) + elif flagSchema: + flagChange = False + foundValue = nextOldNode.hasAttribute("schema") + if foundValue: + oSchema = nextOldNode.getAttribute("schema") + if nSchema != oSchema: + flagChange = True + else: + flagChange = True + if flagChange: + nextOldNode.setAttribute("mtime", + self.currentTime) + nextOldNode.setAttribute("schema",nSchema) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + # Сравнение содержимого нод + if not self.cmpListsNodesEntry([replaceXmlNode], + [nextOldNode]): + replaceXmlNode.setAttribute("mtime", + self.currentTime) + if not\ + self._removeDropNodesAndAttrAction(\ + replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + appendXmlNode.setAttribute("mtime", self.currentTime) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + levelNumber +=1 + if not self._join(node, nextOldNode, False, levelNumber): + flagError = True + break + levelNumber -= 1 + if flagError: + return False + return True + + def join(self, xml_gconfObj): + """Объединяем конфигурации""" + # Получаем текущее время + self.currentTime = self.getCurrentTime() + if isinstance(xml_gconfObj, xml_gconf): + try: + self.joinDoc(xml_gconfObj.doc) + except: + self.setError(_("Can not join profile")) + return False + return True + + def getConfig(self): + """Получение текстового файла из XML документа""" + def expand_start_tabs(s): + q = re.findall("^(\t+)(.*)$",s) + # if q: + return "".join((q[0][0].expand_start_tabs(),q[0][1])) + #else: + # return s + data = self.doc.toprettyxml().split("\n") + data = map(lambda x: expand_start_tabs(x), + filter(lambda x: x.strip(), data)) + dataOut = [] + z = 0 + lenData = len(data) + lenM2 = lenData - 2 + for i in xrange(lenData): + if z>0: + z -= 1 + continue + if i < lenM2 and data[i].endswith(">") and not "<" in data[i+1]: + dataOut.append(data[i] + data[i+1].strip() + data[i+2].strip()) + z = 2 + continue + dataOut.append(data[i]) + return "\n".join(dataOut) diff --git a/build/lib/calculate-lib/pym/cl_utils.py b/build/lib/calculate-lib/pym/cl_utils.py new file mode 100644 index 0000000..195d4d5 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_utils.py @@ -0,0 +1,510 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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 filecmp +import string +from random import choice +from re import search, compile, S +import os +import types +import subprocess + +def getdirlist(s_path): + #Получить список директорий по указаному пути + fdir=filecmp.dircmp(s_path, s_path) + dir_list=fdir.common_dirs + return dir_list + +def prettyColumnStr(*cols): + '''Функция преобразования строк в текстовые колонки. Если указанный текст + не помещается в колонку, то строка переносится на следующую этой же колонки + перенос текста идет по словам, и текст выравнивается по ширине колонки за + счет дополнительных пробелов между словами. Если в строке используется + перенос строки, то текст переносится не просто на следующую строку, а также + на следующую строку колонки, причем если используется \r текст выравнива- + ется по ширине, а если \n, то просто перевод строки. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Возвращаемые параметры: + строка, которую можно использовать для вывода на экран + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + # шаблон поиска переводов строк + wherenr = compile( '[\n\r]', S ) + retstr = "" + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + noconvert = False + space = u' ' + nospace = u'' + for i in xrange(0,len(cols),2): + cols[i] = _toUNICODE(cols[i]) + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + retstr += cols[q] + " " + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + partstr = cols[q][:cols[q+1]] + # искать перевод строки с полученной части + brfind = wherenr.search(partstr) + # если это не последняя колонка + if q + 2 < len(cols): + # добавить разделитель между колонками + cellspacing = space + else: + # разделитель не нужен + cellspacing = nospace + + # если перевод строки найден, то + if brfind != None: + # для текущего вывода в колонку + # берем часть строки до перевода + partstr = partstr[:brfind.start()] + # остальная часть идет в остаток (без перевода) + cols[q] = cols[q][brfind.start()+1:] +# # если используется перевод каретки +# if brfind.group() == '\r': +# # то выравниваем по ширине колонки +# partstr = partstr.ljust(cols[q+1], ' ') +# else: +# # добавить отступы чтобы закончить колонку + partstr = partstr.ljust(cols[q+1], ' ') + # если взята часть строки + elif len(partstr) == cols[q+1] and partstr != cols[q]: + # если взята часть строки (разрыв в слове) + if cols[q][cols[q+1]] != ' ': + # ищем ближайший пробел справа + spacepos = partstr.rfind(' ') + # если пробел найти не удалось + if spacepos == -1: + # то на вывод идет часть строки равной ширине + cols[q] = cols[q][cols[q+1]:] + # если пробел найден + else: + # обрезаем строку до найденного пробела + partstr = partstr[:spacepos] + cols[q] = cols[q][spacepos+1:] + # если взята часть строки (разрыв на пробеле) + else: + # ислючить переносной пробел + cols[q] = cols[q][cols[q+1]+1:] + # выровнить текст по ширине колонки + partstr = partstr.ljust(cols[q+1], ' ') + #partstr = justify(partstr, cols[q+1]) + # остатки строки + else: + # добавить отступы чтобы закончить колонку + partstr = partstr.ljust(cols[q+1], ' ') + cols[q] = '' + + retstr+= partstr + cellspacing + + # остальную часть строки оставить на следующую итерацию + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + retstr += "\n" + return retstr.encode('utf8') + +def columnStr(*cols): + '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную + колонку, то они переносятся на следующую строку в нужную колонку. В строку. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Возвращаемые параметры: + строка, которую можно использовать для вывода на экран + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + retstr = "" + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + for i in xrange(0,len(cols),2): + cols[i] = (str(cols[i])).decode('utf8') + + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + retstr += cols[q] + " " + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \ + + " " + # остальную часть строки оставить на следующую итерацию + cols[q] = cols[q][cols[q+1]:] + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + retstr += "\n" + return retstr + +def columnWrite(*cols): + '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную + колонку, то они переносятся на следующую строку в нужную колонку. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + for i in xrange(0,len(cols),2): + cols[i] = (str(cols[i])).decode('utf8') + + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + print cols[q].encode('utf8'), + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + print (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8'), + # остальную часть строки оставить на следующую итерацию + cols[q] = cols[q][cols[q+1]:] + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + print + +def justify(s,width): + '''Выровнить текст по ширине + + Параметры: + s выводимая строка + width ширина на которую надо выровнить строку + + Возвращаямые параметры: + Выровненная строка + ''' + # если подана строка без пробелов - прекратить обработку + if s.find(' ') == -1: + return s + pos = 0 + # переводим в юникод для правильного вычисления длины + try: + s = s.decode( 'utf-8' ) + # пропуск если это не utf-8 + except UnicodeEncodeError: + pass + # пока длина строки меньше указанной + while len(s) < width: + # находим очередной пробел + pos = s.find( ' ', pos ) + # если не найден искать сначала + if pos == -1: + pos = s.find(' ') + # вставить в позицию еще один пробел + s = s[:pos] +' ' +s[pos:] + # оставить удвоенный пробел + pos += 3 + # вернуть строку в utf8 если она пришла в utf8 + return s.encode('utf-8') + +def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None): + """Выполняет внешнюю программу + + Параметры: + cmd внешняя программа + inStr данные передаваемые программе на страндартный вход. + ret_first вернуть только первую строку + env_dict словарь переменных окружения + Возвращаемые параметры: + строка/строки которую выведет внешняя программа + Возвращает код возврата, stdout+stderr + """ + pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env_dict, + close_fds=True, + shell=True) + fout, fin, ferr = (pipe.stdout, pipe.stdin, pipe.stderr) + # если есть данные на вход, передать их + if inStr: + fin.write(inStr) + fin.close() + # Код возврата + retcode = pipe.wait() + res = fout.readlines() + fout.close() + res += ferr.readlines() + ferr.close() + if res: + if len(res) == 1 or ret_first: + return retcode, res[0].strip() + else: + return retcode, res + return retcode, None + + +def genpassword(passlen=9): + '''Вернуть случайный пассворд указанной длины + + Параметры: + passlen длина пароля который нужно сгенерировать + + Возвращаемые параметры: + Сгенерированный пароль указанной длины + ''' + res=''.join([choice(string.ascii_letters+string.digits)\ + for i in xrange(passlen)]) + return res + +def fillstr(char, width): + '''Заполнить строку указанным числом символов. Псеводоним символ*кол-во''' + return str(char) * width + + #вернуть пути для запуска утилит +def getpathenv(): + bindir=['/sbin','/bin','/usr/sbin','/usr/bin'] + env=os.environ + if env and env.has_key('PATH'): + lpath=env['PATH'].split(":") + npath=[] + for dirname in bindir: + if os.path.exists(dirname) and dirname not in lpath: + npath.append(dirname) + lpath=npath+lpath + return ":".join(lpath) + +#класс для работы с установленными пакетами +class pakages: + #путь к директории установленнх пакетов + pkgdir="/var/db/pkg/" + #список установленных пакетов + pkglist={} + #Объект содержащий параметры пакета + class pakage(object): + #имя пакета с версией + fullname="" + #имя пакета + name="" + #версия + ver="" + #тип пакета в портежах + portdir="" + def __init__(self, **args): + for atname,atvalue in args.items(): + setattr(self,atname, atvalue) + + def __init__(self): + self.pkglist=self.__getpkglist() + + #разбить имя пакета на тип и имя + def __getpkgver(self, fname): + res=search('^(.+)\-([0-9]+.*)$',fname) + if res: + return res.groups() + else: + return (None,None) + + #собрать установленные в системе пакеты + def __getpkglist(self): + portageDirs=[] + instaledPkg={} + #проверим на существование директории с установленными пакетами + if os.path.exists(self.pkgdir): + #получим список типов пакетов + portageDirs=getdirlist(self.pkgdir) + if len(portageDirs)>0: + #обрабатываем содержимое каждого из типов + for portageDir in portageDirs: + pkgList=getdirlist(self.pkgdir+portageDir) + for pkg in pkgList: + fullname=pkg + pkgName,pkgVer= self.__getpkgver(pkg) + pobj=self.pakage(fullname=fullname, + name=pkgName, \ + ver=pkgVer,\ + portdir=portageDir) + fpkg=portageDir+"/"+pkgName + if instaledPkg.has_key(fpkg): + instaledPkg[fpkg].append(pobj) + else: + instaledPkg[fpkg]=[pobj] + return instaledPkg + + #разбить pkgname на составляющие имени пакета + def __partname(self, pkgname): + if not pkgname.strip(): + return False + res=search('^(.+\/)?(.+)',pkgname) + tname=None + if res.group(1): + tname=res.group(1) + if res.group(2): + res2=search('^(.+)(\-[0-9]+.+$)',res.group(2)) + if res2: + name=res2.group(1) + ver=res2.group(2) + else: + name=res.group(2) + ver=None + if res: + if name and name[-1:]=='-': + name=name[:-1] + if tname and tname[-1:]=='/': + tname=tname[:-1] + if ver and ver[0]=='-': + ver=ver[1:] + return [tname, name, ver] + + + #проверить установленн ли пакет + #isinstalled('dev-db/postgresql') + def isinstalled(self, pkgname): + res=self.getinstpkg(pkgname) + if len(res)>0: + return True + else: + return False + + #вернуть список объектов pakage() соответствующих pkgname + #getinstpkg('dev-db/postgresql') + #в случае отсутствия пакетов возвращает пустой список + def getinstpkg(self, pkgname): + pinfo=self.__partname(pkgname) + if pinfo: + ret=[] + if pinfo[0] and pinfo[1] and pinfo[2]: + if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]): + return [] + fpkg=self.pkglist[pinfo[0]+'/'+pinfo[1]] + ret=[] + for i in fpkg: + if i.ver==pinfo[2]: + ret.append(i) + return ret + elif pinfo[0] and pinfo[1]: + if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]): + return [] + return self.pkglist[pinfo[0]+'/'+pinfo[1]] + elif pinfo[1] and pinfo[2]: + for i in self.pkglist.keys(): + if search('^.+\/%s$'%(pinfo[1]),i): + for el in self.pkglist[i]: + if el.ver==pinfo[2]: + ret.append(el) + return ret + elif pinfo[1]: + for i in self.pkglist.keys(): + if search('^.+\/%s$'%(pinfo[1]),i): + ret+=self.pkglist[i] + return ret + return [] + + def getListPkg(self): + return self.pkglist + +def list2str(list): + '''Функция переводит список в строку''' + return '['+','.join(list)+']' + +def str2list(s): + '''Функция переводит строку в список''' + return s[1:-1].split(',') + +def dict2str(dict): + '''Функция перводит словарь в строку''' + return '{'+','.join(["%s:%s" % (str(k),str(v)) \ + for (k,v) in dict.items()])+'}' #: + +def str2dict(s): + '''Функция переводит строку в словарь''' + dict = {} + for i in s[1:-1].split(','): + k,v = i.split(':') + dict[k] = v + return dict + +def convertStrListDict(val): + '''Функция определеяется что на входе (строка, список, словарь) + и переводит их в строку и обратно''' + # если подан список + if type(val) == types.ListType: + return list2str(val) + # если подан словарь + elif type(val) == types.DictType: + return dict2str(val) + # если подана строка + else: + # если поданная строка содержит словарь + if ':' in val and '{' in val: + return str2dict(val) + # если поданная строка содержит список + elif ',' in val and '[' in val: + return str2list(val) + # если это просто строка + else: + return val + +def _toUNICODE(val): + """перевод текста в юникод""" + if type(val) == types.UnicodeType: + return val + else: + return str(val).decode('UTF-8') diff --git a/build/lib/calculate-lib/pym/cl_utils2.py b/build/lib/calculate-lib/pym/cl_utils2.py new file mode 100644 index 0000000..330e7dd --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_utils2.py @@ -0,0 +1,563 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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 +import struct +import termios +import fcntl +import cl_utils + +import cl_profile +import ldap +class ldapFun(cl_profile._error): + '''Объект для работы с LDAP сервером + + подключение к серверу и поиск данных + ''' + def __init__(self, dnUser, password, host="localhost"): + self.conLdap = False + # Получаем соединение с LDAP + try: + self.conLdap = self.__ldapConnect(dnUser, password, host) + except ldap.LDAPError, e: + self.setError(e[0]['desc']) + + def __ldapConnect(self, dnUser, password, host): + """Соединение с LDAP сервером""" + conLdap = ldap.initialize('ldap://%s'%host) + conLdap.simple_bind_s(dnUser, password) + return conLdap + + def ldapSearch(self,baseDN, searchScope, searchFilter, retrieveAttributes): + try: + ldap_result_id = self.conLdap.search(baseDN, searchScope, + searchFilter, + retrieveAttributes) + result_set = [] + while 1: + result_type, result_data = self.conLdap.result(ldap_result_id, + 0) + if (result_data == []): + break + else: + if result_type == ldap.RES_SEARCH_ENTRY: + result_set.append(result_data) + except ldap.NO_SUCH_OBJECT: + return [] + except: + return False + return result_set + +pcs = cl_utils.prettyColumnStr + +class cl_help: + """Объект для работы со справкой, и обработкой параметров. + + Конструктор __init__ должен определить следующие переменные: + + self.chapter список разделов справки, каждый элементы состоит из + имени раздела, флаг видимый/скрытый, кол-во переводов строк + после названия раздела, количество строк после раздела, + тип раздела + Пример: [("Copyright",False,0,2),"options"] + self.relService словарь связей сервисов и действующих опций + ключ - название сервиса, значение - список отображаемых + разделов отмеченных как "options" + Пример: {"samba":[_("Common options"), + _("Service Samba options")]} + self.relOptions словарь связей длинных опций помощи и выводимых разделов + помощи с опциями + ключ - параметр справки, значение список отображаемых + разделов справки + Пример: {"help-ldap":[_("Common options"), + _("Service LDAP options)]} + self.progName словарь имена используемых программ и их номера для + доступа к переменным + Пример: {'cl-groupadd':0, 'cl-groupdel':1} + self.data список данных для справки, каждый элемент словарь: + progAccess: список номеров программ отображающих + Пример: {'progAccess':(0,), + 'shortOption':"g", + 'longOption':"gid", + 'optVal':"GID", + 'helpChapter':_("Options"), + 'help':_("use GID for the new group") + }, + после заполнения параметров необходимо выполнить + self._cl_help__setParamHelp() для заполнения справки + + """ + def __init__(self, cmdName): + # ширина консоли взята за 80 + # -1 чтобы компенсировать расстрояние между колонками + self.consolewidth = 79 + self.column_width = 32 + self.cmdName = cmdName + #короткие опции командной строки + self.shortOpt = [] + #длинные опции командной строки + self.longOpt = [] + # массив разделов (заполняется в __setParamHelp) + self.chapterBloc = [] + #optEnd = "" + #if "user" in self.cmdName and not "mod" in self.cmdName: + #optEnd = _("user") + #elif "group" in self.cmdName and not "mod" in self.cmdName: + #optEnd = _("group") + #self.__setParamHelp() + + def getChapterNumber(self,NameChapter): + """Получить номер раздела по имени""" + num = 0 + for i in self.chapter: + if i[0] == NameChapter: + return num + num += 1 + return False + + def __setParamHelp(self): + """Внутренняя функция формирования справки по данным + + Перебирает все элементы списка data, проверяет их на доступность + данной программы, разбирает опции на среди data и формирует + для по ним справку. + """ + # сформировать нужное количество блоков раздела + self.chapterBloc = [""]*len(self.chapter) + # + sp = {} + i = 0 + # перебираем все элементы справки собираем элементы опции + # так же формируем разделы не опции + for par in self.data: + # перебираем только те опции, которые принадлежат команде + if self.access(par): + # есть короткая (возможно есть и длинная) + if par.has_key("shortOption"): + sp[par["shortOption"]+":"+par["helpChapter"]] = i + # есть только длинная опция + elif par.has_key("longOption"): + sp[par["longOption"]+":"+par["helpChapter"]] = i + # формирование разделов не опций + else: + helpTxt = par['help'] + numChapter = self.getChapterNumber(par['helpChapter']) + self.addChapterHelp(numChapter,helpTxt) + i += 1 + # перебираем все "собранные" опции + # опции перебираются по порядку в списке date + # для сортировки по ключам следует применить код: + # for index in sorted(sp.keys()): + # par = self.data[sp[index]] + for index in sorted(sp.values()): + par = self.data[index] + numChapter = self.getChapterNumber(par['helpChapter']) + # если есть и короткая и длинная + if "shortOption" in par and "longOption" in par: + paraminfo = "-%s, --%s "%(par["shortOption"],par["longOption"]) + # если есть только короткая + elif "shortOption" in par: + paraminfo = "-%s "%par["shortOption"] + # если только длинная + else: + paraminfo = "--%s "%par["longOption"] + # если указан параметр для опции + if "optVal" in par: + optVal = par["optVal"] + else: + optVal = "" + + # вывод вида: " [-o, ][--option] [PARAM]" "helpstring" + helpTxt = pcs(" "+paraminfo+optVal, self.column_width, \ + par['help'], self.consolewidth-self.column_width) + # добавить строку в нужный раздел + self.addChapterHelp(numChapter,helpTxt) + + def getHelp(self, optionsChapters=False): + """Выдать справку. + + Выдает справку в случае если указан optionsChapters, то фильтрует по + типу разделов. + + Параметры: + optionsChapters Flase или список опциональных разделов для + отображения + + Возвращаемые параметры: + Строка со справкой. + """ + # Выдать справку + help = "" + # перебираем все элементы справочных блоков + iterChapterBloc = iter(self.chapterBloc) + # перебираем все разделы по параметрам + for (nameChapter, visibleChapter, beforeStrChapter, \ + afterStrChapter, typeChapter) in self.chapter: + # получаем следующий блок (т.о. textChapterBloc соответ, chapter) + textChapterBloc = iterChapterBloc.next() + # если тип раздела опциональный + if optionsChapters and typeChapter=="options": + # проверяем нужно ли его отображать + if not (nameChapter in optionsChapters): + continue + bef = "\n"*beforeStrChapter + aft = "\n"*afterStrChapter + # если блок не пустой и раздел отображаемый + if len(textChapterBloc) > 0: + if visibleChapter: + help += nameChapter + ": " + bef + help += textChapterBloc + aft + help = help.rstrip()+"\n" + return help + + def addChapterHelp(self, numChapter, helpTxt): + """Добавить в раздел помощи numChapteк тектстовую строку helpTxt + + Параметры: + numChapter номер раздела в который нужно добавить данные справки + helpTxt строка, содержащая данные + """ + self.chapterBloc[numChapter] += helpTxt + return True + + def addData(self,dataHash): + # На будущее (добавляет опции) + self.data.append(dataHash) + return True + + def handleCheckAccess(self,dataHash): + """Замещаемый дополнительный обработчик проверки + доступности опции. + + Входные параметры: + dataHash элементы списка данных справки (self.data) + """ + return True + + def access(self,dataHash): + """Доступна ли опция вызывающей программе + + Параметры: + dataHash словарь элемент типа self.data + + Возвращаемые параметры: + True/False доступна/недоступна + """ + # доступна ли опция вызывающей программе + # опция без progAccess доступна + numProg = self.progName[self.cmdName] + if 'progAccess' in dataHash: + if numProg in dataHash['progAccess']: + # вызов дополнительной проверки доступа к опции + return self.handleCheckAccess(dataHash) + else: + return False + else: + # вызов дополнительной проверки доступа к опции + return self.handleCheckAccess(dataHash) + + def getTypeChapter(self, nameChapter): + """Получить тип раздела по его имени + + Параметры: + nameChapter название раздела + + Возвращаемые параметры: + строка тип раздела + Flase(Boolean) такой раздел отсутствует + """ + # фильтруем список по имени раздела, помещаем в список тип раздела + filtered = [typeChapter for name, na, na, na, typeChapter \ + in self.chapter if name == nameChapter] + # если среди фильтрованных есть хоть один элемент + if len(filtered) > 0: + # возвращаем - он запрашиваемый + return filtered[0] + else: + # такой раздел отсутствует + return False + + def clearAllOpt(self): + """Очистить все опции, полученные посредством getAllOpt""" + if len(self.shortOpt) > 0: + self.shortOpt = [] + if len(self.longOpt) > 0: + self.longOpt = [] + return True + + def getAllOpt(self,typeOpt="all", optionsChapters=False): + """Получить все доступные опции + + Параметры: + typeOpt 'short'/'long'/'all', вернуть короткие или длинные + опции или все (возвращаются кортежем) + optionsChapters фильтр для опций по типам разделов (список,кортеж) + + Возвращаемые параметры: + строка коротки или список строк длинных опций ('hb:c:wg:G:k:ms:u:') + """ + # Выдать все действующие опции + if typeOpt=="short" or typeOpt=="all": + if len(self.shortOpt) == 0: + for par in self.data: + if optionsChapters and\ + self.getTypeChapter(par['helpChapter'])=="options": + if not (par['helpChapter'] in optionsChapters): + continue + if par.has_key("shortOption") and self.access(par): + if par.has_key("optVal"): + self.shortOpt.append(par["shortOption"]+':') + else: + self.shortOpt.append(par["shortOption"]) + if typeOpt=="long" or typeOpt=="all": + if len(self.longOpt) == 0: + for par in self.data: + if optionsChapters and\ + self.getTypeChapter(par['helpChapter'])=="options": + #print par["longOption"] + if not (par['helpChapter'] in optionsChapters): + continue + if par.has_key("longOption") and self.access(par): + if par.has_key("optVal"): + self.longOpt.append(par["longOption"]+'=') + else: + self.longOpt.append(par["longOption"]) + if typeOpt=="short": + return "".join(self.shortOpt) + elif typeOpt=="long": + return self.longOpt + elif typeOpt=="all": + return ("".join(self.shortOpt),self.longOpt) + + def getShortOpt(self,option): + """Из любой опции получить короткую опцию. + + Фильтрация также происходит и по названию команды. + + Параметры: + option запрашиваемая опция + + Возвращаемые параметры: + короткая опция, если же для длинной опции нет короткой, возвращается + пустая строка. + """ + # Из любой опции получаем короткую опцию + for par in self.data: + if par.has_key("shortOption") and self.access(par): + if (par.has_key("longOption") and\ + par["longOption"] == option) or \ + par["shortOption"] == option: + return par["shortOption"] + break + return "" + +class cl_smartcon(object): + + def getconsolewidth(self): + """Получить ширину текущей консоли""" + s = struct.pack("HHHH", 0, 0, 0, 0) + fd_stdout = sys.stdout.fileno() + try: + x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) + except IOError: + # если ошибка то ширина 80 символов + return 80 + #(rows, cols, x pixels, y pixels) + return struct.unpack("HHHH", x)[1] + + def printRight(self, offsetLeft, offsetRight): + """Добавляет необходимое количество пробелов: + + количество пробелов = (ширина консоли - offsetLeft - offsetRight) + """ + cols = self.getconsolewidth() + for i in range(cols - offsetLeft - offsetRight): + sys.stdout.write(" ") + + def colorPrint(self,attr,fg,bg,string): + """Раскрашивает выводимое сообщение + + Параметры: + attr - это атрибут + fg - цвет символа + bg - цвет фона + + в случае если параметр равен "" то он не изменяется + + attr может принимать следующие значения: + 0 сбросить все атрибуты (вернуться в нормальный режим) + 1 яркий (обычно включает толстый шрифт) + 2 тусклый + 3 подчёркнутый + 5 мигающий + 7 реверсный + 8 невидимый + + fg может принимать следующие значения: + 30 чёрный + 31 красный + 32 зелёный + 33 жёлтый + 34 синий + 35 фиолетовый + 36 голубой + 37 белый + + bg может принимать следующие значения: + 40 чёрный + 41 красный + 42 зелёный + 43 жёлтый + 44 синий + 45 фиолетовый + 46 голубой + 47 белый + """ + lst = [] + if attr: + lst.append(attr) + if fg: + lst.append(fg) + if bg: + lst.append(bg) + sys.stdout.write("\033[%sm%s\033[0m" %(";".join(lst),string)) + + def redBrightPrint(self, string): + """Печатает яркое красное сообщение""" + self.colorPrint("1","31","",string) + + def greenBrightPrint(self, string): + """Печатает яркое зеленое сообщение""" + self.colorPrint("1","32","",string) + + def yellowBrightPrint(self, string): + """Печатает яркое желтое сообщение""" + self.colorPrint("1","33","",string) + + def blueBrightPrint(self, string): + """Печатает яркое cинее сообщение""" + self.colorPrint("1","34","",string) + + def lenString(self, string): + """Получаем длинну строки""" + stringUnicode = cl_utils._toUNICODE(string) + lenString = len(stringUnicode) + return lenString + + + def defaultPrint(self, string): + sys.stdout.write(string) + sys.stdout.flush() + + def printLine(self, argL, argR, offsetL=0, printBR=True): + """Печатает справа и слева консоли цветные сообщения""" + #Допустимые цвета + colorDict = {\ + # цвет по умолчанию + '':self.defaultPrint, + # ярко зеленый + 'greenBr':self.greenBrightPrint, + # ярко голубой + 'blueBr':self.blueBrightPrint, + # ярко красный + 'redBr':self.redBrightPrint, + # ярко желтый + 'yellowBr':self.yellowBrightPrint, + } + # cмещение от левого края консоли + #offsetL = 0 + for color,leftString in argL: + offsetL += self.lenString(leftString) + if colorDict.has_key(color): + # печатаем и считаем смещение + colorDict[color](leftString) + else: + colorDict[''](leftString) + # cмещение от правого края консоли + offsetR = 0 + for color,rightString in argR: + offsetR += self.lenString(rightString) + # Добавляем пробелы + if offsetR: + self.printRight(offsetL, offsetR) + for color,rightString in argR: + if colorDict.has_key(color): + # печатаем и считаем смещение + colorDict[color](rightString) + else: + colorDict[''](rightString) + if printBR: + print "" + + def printNotOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае сбоя""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('blueBr','['), + ('redBr',' !! '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOnlyNotOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае сбоя""" + self.printLine((('', string),), + (('blueBr','['), + ('redBr',' !! '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('blueBr','['), + ('greenBr',' ok '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOnlyOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха""" + self.printLine((('',string),), + (('blueBr','['), + ('greenBr',' ok '), + ('blueBr',']'), + ), offsetL, printBR) + + def printWARNING(self, string, offsetL=0, printBR=True): + """Вывод на печать предупреждения""" + self.printLine((('yellowBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) + + def printERROR(self, string, offsetL=0, printBR=True): + """Вывод на печать предупреждения""" + self.printLine((('redBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) + + def printSUCCESS(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха без [ok] справа""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) diff --git a/build/lib/calculate-lib/pym/cl_vars.py b/build/lib/calculate-lib/pym/cl_vars.py new file mode 100644 index 0000000..be174fd --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_vars.py @@ -0,0 +1,91 @@ +#-*- coding: utf-8 -*- + +# Copyright 2008-2010 Mir 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. + +#Допустимые ключи значений +# mode - режим переменной r-не переназначается из командной строки, +# w-переназначается из командной строки +# type - тип переменной состоит из двух элементов(что это и для чего +# это) +# value - значение переменной + +class Data: + + # имя компьютера + os_net_hostname = {'mode':"w"} + # разрешенные сети + os_net_allow ={} + # ip на всех интерфейсах + os_net_ip ={} + + #короткое название системы (CLD) + os_linux_shortname={} + + #домен + os_net_domain = {'mode':"w"} + + # Пути к ini файлам + cl_env_path = {'value':['/var/calculate/remote/calculate.env', + '/var/calculate/calculate.env', + '/etc/calculate/calculate.env']} + + # локаль (прим: ru_RU.UTF-8) + os_locale_locale = {} + # язык (прим: ru_RU) + os_locale_lang = {} + # язык (прим: ru) + os_locale_language = {} + + # раскладка клавиатуры для X + os_locale_xkb = {} + + # названия используемых раскладок клавиатуры для X + os_locale_xkbname = {} + + # архитектура компьютера (i686,x86_64) + os_arch_machine = {} + + #проход при наложении профилей 1,2,3,4,5 и.т д + cl_pass_step = {'mode':"w"} + + # обрабатываемый файл профиля + cl_pass_file = {'mode':"w"} + + # корневой раздел файловой системы + os_root_dev = {} + + # тип носителя (ram, hdd, usb-hdd, livecd) + os_root_type = {} + + # полное название системы + os_linux_name = {} + + # постфикс к названию системы + os_linux_subname = {} + + # название виртуальной машины (virtualbox, vmware, qemu) + hr_virtual = {} + + # версия системы + os_linux_ver = {} + + # Тип профиля + cl_pass_type = {'mode':"w"} + + # Действие программы + cl_pass_run = {'mode':"w"} + + #Логин пользователя + ur_login = {'mode':"w"} diff --git a/pym/cl_profile.py b/pym/cl_profile.py index db47e78..cad373a 100644 --- a/pym/cl_profile.py +++ b/pym/cl_profile.py @@ -6881,6 +6881,7 @@ class xml_gconf(xml_xfce): _comment = ("") # поддерживаемые аттрибуты тега entry. Пример supportEntryTypes = ("int", "bool", "float", "string", "list", "pair") + reStartTabs = re.compile("^(\t+)(.*)$") def __init__(self, text): self.text = text @@ -7111,8 +7112,14 @@ the same nodes at one level") def getConfig(self): """Получение текстового файла из XML документа""" + def expandStartTabs(s): + if s.startswith("\t"): + res = self.reStartTabs.findall(s) + return "".join((res[0][0].replace("\t"," "),res[0][1])) + else: + return s data = self.doc.toprettyxml().split("\n") - data = map(lambda x: x.replace("\t"," "), + data = map(lambda x: expandStartTabs(x), filter(lambda x: x.strip(), data)) dataOut = [] z = 0