#-*- coding: utf-8 -*- # Copyright 2008-2010 Mir Calculate. 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() 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 and \ ( nameValue[0].startswith("!include") or nameValue[0][1:].startswith("!include")): field.name = textLine.replace(self.sepFields,"") field.value = "" field.br = textLine fields.append(field) field = fieldData() elif len (nameValue) == 1: field.name = "" field.value = textLine.replace(self.sepFields,"") field.br = textLine fields.append(field) field = fieldData() elif 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 createFieldTerm(self, name, value, quote, docObj): """Создание поля переменная - значение при создании поля проверяется первый символ названия переменной и добавляется тег action "!" - drop удаляет "+" - join добавляет "-" - replace заменяет """ fieldAction = False if name: if name.startswith("!include") or name[1:].startswith("!include"): prefix = "!" name = re.sub(r"(include)\s*(\S)",r"\1 \2",name[1:]) else: prefix = "" if name[0] == "!" or name[0] == "-" or name[0] == "+": qnt = self.removeSymbolTerm(quote) fieldXML = docObj.createField("var",[qnt], prefix+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","")], prefix+name, [value], False, False) else: fieldXML = docObj.createField("var", [quote.replace("\n","")], name, [value], False, False) if fieldAction: docObj.setActionField(fieldXML, fieldAction) return fieldXML 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") reStartTabs = re.compile("^(\t+)(.*)$") 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 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: expandStartTabs(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)