26 #include <QDirIterator>
29 #include <QJsonDocument>
30 #include <QJsonObject>
31 #include <QNetworkAccessManager>
32 #include <QNetworkReply>
33 #include <QRegularExpression>
41 const QLatin1String
ipv4{
"ipv4"};
42 const QLatin1String
ipv6{
"ipv6"};
51 const QUrl NodeListAddress{
"https://nodes.tox.chat/json"};
52 const QLatin1String jsonNodeArrayName{
"nodes"};
53 const QLatin1String emptyAddress{
"-"};
54 const QRegularExpression ToxPkRegEx(QString(
"(^|\\s)[A-Fa-f0-9]{%1}($|\\s)").arg(64));
55 const QLatin1String builtinNodesFile{
":/conf/nodes.json"};
62 found &= node.contains(key);
66 qDebug() <<
"Node is missing required fields.";
78 for (
int i = 0; i < jsonTcpPorts.count(); ++i) {
79 const auto port = jsonTcpPorts.at(i).toInt();
80 if (port < 1 || port > std::numeric_limits<uint16_t>::max()) {
81 qDebug () <<
"Invalid TCP port in nodes list:" << port;
84 tcp_ports.emplace_back(
static_cast<uint16_t
>(port));
89 if (ipv6_address == emptyAddress) {
90 ipv6_address = QString{};
94 if (ipv4_address == emptyAddress) {
95 ipv4_address = QString{};
98 if (ipv4_address.isEmpty() && ipv6_address.isEmpty()) {
99 qWarning() <<
"Both ipv4 and ipv4 addresses are empty for" <<
public_key;
103 qWarning() <<
"UDP enabled but no UDP port for" <<
public_key;
107 qWarning() <<
"TCP enabled but no TCP ports for:" <<
public_key;
110 if (udp_port < 1 || udp_port > std::numeric_limits<uint16_t>::max()) {
111 qDebug() <<
"Invalid port in nodes list:" <<
udp_port;
114 const quint16 udp_port_u16 =
static_cast<quint16
>(
udp_port);
117 qDebug() <<
"Invalid public key in nodes list" <<
public_key;
128 server.
ipv4 = ipv4_address;
129 server.
ipv6 = ipv6_address;
130 outList.append(server);
138 if (!nodeList.isObject()) {
139 qWarning() <<
"Bootstrap JSON is missing root object";
143 QJsonObject rootObj = nodeList.object();
144 if (!(rootObj.contains(jsonNodeArrayName) && rootObj[jsonNodeArrayName].isArray())) {
145 qWarning() <<
"Bootstrap JSON is missing nodes array";
148 QJsonArray nodes = rootObj[jsonNodeArrayName].toArray();
149 for (
const QJsonValueRef node : nodes) {
150 if (node.isObject()) {
151 jsonNodeToDhtServer(node.toObject(), result);
160 QFile nodesFile{
file};
161 if (!nodesFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
162 qWarning() <<
"Couldn't read bootstrap nodes";
166 QString nodesJson = nodesFile.readAll();
169 auto jsonDoc = QJsonDocument::fromJson(nodesJson.toUtf8());
170 if (jsonDoc.isNull()) {
171 qWarning() <<
"Failed to parse JSON document";
175 return jsonToNodeList(jsonDoc);
180 QJsonArray jsonNodes;
181 for (
auto& node : nodes) {
182 QJsonObject nodeJson;
192 for (
size_t i = 0; i < node.tcpPorts.size(); ++i) {
193 tcp_ports.push_back(node.tcpPorts.at(i));
196 jsonNodes.append(nodeJson);
199 rootObj.insert(
"nodes", jsonNodes);
201 QJsonDocument doc{rootObj};
202 return doc.toJson(QJsonDocument::Indented);
219 if (!QFile(userFilePath).exists()) {
220 qInfo() <<
"Bootstrap node list not found, creating one with default nodes.";
223 auto buildInNodes = loadNodesFile(builtinNodesFile);
224 auto serializedNodes = serialize(buildInNodes);
226 QFile outFile(userFilePath);
227 outFile.open(QIODevice::WriteOnly | QIODevice::Text);
228 outFile.write(serializedNodes.data(), serializedNodes.size());
232 return loadNodesFile(userFilePath);
240 QNetworkRequest request{NodeListAddress};
241 request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json");
252 return loadNodesFile(builtinNodesFile);
257 if (reply->error() != QNetworkReply::NoError) {
258 nam.clearAccessCache();
264 QJsonDocument jsonDocument = QJsonDocument::fromJson(reply->readAll());
265 if (jsonDocument.isNull()) {