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-server/pym/cl_profile.py

6314 lines
270 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 cl_xml
from cl_xml import xpath, firstChild,\
insertBefore, str_to_xml_doc, xml_to_str, xmlShare,\
xmlNode, xmlField, xmlFields, xmlArea, xmlDoc
import subprocess
import types
import copy
import random
import string
import time
from copy import deepcopy
from collections.abc import Iterable
_ = lambda x : x
tr = cl_base.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])
def cmp(a, b):
return (a > b) - (b < a)
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 as 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)
res = eval("("+"'"+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)
res = eval("("+"'"+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
res = eval("(%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
res = eval("("+'"""'+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
res = eval("(%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) == tuple and len(comment) == 2:
_titleList = (_("Modified"), _("File of a profile"))
reCalcHeader =\
re.compile("\s*%s\s+%s.+Calculate.+\s+%s.+\s+%s\s?" % (\
comment[0],
_titleList[0],
_titleList[1],
comment[1],
),
re.M|re.I|re.U)
textUnicode = text
reS = reCalcHeader.search(textUnicode)
if reS:
textBody = textUnicode[:reS.start()]+textUnicode[reS.end():]
if textBody:
self.body = textBody
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 "format" in self.params:
return self.params["format"]
else:
return "raw"
def _getAppend(self):
"""Выдать тип добавления файла"""
if "append" in self.params 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 "comment" in self.params:
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 "append" in self.params 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.set("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 = list(self.docObj.getNodeBody())
for node in childNodes:
if node.tag == "field":
listConfigTxt.append(self.docObj.getQuoteField(node))
elif node.tag == "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 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.append(nameNode)
if action:
actNode = tmpNode.createNode(doc, "action", action)
self.caption.append(actNode)
for q in quotes:
quoteNode = tmpNode.createNode(doc, "quote", q)
self.caption.append(quoteNode)
return self.caption
def getCaption(self):
"""Выдает XML ноду заголовка области"""
return self.caption
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.decode("UTF-8").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 = []
for profileDir in profilesDirs:
if profileDir:
dirP = dirProf()
dirP.baseDir = profileDir
for dirname, dirs, files in os.walk(profileDir):
for nameFile in dirs + files:
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)
dirs.append(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:]
return chr(int(he, 16))
def _hexToChar(self, he):
return chr(int(he, 16))
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:
# from . import cl_utils2
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 'uidNumber' in resSearch[0][0][1] and\
'gidNumber' in resSearch[0][0][1] and\
'homeDirectory' in resSearch[0][0][1]:
uid = resSearch[0][0][1]['uidNumber'][0].decode("UTF-8")
gid = resSearch[0][0][1]['gidNumber'][0].decode("UTF-8")
# uid = searchUser[0][0][1]['uidNumber'][0]
# gid = searchUser[0][0][1]['gidNumber'][0]
homeDir = resSearch[0][0][1]['homeDirectory'][0].decode("UTF-8")
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=None, skipDir=None,
prefix=None, flagDir=False):
"""Время последней модификации внутри директории scanDir"""
if skipFile is None:
skipFile = []
if skipDir is None:
skipDir = []
ret = True
if prefix is None:
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=None, filesFilter=None):
# Необрабатываемые директории
self.dirsFilter = dirsFilter if dirsFilter is not None else []
# Необрабатываемые файлы
self.filesFilter = filesFilter if filesFilter is not None else []
_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)
res = int(strOct, 8)
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(key=len, reverse=True)
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 = [[x,next(genOut)] for x in genIn]
createDirs = []
createDir = []
while (len(inAndOutDirs)>1):
tmpDirIn = baseDir + "/".join(x[0] for x in inAndOutDirs)
tmpDirOut = baseDirMv + "/".join(x[1] for x in 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 as 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 strNum in localVars:
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 nameLocVar not in localVars:
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 = []
for dirname, dirs, files in os.walk(self.basePkgDir):
for nameFile in dirs + files:
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 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 = [os.path.join(category, x) for x in 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 range(lenStr)])
elif terms[0] == fArgvNames[1]:
replace=''.join([random.choice(string.ascii_letters + \
string.digits) for i in range(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(x.split(".")[0] for x in 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) == tuple 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 = []
for profileDir in profilesDirs:
if profileDir:
# Обработка условий в названии директории
if self.getNeedProfile(profileDir):
if self.getError():
flagError = True
break
dirP = dirProf()
dirP.baseDir = profileDir
for dirname, dirs, files in os.walk(profileDir):
for nameFile in dirs + files:
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)
dirs.append(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 = list(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(x.split("?")[0] for x in 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(x.split("?")[0] for x in 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 "name" in objHeadDir.params:
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 = list(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 "name" in objHead.params:
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(x.split("?")[0] for x in 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 "name" in objHeadDir.params:
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 = list(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(x.split("?")[0] for x in 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 = list(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 "name" in objHead.params:
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 "chmod" in objHead.params:
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 "chown" in objHead.params:
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 "name" in objHeadNew.params:
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 "force" in objHeadNew.params:
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 "mirror" in objHeadNew.params:
if "link" in objHeadNew.params:
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 "link" in objHeadNew.params and\
"symbolic" not in objHeadNew.params:
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 "symbolic" in objHeadNew.params:
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 "chmod" in objHeadNew.params:
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 "chown" in objHeadNew.params:
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\
"symbolic" not in objHeadNew.params and\
"link" in objHeadNew.params:
return (applyFiles, False)
return (applyFiles, objHeadNew)
def createNewClass(self, name, bases, attrs=None):
"""Создает объект нового класса
createNewClass(self, name, bases, attrs)
name - имя класса - str,
bases - cписок наследуемых классов - (tuple),
attrs - аттрибуты класса - {dict}
"""
if attrs is None:
attrs = {}
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):
try:
with open(fileName, encoding="utf-8", errors="strict") as FD:
newProfile = FD.read()
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 ("link" in objHeadNew.params and\
"symbolic" in objHeadNew.params):
# проверяем кодировку оригинального файла
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)
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:
objProfNew = eval("%s(self.newProfile)"%\
(objHeadNew.fileType))
except NameError:
#Создаем объект из self.newObjProt с помощью
# метаклассов
if objHeadNew.fileType in self.newObjProt:
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()
# Если не UTF-8 производим преобразование
if flagNotUtf8New:
self.newProfile = objTxtCoder.decode(self.newProfile)
# Титл для объединения
if ListOptTitle:
title = self.getTitle(objProfNew._comment,
ListOptTitle)
# Замена
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:
objProfNew = eval("%s(self.newProfile)"%\
(objHeadNew.fileType))
except NameError:
#Создаем объект из self.newObjProt с помощью
# метаклассов
if objHeadNew.fileType in self.newObjProt:
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)
# В случае пустого конфигурационного файла
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 objHeadNew.fileType in self.newObjProt:
objProfOldCl = self.createNewClass(\
objHeadNew.fileType,
self.newObjProt[objHeadNew.fileType])
objProfOld = objProfOldCl(self.oldProfile)
else:
objProfOld = eval("%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().split("\n")
data.insert(1,title)
self.oldProfile = "\n".join(data)
else:
if objHeadNew.execStr:
self.oldProfile = objHeadNew.execStr + title +\
objProfOld.getConfig()
elif objHeadOld.execStr:
self.oldProfile = objHeadOld.execStr + title +\
objProfOld.getConfig()
else:
self.oldProfile = title +\
objProfOld.getConfig()
# Декодируем если кодировка не 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"""
# удаляем пустые области
xmlAreas = xpath.Evaluate("child::area", self.docObj.body)
removeList = []
for xmlArea in xmlAreas:
xmlFields = xpath.Evaluate("child::field/name", xmlArea)
if not xmlFields:
removeList.append(xmlArea)
for xmlArea in removeList:
parentNode = xmlArea.getparent()
parentNode.remove(xmlArea)
# Для добавления перевода строки между областями если его нет
xmlAreas = xpath.Evaluate("child::area", self.docObj.body)
xmlArea = None
for xmlArea in xmlAreas:
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.getnext() is not None:
parentNode = xmlArea.getparent()
nextNode = xmlArea.getnext()
insertBefore(parentNode, self.docObj.createField(
"br", [], "", [], False, False), nextNode)
# Добавление переводов строк между полями
if xmlFields:
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.getprevious() is not None and
not (self.docObj.getTypeField(
node.getprevious()) in ("br", "comment"))):
insertBefore(xmlArea, self.docObj.createField(
"br", [], "", [], False, False), node)
# Удаление лишних переводов строк
childNodes = self.docObj.getFieldsArea(self.docObj.body)
lenBr = 0
removeBrNodes = []
for node in childNodes:
if (node.tag == "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.remove(rmNode)
# Если после есть BR а за ним ничего нет, удаляем BR
if xmlArea is not None:
if (xmlArea.getnext() is not None and
self.docObj.getTypeField(xmlArea.getnext()) == "br"):
if xmlArea.getnext().getnext() is None:
parentNode = xmlArea.getnext().getparent()
parentNode.remove(xmlArea.getnext())
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.append(area)
else:
fieldsNodes = docObj.tmpFields.getFields()
for fieldNode in fieldsNodes:
rootNode.append(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.append(areaXMLChild)
else:
self.createXML(f.fields, areaXML, docObj)
if "\n" in f.end:
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
areaXML.append(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.append(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
areaXML.append(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
areaXML.append(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.append(fieldXML)
if i.header and i.start:
rootNode.append(areaXML)
if "\n" in i.end:
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
rootNode.append(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.append(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],"", [],
False, False)
rootNode.append(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
rootNode.append(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.append(fieldXML)
#rootNode.append(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.append(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.append(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.set("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.append(area)
else:
fieldsNodes = docObj.tmpFields.getFields()
for fieldNode in fieldsNodes:
rootNode.append(fieldNode)
docObj.clearTmpFields()
z += 1
#print docObj.doc.toprettyxml()
return docObj
def getConfig(self):
"""Выдает конфигурационный файл"""
listConfigTxt = []
childNodes = list(self.docObj.getNodeBody())
for node in childNodes:
if node.tag == "field":
listConfigTxt.append(self.docObj.getQuoteField(node))
elif node.tag == "area":
self.docObj.xmlToText([node], listConfigTxt)
tmp = "".join(listConfigTxt)
return "\n".join(x for x in tmp.split("\n") if x.strip())
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=None):
"""Последующая постобработка XML"""
# Добавляем перевод строки если его нет в конец области
if xmlArea is None:
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.append(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.set("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 postXML(self):
"""Последующая постобработка XML"""
xmlFields = xpath.Evaluate("child::field", self.docObj.body)
# Добавление переводов строк между полями
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.getprevious() is not None and
(self.docObj.getTypeField(node.getprevious()) not in
("br", "comment"))):
insertBefore(self.docObj.body, self.docObj.createField(
"br", [], "", [], False, False), node)
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.append(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
nodeBody.append(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
nodeBody.append(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.append(fieldXML)
return docObj
def join(self, procmailObj):
"""Объединяем конфигурации"""
if isinstance(procmailObj, procmail):
#print self.docObj.doc.toprettyxml()
self.docObj.joinDoc(procmailObj.doc)
self.postXML()
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.append(area)
else:
fieldsNodes = docObj.tmpFields.getFields()
for fieldNode in fieldsNodes:
rootNode.append(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 PlasmaArea():
def __init__(self):
self.header = False
self.start = False
self.fields = []
self.end = ""
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.getparent()
parentNode.remove(rmNode)
captionAreasRemove = xpath.Evaluate(\
"descendant::area/child::caption[child::action='drop']",
xmlNewArea)
for rmNodeCapt in captionAreasRemove:
rmNode = rmNodeCapt.getparent()
parentNode = rmNode.getparent()
parentNode.remove(rmNode)
self.setActionArea(xmlNewArea, "append")
# Добавляем разделитель областей во вложенные области
areaNodes = xpath.Evaluate('descendant::area',xmlNewArea)
for areaNode in areaNodes:
self.setActionArea(areaNode,"append")
parentNode = areaNode.getparent()
insertBefore(parentNode, deepcopy(self.sepAreas),
areaNode)
baseNode.append(xmlNewArea)
# Добавляем разделитель областей
insertBefore(baseNode, deepcopy(self.sepAreas), 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 firstChild(nodesNewArea[0]) is not None:
nameArea = firstChild(nodesNewArea[0]).text.strip()
flagFindArea = False
baseNodes = []
newAreaAction = None
for oName in nodesNames:
newAreaAction = self.getActionArea(xmlNewArea)
oArea = oName.getparent().getparent()
oNameTxt = ""
if firstChild(oName) is not None:
oNameTxt = firstChild(oName).text
if nameArea == oNameTxt:
flagFindArea = True
# При использовании удаления
if newAreaAction == "drop":
prevNode = oName.getparent().getparent().getprevious()
removePrevNodes = []
while prevNode is not None\
and self.getTypeField(prevNode) == "br":
removePrevNodes.append(prevNode)
prevNode = prevNode.getprevious()
for removeNode in removePrevNodes:
baseNode.remove(removeNode)
baseNode.remove(oName.getparent().getparent())
continue
elif newAreaAction == "replace":
oldAreaNode = oName.getparent().getparent()
newAreaCaption = xpath.Evaluate('child::caption',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaCaption and oldAreaCaption:
xmlNewArea.replace(newAreaCaption, oldAreaCaption)
self.setActionArea(xmlNewArea,"replace")
baseNode.replace(oldAreaNode, xmlNewArea)
continue
baseNodes.append(oName.getparent().getparent())
# Заменяем QUOTE
oldAreaNode = oName.getparent().getparent()
oldAreaQuote = xpath.Evaluate('child::caption/quote',
oldAreaNode)[0]
if oldAreaQuote and\
firstChild(oldAreaQuote) is None:
newAreaQuote = xpath.Evaluate('child::caption/quote',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaQuote and oldAreaCaption:
oldAreaCaption.replace(oldAreaQuote, newAreaQuote)
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)
"""
area = PlasmaArea
def findPathArea(listPath, areaF):
"""Ищет путь в области
areaF - объект area
listPath - cписок названий областей
"""
ret = False
if not listPath:
return ret
flagList = False
if isinstance(areaF, list):
fields = areaF
flagList = True
else:
fields = areaF.fields
if areaF.header == listPath[0]:
ret = areaF
else:
return ret
for i in fields:
if isinstance(i, PlasmaArea):
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 = [x for x in slpNamesBlock if x.strip()]
#namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock)
findArea = findPathArea(copy.copy(namesBlock), areas)
namesBlockList.append(namesBlock)
if findArea:
if len(namesBlock) > 1:
namesBlockView = [self.removeSymbolTerm(x) for x in 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 = [self.removeSymbolTerm(x) for x in 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 and
isinstance(findAreaPrev, area)):
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 isinstance(findArea, area):
#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 isinstance(i, PlasmaArea):
if i.header:
areaXML = self.createCaptionTerm(i.header, i.start,
i.end.replace("\n",""),
docObj)
for f in i.fields:
if isinstance(f, PlasmaArea):
if f.header:
areaXMLChild = self.createCaptionTerm(f.header,
f.start,
f.end.replace("\n",""),
docObj)
self.createXML(f.fields, areaXMLChild, docObj)
areaXML.append(areaXMLChild)
else:
self.createXML(f.fields, areaXML, docObj)
if "\n" in f.end:
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
areaXML.append(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.append(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
areaXML.append(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
areaXML.append(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.append(fieldXML)
if i.header:
rootNode.append(areaXML)
if "\n" in i.end:
fieldXMLBr = docObj.createField("br",[],
"",[],
False, False)
rootNode.append(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.append(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br",[],"", [],
False, False)
rootNode.append(fieldXMLBr)
elif field.comment != False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
rootNode.append(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.append(fieldXML)
#rootNode.append(areaXML)
def createTxtConfig(self, strHeader, dictVar):
"""Cоздает область с заголовком
создает текст конфигурационного файла в формате samba из
заголовка (строка) и словаря переменных
"""
if not strHeader:
return ""
if type(strHeader) in (tuple, list):
outTxt = "".join("["+x+"]" for x in 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
# Создаем экземпляр нового класса
docObj = xmlDocPlasma()
# Создание объекта документ c пустым разделителем между полями
docObj.createDoc(self.configName, self.configVersion)
if not areas:
return docObj
self.createXML(areas, docObj.getNodeBody(), docObj)
return docObj
def postXML(self):
"""Последующая постобработка XML"""
# Для добавления перевода строки между областями если его нет
def getQuotesArea(xmlArea):
quotes = []
xmlQuotes = xpath.Evaluate('child::caption/quote', xmlArea)
for node in xmlQuotes:
if firstChild(node) is not None:
quotes.append(firstChild(node).text)
if len(quotes) == 0:
quotes.append("")
quotes.append("")
elif len(quotes) == 1:
quotes.append("")
return quotes
xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body)
for xmlArea in xmlAreas:
# Перед пустой областью и после нее удаляем переводы строк
if getQuotesArea(xmlArea) == ["", ""]:
if (xmlArea.getprevious() is not None and
self.docObj.getTypeField(
xmlArea.getprevious()) == "br"):
parentNode = xmlArea.getprevious().getparent()
prev_prev_sbl = xmlArea.getprevious().getprevious()
if (prev_prev_sbl and
self.docObj.getTypeField(
prev_prev_sbl) == "br"):
parentNode.remove(
xmlArea.getprevious().getprevious())
parentNode.remove(xmlArea.getprevious())
if (xmlArea.getnext() is not None and
self.docObj.getTypeField(
xmlArea.getnext()) == "br"):
parentNode = xmlArea.getnext().getparent()
next_next_sbl = xmlArea.getnext().getnext()
if (next_next_sbl is not None and
self.docObj.getTypeField(
next_next_sbl) == "br"):
parentNode.remove(xmlArea.getnext().getnext())
parentNode.remove(xmlArea.getnext())
continue
# Собираем поля в кучку
xmlChildAreas = xpath.Evaluate("child::area", xmlArea)
if xmlChildAreas:
childNodes = self.docObj.getFieldsArea(xmlArea)
firstChildArea = xmlChildAreas[0]
if (firstChildArea.getprevious() and
self.docObj.getTypeField(
firstChildArea.getprevious()) == "br"):
prev_prev_sbl = (
firstChildArea.getprevious().getprevious())
if prev_prev_sbl:
if self.docObj.getTypeField(prev_prev_sbl) == "br":
firstChildArea = firstChildArea.getprevious()
flagFoundArea = False
it = 0
lenChild = len(childNodes)
for node in childNodes:
it += 1
if node.tag == "area":
flagFoundArea = True
continue
if flagFoundArea and node.tag == "field":
if self.docObj.getTypeField(node) == "var":
insertBefore(xmlArea, node, firstChildArea)
if it < lenChild:
node_type = self.docObj.getTypeField(
childNodes[it])
if node_type == "br":
insertBefore(xmlArea, childNodes[it],
firstChildArea)
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.getprevious() and
not (self.docObj.getTypeField(
node.getprevious()) in ("br", "comment"))):
insertBefore(xmlArea, 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:
insertBefore(xmlArea, self.docObj.createField("br",
[], "", [],
False, False),
xmlFields[0])
# Добавление переводов строк между полями
if xmlFields:
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.getprevious() is not None and
not (self.docObj.getTypeField(
node.getprevious()) in ("br", "comment"))):
insertBefore(xmlArea, self.docObj.createField("br",
[], "", [],
False,
False),
node)
# Если последним полем BR, удаляем его
if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br":
if not xmlFields[-1].getnext():
xmlArea.remove(xmlFields[-1])
# Если предыдущим полем не (BR или комментарий) - добавляем BR
if (xmlArea.getprevious() is not None and
not (self.docObj.getTypeField(
xmlArea.getprevious()) == "br" or
self.docObj.getTypeField(
xmlArea.getprevious()) == "comment")):
parentNode = xmlArea.getparent()
insertBefore(parentNode, self.docObj.createField(
"br", [], "", [], False, False), xmlArea)
# Если есть предыдущее поле, и поле предыдущеее предыдущему
# не равно BR или комментарий то добавляем BR
if xmlArea.getprevious() is not None:
prPrSibling = xmlArea.getprevious().getprevious()
if (prPrSibling is not None and
not (self.docObj.getTypeField(
prPrSibling) == "br" or
self.docObj.getTypeField(
prPrSibling) == "comment")):
parentNode = xmlArea.getparent()
insertBefore(parentNode, self.docObj.createField(
"br", [], "", [], False, False), xmlArea)
# Если после есть BR а за ним ничего нет, удаляем BR
if (xmlArea.getnext() is not None and
self.docObj.getTypeField(xmlArea.getnext()) == "br"):
if xmlArea.getnext().getnext() is None:
parentNode = xmlArea.getnext().getparent()
parentNode.remove(xmlArea.getnext())
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.set("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 = str_to_xml_doc(self.text)
except Exception as e:
print(e)
self.setError(_("The template content is not XML"))
return False
self.rootNode = self.doc.getroottree().getroot()
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 у всех нод
"""
childNodes = list(xmlNode)
if "action" in xmlNode.keys():
nAction = xmlNode.get("action")
if nAction not in ("join", "replace", "drop"):
textError = _("In the text of the XML template, "
"reserved attribute 'action' comes with an "
"incorrect value.\n"
"Valid values of the 'action' attribute are: "
'(action="join", action="replace",'
'action="drop")')
self.setError(textError)
return False
xmlNode.removeAttribute("action")
if nAction == "drop":
parentNode = xmlNode.getparent()
if parentNode:
parentNode.remove(xmlNode)
if childNodes:
for node in childNodes:
if not self._removeDropNodesAndAttrAction(node):
return False
return True
def postXML(self):
"""Последующая постобработка XML"""
# Удаляем теги action и удаляемые ноды
self._removeDropNodesAndAttrAction(self.bodyNode)
def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True):
"""Объединение корневой ноды шаблона и корневой ноды файла"""
xmlNode = xmlNewNode
childNodes = list(xmlNode)
nextOldNode = xmlOldNode
# if xmlNode.nodeType == xmlNode.ELEMENT_NODE:
n = xmlNode
nType = ''
nValue = ''
nAction = ''
attrName = ''
attrType = ''
path = n.tag
if "name" in n.keys():
nName = n.get("name")
attrName = "attribute::name='%s'" % nName
if "type" in n.keys():
nType = n.get("type")
attrType = "attribute::type='%s'" % nType
if "value" in n.keys():
nValue = n.get("value")
if "action" in n.keys():
nAction = n.get("action")
if not nAction in ("join", "replace", "drop"):
textError = _(
"In the text of the XML template, "
"reserved attribute 'action' comes with an "
"incorrect value.\n"
"Valid values of the 'action' attribute are: "
'(action="join", action="replace", action="drop")')
self.setError(textError)
return False
if xmlNewNode.getparent() is not None:
strAttr = [attrName, attrType]
findAttr = [x for x in strAttr if x]
findAttrStr = ''
if findAttr:
strAttr = ' and '.join(findAttr)
findAttrStr = "[%s]" % strAttr
findPath = "child::%s%s" % (path, findAttrStr)
# Рабочая нода
if flagRootNode:
workNode = xmlOldNode.getparent()
else:
workNode = xmlOldNode
oldNodes = xpath.Evaluate(findPath, workNode)
# Новая нода список
flagArray = nType == "array"
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 '
'the root node')
self.setError(textError)
return False
if oldNodes:
if len(oldNodes) > 1:
textError = _("Ambiguity in this template: "
"the same nodes are on a same level")
self.setError(textError)
return False
nextOldNode = oldNodes[0]
# Замещаем ноду в случае массива
if flagArray and not flagDrop:
replaceXmlNode = deepcopy(xmlNode)
if nAction:
del replaceXmlNode.attrib["action"]
workNode.replace(nextOldNode, replaceXmlNode)
flagJoin = False
flagReplace = False
childNodes = False
# Объединение нод
if flagJoin:
if "value" in nextOldNode.keys():
oValue = nextOldNode.get("value")
if nValue != oValue:
nextOldNode.set("value", nValue)
# Замещение ноды
elif flagReplace:
replaceXmlNode = deepcopy(xmlNode)
if not self._removeDropNodesAndAttrAction(
replaceXmlNode):
return False
workNode.replace(nextOldNode, replaceXmlNode)
childNodes = False
# Удаление ноды
elif flagDrop:
workNode.remove(nextOldNode)
childNodes = False
else:
# Добавление ноды
childNodes = False
if not flagDrop:
appendXmlNode = deepcopy(xmlNode)
if not self._removeDropNodesAndAttrAction(
appendXmlNode):
return False
workNode.append(appendXmlNode)
if isinstance(childNodes, Iterable):
for node in childNodes:
if not self._join(node, nextOldNode, False):
return False
return True
def joinDoc(self, doc):
"""Объединение документа шаблона и документа файла"""
if self.doc is None:
self.setError(_("The text file is not XML"))
return False
if doc is None:
self.setError(_("The text file is not XML"))
return False
# Импортируем корневую ноду нового документа в текущий документ
# newImportBodyNode = self.doc.importNode(doc.documentElement, True)
# Объединение корневой ноды шаблона и корневой ноды файла
if not self._join(doc, self.bodyNode):
return False
return True
def getConfig(self):
"""Получение текстового файла из XML документа"""
data = xml_to_str(self.doc).split("\n")
data = [x for x in data if x.strip()]
data.insert(0, '<?xml version="1.0" encoding="UTF-8"?>\n')
return "\n".join(data).replace("\t", " ")
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 = str_to_xml_doc(self.text)
except:
self.setError(_("Can not text profile is XML"))
return False
self.rootNode = self.doc.getroottree().getroot()
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 = list(xmlNode)
nextOldNode = xmlOldNode
n = xmlNode
flagArray = False
nValue = ''
nAction = ''
attrName = ''
path = n.tag
if path == "items":
flagArray = True
if not flagArray:
if "name" in n.keys():
nName = n.get("name")
attrName = "attribute::name='%s'" % nName
if "value" in n.keys():
nValue = n.get("value")
if "action" in n.keys():
nAction = n.get("action")
if not nAction in ("join", "replace", "drop"):
textError = _("In the text of the XML template, "
"reserved attribute 'action' comes with an "
"incorrect value.\n"
"Valid values of the 'action' attribute are: "
'(action="join", action="replace", '
'action="drop")')
self.setError(textError)
return False
if xmlNewNode.getparent() is not None:
findAttrStr = ""
if attrName:
findAttrStr = "[%s]" % attrName
findPath = "child::%s%s" % (path, findAttrStr)
# Рабочая нода
if flagRootNode:
workNode = xmlOldNode.getparent()
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 the 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 = _("Ambiguity in this template: the "
"same nodes are on a same 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 = deepcopy(xmlNode)
if nAction:
del replaceXmlNode.attrib["action"]
workNode.replace(nextOldNode, replaceXmlNode)
flagJoin = False
flagReplace = False
childNodes = False
# Объединение нод
if flagJoin:
if "value" in nextOldNode.keys():
oValue = nextOldNode.get("value")
if nValue != oValue:
nextOldNode.set("value", nValue)
# Замещение ноды
elif flagReplace:
replaceXmlNode = deepcopy(xmlNode)
if not self._removeDropNodesAndAttrAction(
replaceXmlNode):
return False
workNode.replace(nextOldNode, replaceXmlNode)
childNodes = False
# Удаление ноды
elif flagDrop:
workNode.remove(nextOldNode)
childNodes = False
else:
flagAppend = True
flagDrop = False
if flagAppend and not flagDrop:
# Добавление ноды
childNodes = False
if not flagDrop:
appendXmlNode = deepcopy(xmlNode)
if not self._removeDropNodesAndAttrAction(
appendXmlNode):
return False
workNode.append(appendXmlNode)
if isinstance(childNodes, Iterable):
for node in childNodes:
levelNumber += 1
if not self._join(node, nextOldNode, False, levelNumber):
return False
levelNumber -= 1
return True
def join(self, xml_xfceObj):
"""Объединяем конфигурации"""
if isinstance(xml_xfceObj, xml_xfcepanel):
try:
self.joinDoc(xml_xfceObj.doc)
except Exception:
self.setError(_("Failed to join the template"))
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")
# регулярное выражения для поиска \t в начале строки (преобразование xml)
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 = str_to_xml_doc(self.text)
except Exception:
self.setError(_("Can not text profile is XML"))
return False
self.rootNode = self.doc.getroottree().getroot()
self.bodyNode = self.rootNode #? a mistake, maybe?
return self.doc
def cmpListsNodesEntry(self, listXmlA, listXmlB):
"""Сравнение содержимого двух списков XML нод"""
def getTextsNodes(listXml):
for node in (deepcopy(x)
for x in listXml):# if x.nodeType == x.ELEMENT_NODE):
if "mtime" in node.keys():
# node.removeAttribute("mtime")
del node.attrib["mtime"]
text = node.toxml()
yield text.replace(" ", "").replace("\t", "").replace("\n", "")
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 = list(xmlNode)
nextOldNode = xmlOldNode
# if xmlNode.nodeType == xmlNode.ELEMENT_NODE:
n = xmlNode
tagName = n.tag
nAction = ''
nType = ''
nValue = ''
nSchema = ''
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><%(tag)s>..</%(tag)s></gconf>')") % {'tag': tagName})
return False
if "name" not in n.keys():
self.setError(
_("Attribute \"name\" not found in the tag entry"))
return False
flagType = "type" in n.keys()
flagValue = False
flagSchema = "schema" in n.keys()
if flagSchema:
nSchema = n.get("schema")
if not flagType and not flagSchema:
self.setError(
_("Attribute \"type\" not found in the tag entry"))
return False
nName = n.get("name")
attrName = "attribute::name='%s'" % nName
if flagType:
flagValue = "value" in n.keys()
nType = n.get("type")
# Проверка правильности аттрибута type
if nType not in self.supportEntryTypes:
self.setError(_("Incorrect \"type\" attribute "
": <entry type=\"%s\"/>") % nType)
return False
if flagValue:
nValue = n.get("value")
if "action" in n.keys():
nAction = n.get("action")
if nAction not in ("join", "replace", "drop"):
textError = _("In the text of the XML template, "
"reserved attribute 'action' comes with an "
"incorrect value.\n"
"Valid values of the 'action' attribute are: "
'(action="join", action="replace",'
'action="drop")')
self.setError(textError)
return False
if xmlNewNode.getparent():
findAttrStr = ""
if attrName:
findAttrStr = "[%s]" % attrName
findPath = "child::%s%s" % (tagName, findAttrStr)
# Рабочая нода
if flagRootNode:
workNode = xmlOldNode.getparent()
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 '
'the root node')
self.setError(textError)
return False
if oldNodes:
if len(oldNodes) > 1:
textError = _("Ambiguity in this template: the same "
"nodes are on a same level")
self.setError(textError)
return False
nextOldNode = oldNodes[0]
# Объединение нод
if flagJoin:
if flagType and flagValue:
flagChange = False
if "value" in nextOldNode.keys():
oValue = nextOldNode.get("value")
if nValue != oValue:
flagChange = True
else:
flagChange = True
if flagChange:
nextOldNode.set("mtime", self.currentTime)
nextOldNode.set("value", nValue)
elif flagSchema:
flagChange = False
if "schema" in nextOldNode.keys():
oSchema = nextOldNode.get("schema")
if nSchema != oSchema:
flagChange = True
else:
flagChange = True
if flagChange:
nextOldNode.set("mtime", self.currentTime)
nextOldNode.set("schema", nSchema)
# Замещение ноды
elif flagReplace:
replaceXmlNode = deepcopy(xmlNode)
# Сравнение содержимого нод
if not self.cmpListsNodesEntry([replaceXmlNode],
[nextOldNode]):
replaceXmlNode.set("mtime",
self.currentTime)
if not self._removeDropNodesAndAttrAction(
replaceXmlNode):
return False
workNode.replace(nextOldNode, replaceXmlNode)
childNodes = False
# Удаление ноды
elif flagDrop:
workNode.remove(nextOldNode)
childNodes = False
else:
# Добавление ноды
childNodes = False
if not flagDrop:
appendXmlNode = deepcopy(xmlNode)
appendXmlNode.set("mtime", self.currentTime)
if not self._removeDropNodesAndAttrAction(
appendXmlNode):
return False
workNode.append(appendXmlNode)
if isinstance(childNodes, Iterable):
for node in childNodes:
levelNumber += 1
if not self._join(node, nextOldNode, False, levelNumber):
return False
levelNumber -= 1
return True
def join(self, xml_gconfObj):
"""Объединяем конфигурации"""
# Получаем текущее время
self.currentTime = self.getCurrentTime()
if isinstance(xml_gconfObj, xml_gconf):
try:
self.joinDoc(xml_gconfObj.doc)
except Exception:
self.setError(_("Can not join profile"))
return False
return True
def getConfig(self):
"""Получение текстового файла из XML документа"""
def expandStartTabs(s):
if s.startswith("\t"):
res = self.reStartTabs.search(s)
if res:
return str(res.group(1)).replace("\t", " ") + res.group(2)
return s
else:
return s
data = xml_to_str(self.doc).split("\n")
data = [expandStartTabs(x) for x in data if x.strip()]
dataOut = []
z = 0
lenData = len(data)
lenM2 = lenData - 2
for i in range(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])
dataOut.insert(0, '<?xml version="1.0" encoding="UTF-8"?>\n')
return "\n".join(dataOut)