Commit 469c2c03 authored by Julius Künzel's avatar Julius Künzel 💬
Browse files

Ensure files are open before reading to QDomDocument

This is required by future Qt versions

To reduce code duplication this introduces a Xml::docContentFromFile
function

TODO: This commit does this only for a few places yet, others should
follow
parent e16fe038
Pipeline #244132 failed with stage
in 2 minutes and 56 seconds
......@@ -1840,28 +1840,30 @@ void Bin::slotReloadClip()
QString path = currentItem->url();
QFile f(path);
QDomDocument doc;
doc.setContent(&f, false);
f.close();
DocumentChecker d(QUrl::fromLocalFile(path), doc);
if (!d.hasErrorInClips() && doc.documentElement().hasAttribute(QStringLiteral("modified"))) {
QString backupFile = path + QStringLiteral(".backup");
KIO::FileCopyJob *copyjob = KIO::file_copy(QUrl::fromLocalFile(path), QUrl::fromLocalFile(backupFile));
if (copyjob->exec()) {
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::error(this, i18n("Unable to write to file %1", path));
} else {
QTextStream out(&f);
if (!Xml::docContentFromFile(doc, path, false)) {
DocumentChecker d(QUrl::fromLocalFile(path), doc);
if (!d.hasErrorInClips() && doc.documentElement().hasAttribute(QStringLiteral("modified"))) {
QString backupFile = path + QStringLiteral(".backup");
KIO::FileCopyJob *copyjob = KIO::file_copy(QUrl::fromLocalFile(path), QUrl::fromLocalFile(backupFile));
if (copyjob->exec()) {
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::error(this, i18n("Unable to write to file %1", path));
} else {
QTextStream out(&f);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
out.setCodec("UTF-8");
out.setCodec("UTF-8");
#endif
out << doc.toString();
f.close();
KMessageBox::information(
this,
i18n("Your project file was modified by Kdenlive.\nTo make sure you do not lose data, a backup copy called %1 was created.",
backupFile));
out << doc.toString();
f.close();
KMessageBox::information(
this,
i18n("Your project file was modified by Kdenlive.\nTo make sure you do not lose data, a backup copy called %1 was created.",
backupFile));
}
}
}
} else {
KMessageBox::error(this, i18n("Unable to parse file %1", path));
}
}
currentItem->reloadProducer(false, false, true);
......@@ -4330,9 +4332,9 @@ void Bin::showTitleWidget(const std::shared_ptr<ProjectClip> &clip)
QDomDocument doc;
QString xmldata = clip->getProducerProperty(QStringLiteral("xmldata"));
if (xmldata.isEmpty() && QFile::exists(path)) {
QFile file(path);
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, path, false)) {
return;
}
} else {
doc.setContent(xmldata);
}
......
......@@ -91,29 +91,25 @@ QDomDocument ClipCreator::getXmlFromUrl(const QString &path)
} else if (type.inherits(QStringLiteral("application/x-kdenlivetitle"))) {
// opening a title file
QDomDocument txtdoc(QStringLiteral("titledocument"));
QFile txtfile(path);
if (txtfile.open(QIODevice::ReadOnly) && txtdoc.setContent(&txtfile)) {
txtfile.close();
// extract embedded images
QDomNodeList items = txtdoc.elementsByTagName(QStringLiteral("content"));
for (int j = 0; j < items.count(); ++j) {
QDomElement content = items.item(j).toElement();
if (content.hasAttribute(QStringLiteral("base64"))) {
QString titlesFolder = pCore->currentDoc()->projectDataFolder() + QStringLiteral("/titles/");
QString imgPath = TitleDocument::extractBase64Image(titlesFolder, content.attribute(QStringLiteral("base64")));
if (!imgPath.isEmpty()) {
content.setAttribute(QStringLiteral("url"), imgPath);
content.removeAttribute(QStringLiteral("base64"));
}
if (!Xml::docContentFromFile(txtdoc, path, false)) {
return QDomDocument();
}
// extract embedded images
QDomNodeList items = txtdoc.elementsByTagName(QStringLiteral("content"));
for (int j = 0; j < items.count(); ++j) {
QDomElement content = items.item(j).toElement();
if (content.hasAttribute(QStringLiteral("base64"))) {
QString titlesFolder = pCore->currentDoc()->projectDataFolder() + QStringLiteral("/titles/");
QString imgPath = TitleDocument::extractBase64Image(titlesFolder, content.attribute(QStringLiteral("base64")));
if (!imgPath.isEmpty()) {
content.setAttribute(QStringLiteral("url"), imgPath);
content.removeAttribute(QStringLiteral("base64"));
}
}
prod = createProducer(xml, ClipType::Text, path, QString(), -1, QString());
QString titleData = txtdoc.toString();
prod.setAttribute(QStringLiteral("xmldata"), titleData);
} else {
txtfile.close();
return QDomDocument();
}
prod = createProducer(xml, ClipType::Text, path, QString(), -1, QString());
QString titleData = txtdoc.toString();
prod.setAttribute(QStringLiteral("xmldata"), titleData);
} else {
// it is a "normal" file, just use a producer
prod = xml.createElement(QStringLiteral("producer"));
......@@ -173,8 +169,7 @@ QString ClipCreator::createTitleTemplate(const QString &path, const QString &tex
// We try to retrieve duration for template
int duration = 0;
QDomDocument titledoc;
QFile txtfile(path);
if (txtfile.open(QIODevice::ReadOnly) && titledoc.setContent(&txtfile)) {
if (Xml::docContentFromFile(titledoc, path, false)) {
if (titledoc.documentElement().hasAttribute(QStringLiteral("duration"))) {
duration = titledoc.documentElement().attribute(QStringLiteral("duration")).toInt();
} else {
......@@ -182,7 +177,6 @@ QString ClipCreator::createTitleTemplate(const QString &path, const QString &tex
duration = titledoc.documentElement().attribute(QStringLiteral("out")).toInt();
}
}
txtfile.close();
// Duration not found, we fall-back to defaults
if (duration == 0) {
......
......@@ -37,10 +37,11 @@ Generators::Generators(const QString &path, QWidget *parent)
, m_container(nullptr)
, m_preview(nullptr)
{
QFile file(path);
QDomDocument doc;
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, path, false)) {
return;
}
QDomElement base = doc.documentElement();
if (base.tagName() == QLatin1String("generator")) {
QString generatorTag = base.attribute(QStringLiteral("tag"));
......@@ -129,9 +130,10 @@ QPair<QString, QString> Generators::parseGenerator(const QString &path, const QS
{
QPair<QString, QString> result;
QDomDocument doc;
QFile file(path);
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, path, false)) {
return result;
}
QDomElement base = doc.documentElement();
if (base.tagName() == QLatin1String("generator")) {
QString generatorTag = base.attribute(QStringLiteral("tag"));
......
......@@ -1675,10 +1675,10 @@ void RenderWidget::parseScriptFiles()
}
for (int i = 0; i < scriptFiles.size(); ++i) {
QUrl scriptpath = QUrl::fromLocalFile(projectFolder.absoluteFilePath(scriptFiles.at(i)));
QFile f(scriptpath.toLocalFile());
QDomDocument doc;
doc.setContent(&f, false);
f.close();
if (!Xml::docContentFromFile(doc, scriptpath.toLocalFile(), false)) {
continue;
}
QDomElement consumer = doc.documentElement().firstChildElement(QStringLiteral("consumer"));
if (consumer.isNull()) {
continue;
......
......@@ -1108,18 +1108,10 @@ void KdenliveDoc::updateProjectFolderPlacesEntry()
// static
double KdenliveDoc::getDisplayRatio(const QString &path)
{
QFile file(path);
QDomDocument doc;
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(KDENLIVE_LOG) << "ERROR, CANNOT READ: " << path;
return 0;
}
if (!doc.setContent(&file)) {
qCWarning(KDENLIVE_LOG) << "ERROR, CANNOT READ: " << path;
file.close();
if (!Xml::docContentFromFile(doc, path, false)) {
return 0;
}
file.close();
QDomNodeList list = doc.elementsByTagName(QStringLiteral("profile"));
if (list.isEmpty()) {
return 0;
......
......@@ -56,10 +56,11 @@ Mlt::Properties *EffectsRepository::getMetadata(const QString &effectId) const
void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unordered_map<QString, Info> &customAssets) const
{
QFile file(file_name);
QDomDocument doc;
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, file_name, false)) {
return;
}
QDomElement base = doc.documentElement();
if (base.tagName() == QLatin1String("effectgroup")) {
QDomNodeList effects = base.elementsByTagName(QStringLiteral("effect"));
......@@ -162,7 +163,7 @@ void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unor
if (effectFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
effectFile.write(doc.toString().toUtf8());
}
file.close();
effectFile.close();
}
} else if (type == QLatin1String("text")) {
result.type = AssetListType::AssetType::Text;
......@@ -288,10 +289,11 @@ QPair<QStringList, QStringList> EffectsRepository::fixDeprecatedEffects()
QPair<QString, QString> EffectsRepository::fixCustomAssetFile(const QString &path)
{
QPair<QString, QString> results;
QFile file(path);
QDomDocument doc;
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, path, false)) {
return results;
}
QDomElement base = doc.documentElement();
if (base.tagName() == QLatin1String("effectgroup")) {
// Groups not implemented
......@@ -375,6 +377,7 @@ QPair<QString, QString> EffectsRepository::fixCustomAssetFile(const QString &pat
if (effectAdjusted) {
QDir dir(QFileInfo(path).absoluteDir());
dir.cd(QStringLiteral("legacy"));
QFile file(path);
if (!file.copy(dir.absoluteFilePath(QFileInfo(file).fileName()))) {
// Cannot copy the backup file
qWarning() << "Could not copy old effect to" << dir.absoluteFilePath(QFileInfo(file).fileName());
......
......@@ -8,6 +8,7 @@
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
#include "renderpresetmodel.hpp"
#include "xml/xml.hpp"
#include <KLocalizedString>
#include <KMessageBox>
#include <QDir>
......@@ -115,9 +116,9 @@ void RenderPresetRepository::refresh(bool fullRefresh)
void RenderPresetRepository::parseFile(const QString &exportFile, bool editable)
{
QDomDocument doc;
QFile file(exportFile);
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, exportFile, false)) {
return;
}
QDomElement documentElement;
QDomNodeList groups = doc.elementsByTagName(QStringLiteral("group"));
......
......@@ -22,6 +22,7 @@
#include "profiles/profilemodel.hpp"
#include "titler/patternsmodel.h"
#include "widgets/timecodedisplay.h"
#include "xml/xml.hpp"
#include <cmath>
......@@ -2138,12 +2139,12 @@ void TitleWidget::loadTitle(QUrl url)
}
m_scene->clearTextSelection();
QDomDocument doc;
QFile file(url.toLocalFile());
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, url.toLocalFile(), false)) {
return;
}
setXml(doc);
updateGuides(0);
m_projectTitlePath = QFileInfo(file).dir().absolutePath();
m_projectTitlePath = QFileInfo(url.toLocalFile()).dir().absolutePath();
KRecentDirs::add(QStringLiteral(":KdenliveProjectsTitles"), m_projectTitlePath);
}
}
......
......@@ -49,10 +49,10 @@ Mlt::Properties *TransitionsRepository::getMetadata(const QString &assetId) cons
void TransitionsRepository::parseCustomAssetFile(const QString &file_name, std::unordered_map<QString, Info> &customAssets) const
{
QFile file(file_name);
QDomDocument doc;
doc.setContent(&file, false);
file.close();
if (!Xml::docContentFromFile(doc, file_name, false)) {
return;
}
QDomElement base = doc.documentElement();
QDomNodeList transitions = doc.elementsByTagName(QStringLiteral("transition"));
......
......@@ -5,8 +5,25 @@
#include "xml.hpp"
#include <QDebug>
#include <QFile>
// static
bool Xml::docContentFromFile(QDomDocument &doc, const QString &fileName, bool namespaceProcessing)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Failed to open file" << file.fileName() << "for reading";
return false;
}
if (!doc.setContent(&file, namespaceProcessing)) {
qWarning() << "Failed to parse file" << file.fileName() << "to QDomDocument";
file.close();
return false;
}
file.close();
return true;
}
QString Xml::getSubTagContent(const QDomElement &element, const QString &tagName)
{
QVector<QDomNode> nodeList = getDirectChildrenByTagName(element, tagName);
......
......@@ -15,6 +15,20 @@
*/
namespace Xml {
/** @brief Set the content of the given @param doc from a file.
*
* It makes sure that the file is open for reading as required by Qt in the future (as of Qt5 2022-10-08)
*
* @param doc
* @param fileName
* @param namespaceProcessing parameter of QDomDocument::setContent(). If namespaceProcessing is true, the parser recognizes namespaces in the XML file and
* sets the prefix name, local name and namespace URI to appropriate values. If namespaceProcessing is false, the parser does no namespace processing when it
* reads the XML file.
*
* @returns false if an error occured while reading or parsing the file to the document
*/
bool docContentFromFile(QDomDocument &doc, const QString &fileName, bool namespaceProcessing);
/** @brief Returns the content of a given tag within the current DomElement.
For example, if your \@param element looks like <html><title>foo</title><head>bar</head></html>, passing \@tagName = "title" will return foo, and \@tagName
= "head" returns bar
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment