KSyntaxHighlighting

definitiondownloader.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Volker Krause <[email protected]>
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 
22 using namespace KSyntaxHighlighting;
23 
24 class KSyntaxHighlighting::DefinitionDownloaderPrivate
25 {
26 public:
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 
41 void 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 
69 void 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'...").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...").arg(name, version));
86  downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString()));
87  }
88 }
89 
90 void 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 
109 void 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 
138 void 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 
167 {
168 }
169 
171 {
172  const QString url = QLatin1String("https://www.kate-editor.org/syntax/update-") + QString::number(SyntaxHighlighting_VERSION_MAJOR) + QLatin1Char('.')
173  + QString::number(SyntaxHighlighting_VERSION_MINOR) + QLatin1String(".xml");
174  auto req = QNetworkRequest(QUrl(url));
176  auto reply = d->nam->get(req);
177  QObject::connect(reply, &QNetworkReply::finished, this, [=]() {
178  d->definitionListDownloadFinished(reply);
179  });
180 }
DefinitionDownloader(Repository *repo, QObject *parent=nullptr)
Constructor.
QString number(int n, int base)
QUrl toUrl() const const
QStringRef value(const QString &namespaceUri, const QString &name) const const
Helper class to download definition file updates.
QNetworkReply::NetworkError error() const const
QString writableLocation(QStandardPaths::StandardLocation type)
bool exists() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setScheme(const QString &scheme)
QUrl url() const const
bool isValid() const const
Syntax highlighting repository.
Definition: repository.h:127
char * toString(const T &value)
bool isEmpty() const const
QString fileName(QUrl::ComponentFormattingOptions options) const const
bool mkpath(const QString &dirPath) const const
QXmlStreamAttributes attributes() const const
QUrl resolved(const QUrl &relative) const const
void start()
Starts the update procedure.
QVariant attribute(QNetworkRequest::Attribute code) const const
unsigned int version()
QString name(StandardShortcut id)
QByteArray readAll()
QString tr(const char *sourceText, const char *disambiguation, int n)
void done()
This signal is emitted when there are no pending downloads anymore.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:09:17 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.