|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2008-2016 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.
|
|
|
from contextlib import contextmanager
|
|
|
|
|
|
import sys
|
|
|
import os
|
|
|
import errno
|
|
|
from calculate.lib.cl_xml import xpath
|
|
|
import time
|
|
|
import fcntl
|
|
|
|
|
|
from .utils.common import _error
|
|
|
from .cl_template import FormatFactory, TemplatesError
|
|
|
|
|
|
from calculate.lib.cl_lang import setLocalTranslate
|
|
|
|
|
|
_ = lambda x: x
|
|
|
setLocalTranslate('cl_lib3', sys.modules[__name__])
|
|
|
|
|
|
|
|
|
class iniParser(_error):
|
|
|
"""Класс для работы с ini файлами
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, iniFile=None, text=None):
|
|
|
# название ini файла
|
|
|
self.iniFile = iniFile or ""
|
|
|
# права создаваемого ini-файла
|
|
|
self.mode = 0o640
|
|
|
# Cоответствует ли формат файла нужному
|
|
|
self.checkIni = None
|
|
|
self.FD = None
|
|
|
self.readOnly = False
|
|
|
self.text = text
|
|
|
self.locked = False
|
|
|
self.formatFactory = FormatFactory(self)
|
|
|
|
|
|
def joinText(self, iniObj, xmlNewDoc):
|
|
|
"""Объединяет два документа"""
|
|
|
newRootNode = xmlNewDoc.documentElement
|
|
|
newBodyNode = xpath.Evaluate('child::body', newRootNode)[0]
|
|
|
newImportBodyNode = iniObj.doc.importNode(newBodyNode, True)
|
|
|
iniObj.docObj.joinBody(iniObj.docObj.body, newImportBodyNode)
|
|
|
# iniObj.docObj.insertBRtoBody(iniObj.docObj.body)
|
|
|
|
|
|
def setMode(self, mode):
|
|
|
"""установка прав создаваемого ini-файла"""
|
|
|
self.mode = mode
|
|
|
|
|
|
def lockfile(self, fd, fn, timeout=5, readonly=False):
|
|
|
"""
|
|
|
Блокировка файла с таймаутом
|
|
|
"""
|
|
|
if self.locked:
|
|
|
return True
|
|
|
for i in range(0, timeout):
|
|
|
try:
|
|
|
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
|
return True
|
|
|
except IOError as e:
|
|
|
# dirty hack for lock ini.env on cifs
|
|
|
if e.errno == errno.EBADF and readonly:
|
|
|
return False
|
|
|
if e.errno != errno.EAGAIN:
|
|
|
raise e
|
|
|
time.sleep(1)
|
|
|
else:
|
|
|
raise TemplatesError(_("Lock timeout of %s") % fn)
|
|
|
|
|
|
@property
|
|
|
def iniFile_lock(self):
|
|
|
return self.iniFile + ".lock~"
|
|
|
|
|
|
def wait_global_lock(self, timeout=10):
|
|
|
lockfn = self.iniFile_lock
|
|
|
for i in range(0, timeout):
|
|
|
if self.locked or not os.path.exists(lockfn):
|
|
|
break
|
|
|
time.sleep(1)
|
|
|
|
|
|
@contextmanager
|
|
|
def lock(self):
|
|
|
lockfn = self.iniFile_lock
|
|
|
lockf = open(lockfn, "w+")
|
|
|
self.lockfile(lockf.fileno(), lockfn)
|
|
|
self.locked = True
|
|
|
try:
|
|
|
yield
|
|
|
finally:
|
|
|
self.locked = False
|
|
|
os.unlink(lockfn)
|
|
|
|
|
|
def _open(self, mode):
|
|
|
self.wait_global_lock()
|
|
|
return open(self.iniFile, mode)
|
|
|
|
|
|
def openIniFile(self):
|
|
|
if not self.text is None:
|
|
|
return self.text
|
|
|
if not os.access(self.iniFile, os.R_OK):
|
|
|
return ""
|
|
|
self.FD = self._open("r")
|
|
|
self.lockfile(self.FD.fileno(), self.iniFile, readonly=True)
|
|
|
textIni = self.FD.read()
|
|
|
return textIni
|
|
|
|
|
|
def openRWIniFile(self):
|
|
|
if not os.access(self.iniFile, os.R_OK):
|
|
|
return ""
|
|
|
try:
|
|
|
self.FD = self._open("r+")
|
|
|
except (IOError, OSError):
|
|
|
self.FD = self._open("r")
|
|
|
self.readOnly = True
|
|
|
self.lockfile(self.FD.fileno(), self.iniFile)
|
|
|
textIni = self.FD.read()
|
|
|
return textIni
|
|
|
|
|
|
def writeIniFile(self, txtConfig):
|
|
|
if self.readOnly:
|
|
|
self.setError(_("Failed to write to file")
|
|
|
+ _(": ") + self.iniFile)
|
|
|
return False
|
|
|
if not os.path.exists(self.iniFile):
|
|
|
try:
|
|
|
# Создание файла
|
|
|
self.FD = self._open("w+")
|
|
|
self.lockfile(self.FD.fileno(), self.iniFile)
|
|
|
os.chmod(self.iniFile, self.mode)
|
|
|
except Exception:
|
|
|
self.setError(_("Failed to create the file") +
|
|
|
_(": ") + self.iniFile)
|
|
|
return False
|
|
|
if not self.FD:
|
|
|
self.setError(_("Failed to write to file")
|
|
|
+ _(": ") + self.iniFile)
|
|
|
return False
|
|
|
self.FD.truncate(0)
|
|
|
self.FD.seek(0)
|
|
|
self.FD.write(txtConfig)
|
|
|
self.FD.close()
|
|
|
self.FD = None
|
|
|
return True
|
|
|
|
|
|
def setVar(self, strHeader, dictVar):
|
|
|
"""Заменяет или добавляет область и переменные
|
|
|
|
|
|
Добавляет область в ini-файл или объединяет с существующей
|
|
|
strHeader - имя области
|
|
|
dictVar - словарь переменных
|
|
|
"""
|
|
|
textIni = self.openRWIniFile()
|
|
|
nameFomat = self.checkIniFile(textIni)
|
|
|
if not nameFomat:
|
|
|
return False
|
|
|
if type(strHeader) in (tuple, list):
|
|
|
# формат plasma
|
|
|
classObj = self.formatFactory.getClassObj("plasma")
|
|
|
else:
|
|
|
if nameFomat == "plasma":
|
|
|
self.setError(_("Trying to write a variable of 'samba' "
|
|
|
"format to file %s ('plasma' format)") \
|
|
|
% self.iniFile)
|
|
|
return False
|
|
|
# формат samba
|
|
|
classObj = self.formatFactory.getClassObj("samba")
|
|
|
# создаем объект
|
|
|
# и записываем в него содержимое ini-файла
|
|
|
objIni = classObj(textIni, self)
|
|
|
# создаем текст из строки заголовка и
|
|
|
# словаря переменных области
|
|
|
txtConfig = objIni.createTxtConfig(strHeader, dictVar)
|
|
|
# создаем объект и записываем в него текст
|
|
|
objIniAdd = classObj(txtConfig, self)
|
|
|
# объединяем объекты для получения результирующего текста
|
|
|
objIni.join(objIniAdd)
|
|
|
# получаем текст
|
|
|
txtConfig = objIni.getConfig().encode("UTF-8")
|
|
|
# записываем его в ini файл
|
|
|
if not self.writeIniFile(txtConfig):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def isEmptyFile(self, textIni):
|
|
|
"""Если файл пустой или содержит только комментарии - False
|
|
|
|
|
|
иначе - True
|
|
|
"""
|
|
|
if textIni.strip():
|
|
|
if filter(lambda x: x.strip(),
|
|
|
map(lambda x: x[0].split(";")[0],
|
|
|
map(lambda x: x.split("#"),
|
|
|
textIni.splitlines()))):
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
else:
|
|
|
return True
|
|
|
|
|
|
def checkIniFile(self, textIni):
|
|
|
"""Проверка на правильность формата файла"""
|
|
|
if self.checkIni is None:
|
|
|
# Ошибка
|
|
|
if textIni is False:
|
|
|
self.checkIni = False
|
|
|
return False
|
|
|
self.checkIni = "samba"
|
|
|
# В файле есть данные
|
|
|
if not self.isEmptyFile(textIni):
|
|
|
try:
|
|
|
objIni = self.formatFactory.getClassObj(
|
|
|
"plasma")(textIni, self)
|
|
|
except Exception:
|
|
|
self.setError(_("Incorrect file format") + _(": ") + \
|
|
|
self.iniFile)
|
|
|
self.checkIni = False
|
|
|
return self.checkIni
|
|
|
allAreas = objIni.docObj.getAllAreas()
|
|
|
for xmlArea in allAreas:
|
|
|
parentNode = xmlArea.parentNode
|
|
|
if parentNode and parentNode.tagName == "area":
|
|
|
self.checkIni = "plasma"
|
|
|
break
|
|
|
if self.checkIni == "samba":
|
|
|
objIni = self.formatFactory.getClassObj(
|
|
|
"samba")(textIni, self)
|
|
|
xmlBody = objIni.docObj.getNodeBody()
|
|
|
if not xmlBody.firstChild:
|
|
|
self.checkIni = False
|
|
|
return self.checkIni
|
|
|
|
|
|
def delVar(self, strHeader, nameVar):
|
|
|
"""Удаляем переменную из ini файла"""
|
|
|
delNameVar = "!%s" % nameVar
|
|
|
dictVar = {delNameVar: "del"}
|
|
|
res = self.setVar(strHeader, dictVar)
|
|
|
return res
|
|
|
|
|
|
def delArea(self, strHeader):
|
|
|
"""Удаляем область из ini файла"""
|
|
|
if type(strHeader) in (tuple, list):
|
|
|
# Формат plasma
|
|
|
delStrHeader = list(strHeader[:])
|
|
|
delStrHeader[-1] = "!%s" % delStrHeader[-1]
|
|
|
else:
|
|
|
# Формат samba
|
|
|
delStrHeader = "!%s" % strHeader
|
|
|
dictVar = {"del": "del"}
|
|
|
res = self.setVar(delStrHeader, dictVar)
|
|
|
return res
|
|
|
|
|
|
def getVar(self, strHeader, nameVar, checkExistVar=False):
|
|
|
"""Получаем значение переменной из ini-файла"""
|
|
|
textIni = self.openIniFile()
|
|
|
if self.FD:
|
|
|
self.FD.close()
|
|
|
self.FD = None
|
|
|
nameFomat = self.checkIniFile(textIni)
|
|
|
if not nameFomat:
|
|
|
return False
|
|
|
formatPlasma = False
|
|
|
if type(strHeader) in (tuple, list):
|
|
|
# формат plasma
|
|
|
classObj = self.formatFactory.getClassObj("plasma")
|
|
|
formatPlasma = True
|
|
|
else:
|
|
|
if nameFomat == "plasma":
|
|
|
self.setError(_("Trying to fetch a variable of 'samba' "
|
|
|
"format from file %s ('plasma' format)") \
|
|
|
% self.iniFile)
|
|
|
return False
|
|
|
# формат samba
|
|
|
classObj = self.formatFactory.getClassObj("samba")
|
|
|
# создаем объект и записываем в него содержимое ini-файла
|
|
|
objIni = classObj(textIni, self)
|
|
|
# получаем ноду body
|
|
|
xmlBody = objIni.docObj.getNodeBody()
|
|
|
flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
|
|
|
formatPlasma)
|
|
|
if flagFound and xmlBody:
|
|
|
if formatPlasma:
|
|
|
strHeader = strHeader[-1]
|
|
|
# находим в области переменную
|
|
|
res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody)
|
|
|
else:
|
|
|
res = False
|
|
|
if checkExistVar:
|
|
|
if res is False:
|
|
|
return False, ""
|
|
|
else:
|
|
|
return True, res
|
|
|
else:
|
|
|
if res is False:
|
|
|
return ""
|
|
|
else:
|
|
|
return res
|
|
|
|
|
|
def getLastNode(self, objIni, xmlBody, strHeader, formatPlasma):
|
|
|
"""Ищет область в XML в которой область с переменными"""
|
|
|
flagFound = True
|
|
|
if not strHeader:
|
|
|
flagFound = False
|
|
|
return flagFound, xmlBody
|
|
|
lenStrHeader = len(strHeader)
|
|
|
if formatPlasma and lenStrHeader > 0:
|
|
|
xmlAreas = [xmlBody]
|
|
|
for i in range(lenStrHeader - 1):
|
|
|
flagFound = False
|
|
|
for xmlArea in xmlAreas:
|
|
|
xmlAreas = objIni.docObj.getArea(strHeader[i], xmlArea)
|
|
|
if xmlAreas:
|
|
|
flagFound = True
|
|
|
break
|
|
|
if xmlAreas:
|
|
|
xmlBody = xmlAreas[0]
|
|
|
return flagFound, xmlBody
|
|
|
|
|
|
def getAreaVars(self, strHeader):
|
|
|
"""Получаем все переменнные области из ini-файла"""
|
|
|
textIni = self.openIniFile()
|
|
|
if self.FD:
|
|
|
self.FD.close()
|
|
|
self.FD = None
|
|
|
nameFomat = self.checkIniFile(textIni)
|
|
|
if not nameFomat:
|
|
|
return False
|
|
|
formatPlasma = False
|
|
|
if type(strHeader) in (tuple, list):
|
|
|
# формат plasma
|
|
|
classObj = self.formatFactory.getClassObj("plasma")
|
|
|
formatPlasma = True
|
|
|
else:
|
|
|
if nameFomat == "plasma":
|
|
|
self.setError(_("Trying to fetch a variable of 'samba' "
|
|
|
"format from file %s ('plasma' format)") \
|
|
|
% self.iniFile)
|
|
|
return False
|
|
|
# формат samba
|
|
|
classObj = self.formatFactory.getClassObj("samba")
|
|
|
# создаем объект типа samba и записываем в него содержимое ini-файла
|
|
|
objIni = classObj(textIni, self)
|
|
|
# получаем ноду body
|
|
|
xmlBody = objIni.docObj.getNodeBody()
|
|
|
flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
|
|
|
formatPlasma)
|
|
|
if flagFound and xmlBody:
|
|
|
if formatPlasma:
|
|
|
strHeader = strHeader[-1]
|
|
|
# если находим область то выдаем словарем все переменные иначе False
|
|
|
res = objIni.docObj.getAreaFields(strHeader, xmlBody, allVars=True)
|
|
|
else:
|
|
|
res = False
|
|
|
if res is False:
|
|
|
return {}
|
|
|
else:
|
|
|
return res
|
|
|
|
|
|
def getAllSectionNames(self):
|
|
|
"""Получаем все имена секций определенных в ini файле
|
|
|
|
|
|
Если формат ini файла plasma то имя секции -
|
|
|
имена нескольких секций через запятую
|
|
|
"""
|
|
|
textIni = self.openIniFile()
|
|
|
if self.FD:
|
|
|
self.FD.close()
|
|
|
self.FD = None
|
|
|
nameFomat = self.checkIniFile(textIni)
|
|
|
if not nameFomat:
|
|
|
return False
|
|
|
if nameFomat == "samba":
|
|
|
# создаем объект типа samba и записываем в него содержимое ini-файла
|
|
|
objIni = self.formatFactory.getClassObj("samba")(textIni, self)
|
|
|
elif nameFomat == "plasma":
|
|
|
# создаем объект типа plasma и записываем в него содержимое
|
|
|
# ini-файла
|
|
|
objIni = self.formatFactory.getClassObj("plasma")(textIni, self)
|
|
|
else:
|
|
|
return []
|
|
|
xmlNodes = objIni.docObj.getAllAreas()
|
|
|
# Имена секций ini файла
|
|
|
namesSection = []
|
|
|
if nameFomat == "plasma":
|
|
|
for xmlNode in xmlNodes:
|
|
|
nSect = objIni.docObj.getNameArea(xmlNode)
|
|
|
if nSect:
|
|
|
namesSect = [nSect]
|
|
|
parentNode = xmlNode.parentNode
|
|
|
while parentNode != objIni.docObj.body:
|
|
|
nameSect = objIni.docObj.getNameArea(parentNode)
|
|
|
if nameSect:
|
|
|
namesSect.append(nameSect)
|
|
|
parentNode = parentNode.parentNode
|
|
|
else:
|
|
|
return []
|
|
|
namesSection.append(",".join(reversed(namesSect)))
|
|
|
elif nameFomat == "samba":
|
|
|
# получаем ноду body
|
|
|
for xmlNode in xmlNodes:
|
|
|
nSect = objIni.docObj.getNameArea(xmlNode)
|
|
|
if nSect:
|
|
|
namesSection.append(nSect)
|
|
|
return namesSection
|