KSyntaxHighlighting

definitiondownloader.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: MIT
5*/
6
7#include "definitiondownloader.h"
8#include "definition.h"
9#include "ksyntaxhighlighting_logging.h"
10#include "ksyntaxhighlighting_version.h"
11#include "repository.h"
12
13#include <QDir>
14#include <QFile>
15#include <QNetworkAccessManager>
16#include <QNetworkReply>
17#include <QNetworkRequest>
18#include <QStandardPaths>
19#include <QTimer>
20#include <QXmlStreamReader>
21
22using namespace KSyntaxHighlighting;
23
24class KSyntaxHighlighting::DefinitionDownloaderPrivate
25{
26public:
28 Repository *repo;
30 QString downloadLocation;
31 int pendingDownloads;
32 bool needsReload;
33
34 void definitionListDownloadFinished(QNetworkReply *reply);
35 void updateDefinition(QXmlStreamReader &parser);
36 void downloadDefinition(const QUrl &url);
37 void downloadDefinitionFinished(QNetworkReply *reply);
38 void checkDone();
39};
40
41void DefinitionDownloaderPrivate::definitionListDownloadFinished(QNetworkReply *reply)
42{
43 const auto networkError = reply->error();
44 if (networkError != QNetworkReply::NoError) {
45 qCWarning(Log) << networkError;
46 Q_EMIT q->done(); // TODO return error
47 return;
48 }
49
50 QXmlStreamReader parser(reply);
51 while (!parser.atEnd()) {
52 switch (parser.readNext()) {
54 if (parser.name() == QLatin1String("Definition")) {
55 updateDefinition(parser);
56 }
57 break;
58 default:
59 break;
60 }
61 }
62
63 if (pendingDownloads == 0) {
64 Q_EMIT q->informationMessage(QObject::tr("All syntax definitions are up-to-date."));
65 }
66 checkDone();
67}
68
69void DefinitionDownloaderPrivate::updateDefinition(QXmlStreamReader &parser)
70{
71 const auto name = parser.attributes().value(QLatin1String("name"));
72 if (name.isEmpty()) {
73 return;
74 }
75
76 auto localDef = repo->definitionForName(name.toString());
77 if (!localDef.isValid()) {
78 Q_EMIT q->informationMessage(QObject::tr("Downloading new syntax definition for '%1'…", "@info").arg(name));
79 downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString()));
80 return;
81 }
82
83 const auto version = parser.attributes().value(QLatin1String("version"));
84 if (localDef.version() < version.toFloat()) {
85 Q_EMIT q->informationMessage(QObject::tr("Updating syntax definition for '%1' to version %2…", "@info").arg(name, version));
86 downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString()));
87 }
88}
89
90void DefinitionDownloaderPrivate::downloadDefinition(const QUrl &downloadUrl)
91{
92 if (!downloadUrl.isValid()) {
93 return;
94 }
95 auto url = downloadUrl;
96 if (url.scheme() == QLatin1String("http")) {
97 url.setScheme(QStringLiteral("https"));
98 }
99
100 QNetworkRequest req(url);
101 auto reply = nam->get(req);
102 QObject::connect(reply, &QNetworkReply::finished, q, [this, reply]() {
103 downloadDefinitionFinished(reply);
104 });
105 ++pendingDownloads;
106 needsReload = true;
107}
108
109void DefinitionDownloaderPrivate::downloadDefinitionFinished(QNetworkReply *reply)
110{
111 --pendingDownloads;
112
113 const auto networkError = reply->error();
114 if (networkError != QNetworkReply::NoError) {
115 qCWarning(Log) << "Failed to download definition file" << reply->url() << networkError;
116 checkDone();
117 return;
118 }
119
120 // handle redirects
121 // needs to be done manually, download server redirects to unsafe http links
122 const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
123 if (!redirectUrl.isEmpty()) {
124 downloadDefinition(reply->url().resolved(redirectUrl));
125 checkDone();
126 return;
127 }
128
129 QFile file(downloadLocation + QLatin1Char('/') + reply->url().fileName());
130 if (!file.open(QFile::WriteOnly)) {
131 qCWarning(Log) << "Failed to open" << file.fileName() << file.error();
132 } else {
133 file.write(reply->readAll());
134 }
135 checkDone();
136}
137
138void DefinitionDownloaderPrivate::checkDone()
139{
140 if (pendingDownloads == 0) {
141 if (needsReload) {
142 repo->reload();
143 }
144
146 }
147}
148
150 : QObject(parent)
151 , d(new DefinitionDownloaderPrivate())
152{
153 Q_ASSERT(repo);
154
155 d->q = this;
156 d->repo = repo;
157 d->nam = new QNetworkAccessManager(this);
158 d->pendingDownloads = 0;
159 d->needsReload = false;
160
161 d->downloadLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/org.kde.syntax-highlighting/syntax");
162 QDir().mkpath(d->downloadLocation);
163 Q_ASSERT(QFile::exists(d->downloadLocation));
164}
165
169
171{
172 const QString url = QLatin1String("https://www.kate-editor.org/syntax/update-") + QString::number(KSYNTAXHIGHLIGHTING_VERSION_MAJOR) + QLatin1Char('.')
173 + QString::number(KSYNTAXHIGHLIGHTING_VERSION_MINOR) + QLatin1String(".xml");
174 auto req = QNetworkRequest(QUrl(url));
176 auto reply = d->nam->get(req);
177 QObject::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
178 d->definitionListDownloadFinished(reply);
179 });
180}
181
182#include "moc_definitiondownloader.cpp"
Helper class to download definition file updates.
void done()
This signal is emitted when there are no pending downloads anymore.
DefinitionDownloader(Repository *repo, QObject *parent=nullptr)
Constructor.
void start()
Starts the update procedure.
void informationMessage(const QString &msg)
Prints the information about the current state of the definition files.
Syntax highlighting repository.
Definition repository.h:122
void reload()
Reloads the repository.
Q_INVOKABLE KSyntaxHighlighting::Definition definitionForName(const QString &defName) const
Returns the Definition named defName.
char * toString(const EngineQuery &query)
KDB_EXPORT KDbVersionInfo version()
QString name(StandardAction id)
Syntax highlighting engine for Kate syntax definitions.
bool mkpath(const QString &dirPath) const const
bool exists() const const
QByteArray readAll()
QNetworkReply * get(const QNetworkRequest &request)
QVariant attribute(QNetworkRequest::Attribute code) const const
NetworkError error() const const
QUrl url() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString tr(const char *sourceText, const char *disambiguation, int n)
QString writableLocation(StandardLocation type)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString fileName(ComponentFormattingOptions options) const const
bool isValid() const const
QUrl resolved(const QUrl &relative) const const
QUrl toUrl() const const
QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const const
QXmlStreamAttributes attributes() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:17:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.