You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-2.1-lib/pym/cl_profile.py

7235 line
309 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#-*- 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
"!" - <action>drop</action> удаляет
"+" - <action>join</action> добавляет
"-" - <action>replace</action> заменяет
"""
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 = '<?xml version="1.0" encoding="UTF-8"?><cxmlconf><head>'
docTxt += '<ver>%s</ver>'% version
docTxt += '<format>%s</format>' % typeDoc
docTxt += '</head><body></body></cxmlconf>'
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<lenOldFields:
nextNode = oldFields[i+1]
if self.getTypeField(nextNode) == "br":
removeNodes.append(nextNode)
removeNodesDict[nameNewField] = removeNodes
return removeNodes
def addNewFielsOldArea(self, newFields, joinNewFields, xmlOldArea):
"""Добавляет новые XML поля в область профиля"""
removeNodesDict = {}
notRemoveNodesDict = {}
for notRemNode in joinNewFields:
nameField = self.getNameField(notRemNode)
if not notRemoveNodesDict.has_key(nameField):
notRemoveNodesDict[nameField] = []
notRemoveNodesDict[nameField].append(notRemNode)
else:
notRemoveNodesDict[nameField].append(notRemNode)
notSepListField = []
sepListField = []
for nField in newFields:
if self.getRemoveNodeSepList(removeNodesDict, xmlOldArea,
nField):
sepListField.append(nField)
else:
if self.getNameField(nField):
notSepListField.append(nField)
for name in notRemoveNodesDict.keys():
if removeNodesDict.has_key(name):
removeNodesDict[name] = []
for removeNodes in removeNodesDict.values():
if removeNodes:
if self.getTypeField(removeNodes[-1]) == "seplist":
removeNodes = removeNodes[:-1]
else:
removeNodes = removeNodes[:-2]
for removeNode in removeNodes:
xmlOldArea.removeChild(removeNode)
for node in sepListField:
node.setAttribute("type", "seplist")
if not (self.getActionField(node) == "join" or\
self.getActionField(node) == "drop"):
self.setActionField(node,"insert")
self.joinField(xmlOldArea, node)
#else:
#self.setActionField(node, "append")
#baseBody.appendChild(node)
for node in notSepListField:
if self.getTypeField(node) == "seplist":
#if removeNodesDict.has_key(self.getNameField(node)):
#print removeNodesDict[self.getNameField(node)]
self.setActionField(node, "append")
xmlOldArea.appendChild(node)
else:
self.joinField(xmlOldArea, node)
def insertBeforeSepAreas(self, xmlArea):
"""Добавляет разделитель областей перед каждой областью"""
if not self.sepAreas:
return False
areaNodes = xpath.Evaluate('descendant::area',xmlArea)
for areaNode in areaNodes:
prevNode = areaNode.previousSibling
if prevNode:
parentNode = areaNode.parentNode
parentNode.insertBefore(self.sepAreas.cloneNode(True),
areaNode)
return True
def getAreaFields(self, nameArea, xmlArea):
"""По имени области выводит названия и значения всех переменных
поиск ведется только в 1-х потомках области xmlArea
на выход словарь переменных {имя:значение}
"""
namesAreaComare = xpath.Evaluate(\
"child::area/child::caption[child::name='%s']" %(nameArea),
xmlArea)
if not namesAreaComare:
return False
fields = xpath.Evaluate("child::field/child::name",
namesAreaComare[0].parentNode)
dictVar = {}
for fieldName in fields:
nodeField = fieldName.parentNode
fieldValue = xpath.Evaluate("child::value",nodeField)
name = fieldName.firstChild.nodeValue
value = ""
if fieldValue and fieldValue[0].firstChild:
value = fieldValue[0].firstChild.nodeValue
dictVar[name] = value
return dictVar
def getAreaFieldValues(self, nameArea, nameField, xmlArea):
"""По имени области и имени переменной выводит значениe переменной
поиск ведется только в 1-х потомках области xmlArea
"""
namesAreaComare = xpath.Evaluate(\
"child::area/child::caption[child::name='%s']" %(nameArea),
xmlArea)
fieldsVal = False
for areaComp in namesAreaComare:
fieldsVal = xpath.Evaluate(\
"child::field[child::name='%s'] "\
%(nameField), areaComp.parentNode)
if fieldsVal:
break
if not fieldsVal:
return False
fieldValue = xpath.Evaluate("child::value",
fieldsVal[0])
return fieldValue[0].firstChild.nodeValue
def getAllAreas(self):
"""Выдает все области"""
return xpath.Evaluate('descendant::area', self.body)
def getArea(self, nameArea, xmlArea):
"""По имени области находит области (первый потомок xmlArea)"""
namesAreaComare = xpath.Evaluate(\
"child::area/child::caption[child::name='%s']" %(nameArea),
xmlArea)
return map(lambda x: x.parentNode, namesAreaComare)
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" or newAreaAction == "replace"):
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)
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" or\
newAreaAction == "replace"):
appendArea(baseNode, xmlNewArea)
else:
tmpXmlNewAreas = xpath.Evaluate('child::area',xmlNewArea)
for na in tmpXmlNewAreas:
for bn in baseNodes:
self.joinArea(bn, na)
return True
def joinDoc(self, xmlNewDoc):
"""Объединяет два документа"""
newRootNode = xmlNewDoc.documentElement
newBodyNode = xpath.Evaluate('child::body',newRootNode)[0]
newImportBodyNode = self.doc.importNode(newBodyNode, True)
# Перед объединение области с документом
# удаляем комментарии
self.removeComment(newImportBodyNode)
self.joinBody(self.body, newImportBodyNode)
# расставляем BR
self.insertBRtoBody(self.body)
def getQuoteField(self, xmlField):
"""Выдает текст из поля"""
xmlQuotes = xpath.Evaluate('child::quote',xmlField)
br = ""
if xmlField.hasAttribute("type") and\
xmlField.getAttribute("type") == "br":
br = "\n"
if xmlQuotes:
field = xmlQuotes[0]
if field.firstChild:
return field.firstChild.nodeValue + br
return "" + br
def getFieldsArea(self, xmlArea):
"""Выдает потомков XML области"""
xmlFields = []
childNodes = xmlArea.childNodes
for node in childNodes:
if node.nodeType == node.ELEMENT_NODE:
if node.tagName == "area" or node.tagName == "field":
xmlFields.append(node)
return xmlFields
def getTypeField(self, xmlField):
"""Выдает тип поля"""
if xmlField.hasAttribute("type"):
return xmlField.getAttribute("type")
else:
return False
def getNameField(self, xmlField):
"""Выдает имя поля"""
xmlNameFields = xpath.Evaluate('child::name', xmlField)
if xmlNameFields and xmlNameFields[0].firstChild:
return xmlNameFields[0].firstChild.nodeValue
else:
return False
def getNameArea(self, xmlArea):
"""Выдает имя области"""
xmlNameAreas = xpath.Evaluate('child::caption/name', xmlArea)
if xmlNameAreas and xmlNameAreas[0].firstChild:
return xmlNameAreas[0].firstChild.nodeValue
else:
return False
def xmlToText(self, xmlAreas, text):
"""Преобразует список XML областей в текст"""
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
for i in xmlAreas:
if i.tagName == "area":
quotesI = getQuotesArea(i)
startAreaI = quotesI[0]
endAreaI = quotesI[1]
text.append(startAreaI)
xmlFieldsI = self.getFieldsArea(i)
for f in xmlFieldsI:
if f.tagName == "area":
quotesF = getQuotesArea(f)
startAreaF = quotesF[0]
endAreaF = quotesF[1]
text.append(startAreaF)
xmlFieldsF = self.getFieldsArea(f)
self.xmlToText(xmlFieldsF, text)
text.append(endAreaF)
else:
fieldF = self.getQuoteField(f)
text.append(fieldF)
text.append(endAreaI)
else:
fieldI = self.getQuoteField(i)
text.append(fieldI)
def getActionField(self, xmlField):
"""Выдает свойство action XML поля"""
xmlActions = xpath.Evaluate('child::action',xmlField)
if xmlActions and xmlActions[0].firstChild:
return xmlActions[0].firstChild.nodeValue
else:
return False
def getFieldValues(self, xmlField):
"""Выдает значения XML поля в виде массива"""
vals = []
xmlValues = xpath.Evaluate('child::value',xmlField)
if xmlValues:
for node in xmlValues:
if node.firstChild:
vals.append(node.firstChild.nodeValue)
return vals
def getActionArea(self, xmlArea):
"""Выдает свойство action XML области"""
xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
if xmlActions and xmlActions[0].firstChild:
return xmlActions[0].firstChild.nodeValue
else:
return False
def delActionNodeArea(self, xmlArea):
"""Удаляет свойство action XML области"""
xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
if xmlActions and xmlActions[0].firstChild:
parentNode = xmlActions[0].parentNode
parentNode.removeChild(xmlActions[0])
return True
else:
return False
def delActionNodeField(self, xmlField):
"""Удаляет свойство action XML поля"""
xmlActions = xpath.Evaluate('child::action',xmlField)
if xmlActions and xmlActions[0].firstChild:
parentNode = xmlActions[0].parentNode
parentNode.removeChild(xmlActions[0])
return True
else:
return False
# Создает распределенные списки
def postParserListSeplist(self, xmlArea):
"""Создает распределенные списки из элементов области"""
# Потомки
childNodes = self.getFieldsArea(xmlArea)
# содержит списки нод полей с одинаковыми именами в одной области
fieldsSeplist = {}
for node in childNodes:
if node.tagName == "area":
self.postParserListSeplist(node)
else:
fieldName = False
xmlFieldNameNodes = xpath.Evaluate('child::name',node)
if xmlFieldNameNodes and xmlFieldNameNodes[0].firstChild:
fieldName = xmlFieldNameNodes[0].firstChild.nodeValue
if fieldName:
if fieldsSeplist.has_key(fieldName):
fieldsSeplist[fieldName].append(node)
else:
fieldsSeplist[fieldName] = []
fieldsSeplist[fieldName].append(node)
for listNodes in fieldsSeplist.values():
if len(listNodes) > 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)<len(blocsTxt):
headersTxt.insert(0,"")
if len(headersTxt)!=len(blocsTxt):
return False
return (headersTxt, blocsTxt)
else:
return False
class _file(_error):
"""Класс для работы с файлами
"""
def __init__(self):
# Имя файла старого профиля
self.nameFileOld = ""
# Старый профиль
self.oldProfile = ""
# Имя файла нового профиля
self.nameFileNew = ""
# Новый профиль
self.newProfile = ""
# Дескриптор файла нового профиля
self.FN = False
# Дескриптор файла старого профиля
self.FO = False
# Владелец и режим доступа файла профиля
self._mode = False
self._uid = False
self._gid = False
self.openNewFile = self.__openNewFile
self.absFileName = self.__absFileName
self.closeNewFile = self.__closeNewFile
def getFileType(self, profile="New"):
"""выдать тип файла (text, bin)
"""
if self.nameFileNew:
nameFile = self.nameFileNew
if profile=="Old" and self.nameFileOld:
nameFile = self.nameFileOld
sp = subprocess.Popen("file '%s'"%nameFile, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
close_fds=True, shell=True)
fout, fin, ferr = (sp.stdout, sp.stdin, sp.stderr)
fin.close()
textLine = fout.readline()
fout.readlines()
fout.close()
retText = ""
if textLine:
listTextLine = textLine.split(":")
if len(listTextLine) == 2:
textFormats = ["text", "XML"]
retText = "bin"
fileTypeString = listTextLine[1]
for textFormat in textFormats:
if textFormat in fileTypeString:
retText = "text"
break
ferr.close()
return retText
def scanDirs(self, profilesDirs):
"""Сканирует дерево каталогов выдает два списка: директории, файлы"""
dirs = []
class dirProf:
def __init__(self):
self.baseDir = False
self.dirs = []
self.files = []
self.links = []
self.sockets = []
self.fifo = []
def getFilesDir(dirP, dirname,names):
for nameFile in names:
absNameFile = dirname + "/" + nameFile
if os.path.islink(absNameFile):
dest = absNameFile
src = os.readlink(absNameFile)
dirP.links.append((src,dest))
elif os.path.isfile(absNameFile):
dirP.files.append(absNameFile)
elif os.path.isdir(absNameFile):
dirP.dirs.append(absNameFile)
elif stat.S_ISSOCK(os.stat(absNameFile)[stat.ST_MODE]):
dirP.sockets.append(absNameFile)
elif stat.S_ISFIFO(os.stat(absNameFile)[stat.ST_MODE]):
dirP.fifo.append(absNameFile)
for profileDir in profilesDirs:
if profileDir:
dirP = dirProf()
dirP.baseDir = profileDir
dirs.append(dirP)
os.path.walk(profileDir,getFilesDir, dirP)
return dirs
def __absFileName(self, nameFile):
"""Вычисление пути к файлу"""
pathList = nameFile.split("/")
chortNameFile = pathList.pop()
absPath = os.path.abspath("/".join(pathList))
File = absPath + "/" + chortNameFile
return File
def saveOldFile(self):
"""Записать конфигурацию"""
if self.FO:
self.FO.truncate(0)
self.FO.seek(0)
if not self.oldProfile:
self.oldProfile = self.newProfile
try:
self.FO.write(self.oldProfile)
except:
self.setError (_("not open file:" ) + self.nameFileOld)
return False
self.FO.flush()
return True
def getModeFile(self, nameFile):
"""Выдает информацию о файле
права файла, владелец, группа файла (777,test, group)
"""
fd = os.open(nameFile, os.O_RDONLY)
fst = os.fstat(fd)
uid = fst.st_uid
gid = fst.st_gid
mode = stat.S_IMODE(fst.st_mode)
os.close(fd)
return (mode,uid,gid)
def __openNewFile(self, nameFileNew):
"""Открыть файл профиля"""
FN = False
try:
FN = open (nameFileNew, "r")
self._mode,self._uid,self._gid = self.getModeFile(nameFileNew)
except:
self.setError (_("not open file:" ) + nameFileNew)
return False
return FN
def __closeNewFile(self):
if self.FN:
self.FN.close()
self.FN = False
def __closeOldFile(self):
if self.FO:
self.FO.close()
self.FO = False
def __openOldFile(self, nameFileOld, mode, uid, gid):
"""Октрыть конфигурационный файл"""
FO = False
try:
if os.path.islink(nameFileOld):
# если ссылка то удаляем её
os.unlink(nameFileOld)
FO = open (nameFileOld, "r+")
except:
try:
fd = os.open(nameFileOld, os.O_CREAT)
os.close(fd)
os.chmod(nameFileOld, mode)
os.chown(nameFileOld,uid,gid)
FO = open(nameFileOld, "r+")
except:
self.setError (_("not open file:" ) + nameFileOld)
return False
return FO
def openFiles(self, nameFileNew, nameFileOld):
"""Открывает два профайла новый и старый"""
self.oldProfile = ""
self.newProfile = ""
self.closeFiles()
self.FN = False
self.FO = False
self.nameFileOld = self.__absFileName(nameFileOld)
self.nameFileNew = self.__absFileName(nameFileNew)
self.FN = self.__openNewFile(self.nameFileNew)
self.FO = self.__openOldFile(self.nameFileOld,
self._mode, self._uid, self._gid)
if self.FN and self.FO:
self.newProfile = self.FN.read()
self.oldProfile = self.FO.read()
def __del__(self):
self.closeFiles()
def closeFiles(self):
"""Закрытие файлов"""
self.__closeNewFile()
self.__closeOldFile()
class utfBin:
"""Класс для преобразования в utf-8
преобразование бинарного или смеси бинарного и utf-8 кода в utf-8 и
обратное преобразование
методы класса encode и decode
"""
def _retUTF(self, char):
byte = ord(char)
if byte<=127:
return ('_ch_',1)
elif byte<=191:
return ('_nb_',1)
elif byte<=223:
return ('_fb_',2)
elif byte<=239:
return ('_fb_',3)
elif byte<=247:
return ('_fb_',4)
else:
return ('_er_',1)
def _sumbUtf(self, symbols, lenTail):
if not symbols:
return (False,0)
lenSymb = len(symbols)
if lenSymb >= 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<rTerm>[a-zA-Z0-9\-_]+)\
(?P<func>\([a-zA-Z0-9_\-\+\,\*\/\.\'\"~]+\))?\
(?P<lTerm>[\>\<\=\!\&\|]+\
[a-zA-Z0-9\>\<\=\!\|\&\-\+\*\/_\.\,\(\)\'\"~]*)#\
\n*(?P<body>.+?)\n*#(?P=rTerm)#(?P<end>[ ,\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
"!" - <action>drop</action>
"-" - <action>replace</action>
"""
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
"!" - <action>drop</action> удаляет
"+" - <action>join</action> добавляет
"-" - <action>replace</action> заменяет
"""
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
"!" - <action>drop</action>
"-" - <action>replace</action>
"""
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 = '''<?xml version="1.0" encoding="UTF-8"?>
<channel version="1.0">
</channel>'''
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 = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE config SYSTEM "config.dtd">
<panels>
</panels>'''
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. Пример <entry type="int"/>
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 = '''<?xml version="1.0"?><gconf></gconf>'''
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 '<gconf>...</gconf>')"))
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 '<gconf><%s>..</%s></gconf>'")%(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" - <entry type="%s"/>')\
%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)