Freeciv21
Develop your civilization from humble roots to a global empire
netfile.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <fc_config.h>
15 
16 #include <memory>
17 
18 // Qt
19 #include <QBuffer>
20 #include <QEventLoop>
21 #include <QJsonDocument>
22 #include <QNetworkAccessManager>
23 #include <QNetworkReply>
24 #include <QNetworkRequest>
25 #include <QSaveFile>
26 
27 // utility
28 #include "fcintl.h"
29 #include "log.h"
30 #include "registry_ini.h"
31 #include "version.h"
32 
33 #include "netfile.h"
34 
39 static bool netfile_download_file_core(const QUrl &url, QIODevice *out,
40  const nf_errmsg &cb)
41 {
42  fc_assert_ret_val(out != nullptr, false);
43 
44  // Create a network manager
45  auto manager = std::make_unique<QNetworkAccessManager>();
46 
47  // Initiate the request
48  auto request = QNetworkRequest(url);
49  request.setHeader(QNetworkRequest::UserAgentHeader,
50  QLatin1String("Freeciv21/") + freeciv21_version());
51  request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
52  QNetworkRequest::NoLessSafeRedirectPolicy);
53 
54  auto *reply = manager->get(request);
55  bool retval = true;
56 
57  QEventLoop loop; // Need an event loop for QNetworkReply to work
58 
59  // Data are available
60  QObject::connect(reply, &QNetworkReply::readyRead, [&]() {
61  while (reply->bytesAvailable() > 0) {
62  out->write(reply->read(1 << 20)); // Read max 1MB at a time
63  }
64  });
65 
66  // It's over -- for good or bad reasons
67  QObject::connect(reply, &QNetworkReply::finished, [&] {
68  if (reply->error() != QNetworkReply::NoError) {
69  // Error
70  retval = false;
71 
72  if (cb != nullptr) {
73  // TRANS: %1 is URL, %2 is Curl error message (not in Freeciv
74  // translation domain)
75  auto msg = QString::fromUtf8(_("Failed to fetch %1: %2"))
76  .arg(url.toDisplayString())
77  .arg(reply->errorString());
78  cb(msg);
79  }
80  }
81 
82  // Clean up
83  reply->deleteLater();
84  manager->deleteLater();
85 
86  loop.quit();
87  });
88 
89  // Perform the request
90  loop.exec();
91 
92  return retval;
93 }
94 
98 QJsonDocument netfile_get_json_file(const QUrl &url, const nf_errmsg &cb)
99 {
100  QBuffer buffer;
101  buffer.open(QIODevice::WriteOnly);
102 
103  // Try to download into the buffer
104  if (netfile_download_file_core(url, &buffer, cb)) {
105  // Parse
106  QJsonParseError error;
107  auto document = QJsonDocument::fromJson(buffer.data(), &error);
108  if (error.error != QJsonParseError::NoError) {
109  cb(QString::fromUtf8(_("Error parsing JSON: %1"))
110  .arg(error.errorString()));
111  return QJsonDocument();
112  }
113  return document;
114  }
115 
116  return QJsonDocument();
117 }
118 
122 section_file *netfile_get_section_file(const QUrl &url, const nf_errmsg &cb)
123 {
124  QBuffer buffer;
125  buffer.open(QIODevice::WriteOnly);
126 
127  // Try to download into the buffer
128  if (netfile_download_file_core(url, &buffer, cb)) {
129  // Parse
130  return secfile_from_stream(&buffer, true);
131  }
132 
133  return nullptr;
134 }
135 
139 bool netfile_download_file(const QUrl &url, const char *filename,
140  const nf_errmsg &cb)
141 {
142  QSaveFile out(QString::fromUtf8(filename));
143  if (!out.open(QIODevice::WriteOnly)) {
144  if (cb != nullptr) {
145  auto msg = QString::fromUtf8(_("Could not open %1 for writing"))
146  .arg(filename);
147  cb(msg);
148  }
149  return false;
150  }
151 
152  auto ok = netfile_download_file_core(url, &out, cb);
153  if (ok) {
154  out.commit();
155  }
156  return ok;
157 }
#define _(String)
Definition: fcintl.h:50
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
section_file * netfile_get_section_file(const QUrl &url, const nf_errmsg &cb)
Fetch section file from net.
Definition: netfile.cpp:122
bool netfile_download_file(const QUrl &url, const char *filename, const nf_errmsg &cb)
Fetch file from given URL and save as given filename.
Definition: netfile.cpp:139
static bool netfile_download_file_core(const QUrl &url, QIODevice *out, const nf_errmsg &cb)
Fetch file from given URL to given file stream.
Definition: netfile.cpp:39
QJsonDocument netfile_get_json_file(const QUrl &url, const nf_errmsg &cb)
Fetch a JSON file from the net.
Definition: netfile.cpp:98
std::function< void(const QString &message)> nf_errmsg
Definition: netfile.h:23
struct section_file * secfile_from_stream(QIODevice *stream, bool allow_duplicates)
Create a section file from a stream.
const char * freeciv21_version()
Returns the raw version string.
Definition: version.cpp:29