Skip to content

Commit 91b2687

Browse files
authored
WebAPI: Prevent producing empty sync data
PR qbittorrent#21688.
1 parent be3eefd commit 91b2687

File tree

1 file changed

+42
-36
lines changed

1 file changed

+42
-36
lines changed

src/webui/api/synccontroller.cpp

+42-36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Bittorrent Client using Qt and libtorrent.
3-
* Copyright (C) 2018-2023 Vladimir Golovnev <[email protected]>
3+
* Copyright (C) 2018-2024 Vladimir Golovnev <[email protected]>
44
*
55
* This program is free software; you can redistribute it and/or
66
* modify it under the terms of the GNU General Public License
@@ -28,8 +28,6 @@
2828

2929
#include "synccontroller.h"
3030

31-
#include <algorithm>
32-
3331
#include <QJsonArray>
3432
#include <QJsonObject>
3533
#include <QMetaObject>
@@ -119,9 +117,9 @@ namespace
119117
const QString KEY_FULL_UPDATE = u"full_update"_s;
120118
const QString KEY_RESPONSE_ID = u"rid"_s;
121119

122-
void processMap(const QVariantMap &prevData, const QVariantMap &data, QVariantMap &syncData);
123-
void processHash(QVariantHash prevData, const QVariantHash &data, QVariantMap &syncData, QVariantList &removedItems);
124-
void processList(QVariantList prevData, const QVariantList &data, QVariantList &syncData, QVariantList &removedItems);
120+
QVariantMap processMap(const QVariantMap &prevData, const QVariantMap &data);
121+
std::pair<QVariantMap, QVariantList> processHash(QVariantHash prevData, const QVariantHash &data);
122+
std::pair<QVariantList, QVariantList> processList(QVariantList prevData, const QVariantList &data);
125123
QJsonObject generateSyncData(int acceptedResponseId, const QVariantMap &data, QVariantMap &lastAcceptedData, QVariantMap &lastData);
126124

127125
QVariantMap getTransferInfo()
@@ -171,31 +169,28 @@ namespace
171169

172170
// Compare two structures (prevData, data) and calculate difference (syncData).
173171
// Structures encoded as map.
174-
void processMap(const QVariantMap &prevData, const QVariantMap &data, QVariantMap &syncData)
172+
QVariantMap processMap(const QVariantMap &prevData, const QVariantMap &data)
175173
{
176174
// initialize output variable
177-
syncData.clear();
175+
QVariantMap syncData;
178176

179177
for (auto i = data.cbegin(); i != data.cend(); ++i)
180178
{
181179
const QString &key = i.key();
182180
const QVariant &value = i.value();
183-
QVariantList removedItems;
184181

185182
switch (value.userType())
186183
{
187184
case QMetaType::QVariantMap:
188185
{
189-
QVariantMap map;
190-
processMap(prevData[key].toMap(), value.toMap(), map);
186+
const QVariantMap map = processMap(prevData[key].toMap(), value.toMap());
191187
if (!map.isEmpty())
192188
syncData[key] = map;
193189
}
194190
break;
195191
case QMetaType::QVariantHash:
196192
{
197-
QVariantMap map;
198-
processHash(prevData[key].toHash(), value.toHash(), map, removedItems);
193+
const auto [map, removedItems] = processHash(prevData[key].toHash(), value.toHash());
199194
if (!map.isEmpty())
200195
syncData[key] = map;
201196
if (!removedItems.isEmpty())
@@ -204,8 +199,7 @@ namespace
204199
break;
205200
case QMetaType::QVariantList:
206201
{
207-
QVariantList list;
208-
processList(prevData[key].toList(), value.toList(), list, removedItems);
202+
const auto [list, removedItems] = processList(prevData[key].toList(), value.toList());
209203
if (!list.isEmpty())
210204
syncData[key] = list;
211205
if (!removedItems.isEmpty())
@@ -228,21 +222,22 @@ namespace
228222
break;
229223
default:
230224
Q_ASSERT_X(false, "processMap"
231-
, u"Unexpected type: %1"_s
232-
.arg(QString::fromLatin1(value.metaType().name()))
233-
.toUtf8().constData());
225+
, u"Unexpected type: %1"_s.arg(QString::fromLatin1(value.metaType().name()))
226+
.toUtf8().constData());
234227
}
235228
}
229+
230+
return syncData;
236231
}
237232

238233
// Compare two lists of structures (prevData, data) and calculate difference (syncData, removedItems).
239234
// Structures encoded as map.
240235
// Lists are encoded as hash table (indexed by structure key value) to improve ease of searching for removed items.
241-
void processHash(QVariantHash prevData, const QVariantHash &data, QVariantMap &syncData, QVariantList &removedItems)
236+
std::pair<QVariantMap, QVariantList> processHash(QVariantHash prevData, const QVariantHash &data)
242237
{
243238
// initialize output variables
244-
syncData.clear();
245-
removedItems.clear();
239+
std::pair<QVariantMap, QVariantList> result;
240+
auto &[syncData, removedItems] = result;
246241

247242
if (prevData.isEmpty())
248243
{
@@ -264,8 +259,7 @@ namespace
264259
}
265260
else
266261
{
267-
QVariantMap map;
268-
processMap(prevData[i.key()].toMap(), i.value().toMap(), map);
262+
const QVariantMap map = processMap(prevData[i.key()].toMap(), i.value().toMap());
269263
// existing list item found - remove it from prevData
270264
prevData.remove(i.key());
271265
if (!map.isEmpty())
@@ -283,9 +277,7 @@ namespace
283277
}
284278
else
285279
{
286-
QVariantList list;
287-
QVariantList removedList;
288-
processList(prevData[i.key()].toList(), i.value().toList(), list, removedList);
280+
const auto [list, removedList] = processList(prevData[i.key()].toList(), i.value().toList());
289281
// existing list item found - remove it from prevData
290282
prevData.remove(i.key());
291283
if (!list.isEmpty() || !removedList.isEmpty())
@@ -309,14 +301,16 @@ namespace
309301
removedItems << i.key();
310302
}
311303
}
304+
305+
return result;
312306
}
313307

314308
// Compare two lists of simple value (prevData, data) and calculate difference (syncData, removedItems).
315-
void processList(QVariantList prevData, const QVariantList &data, QVariantList &syncData, QVariantList &removedItems)
309+
std::pair<QVariantList, QVariantList> processList(QVariantList prevData, const QVariantList &data)
316310
{
317311
// initialize output variables
318-
syncData.clear();
319-
removedItems.clear();
312+
std::pair<QVariantList, QVariantList> result;
313+
auto &[syncData, removedItems] = result;
320314

321315
if (prevData.isEmpty())
322316
{
@@ -346,6 +340,8 @@ namespace
346340
removedItems = prevData;
347341
}
348342
}
343+
344+
return result;
349345
}
350346

351347
QJsonObject generateSyncData(int acceptedResponseId, const QVariantMap &data, QVariantMap &lastAcceptedData, QVariantMap &lastData)
@@ -373,7 +369,7 @@ namespace
373369
}
374370
else
375371
{
376-
processMap(lastAcceptedData, data, syncData);
372+
syncData = processMap(lastAcceptedData, data);
377373
}
378374

379375
const int responseId = (lastResponseId % 1000000) + 1; // cycle between 1 and 1000000
@@ -595,8 +591,11 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
595591
category.insert(u"name"_s, categoryName);
596592

597593
auto &categorySnapshot = m_maindataSnapshot.categories[categoryName];
598-
processMap(categorySnapshot, category, m_maindataSyncBuf.categories[categoryName]);
599-
categorySnapshot = category;
594+
if (const QVariantMap syncData = processMap(categorySnapshot, category); !syncData.isEmpty())
595+
{
596+
m_maindataSyncBuf.categories[categoryName] = syncData;
597+
categorySnapshot = category;
598+
}
600599
}
601600
m_updatedCategories.clear();
602601

@@ -630,8 +629,12 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
630629
serializedTorrent.remove(KEY_TORRENT_ID);
631630

632631
auto &torrentSnapshot = m_maindataSnapshot.torrents[torrentID.toString()];
633-
processMap(torrentSnapshot, serializedTorrent, m_maindataSyncBuf.torrents[torrentID.toString()]);
634-
torrentSnapshot = serializedTorrent;
632+
633+
if (const QVariantMap syncData = processMap(torrentSnapshot, serializedTorrent); !syncData.isEmpty())
634+
{
635+
m_maindataSyncBuf.torrents[torrentID.toString()] = syncData;
636+
torrentSnapshot = serializedTorrent;
637+
}
635638
}
636639
m_updatedTorrents.clear();
637640

@@ -668,8 +671,11 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
668671
serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
669672
serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
670673
serverState[KEY_SYNC_MAINDATA_USE_SUBCATEGORIES] = session->isSubcategoriesEnabled();
671-
processMap(m_maindataSnapshot.serverState, serverState, m_maindataSyncBuf.serverState);
672-
m_maindataSnapshot.serverState = serverState;
674+
if (const QVariantMap syncData = processMap(m_maindataSnapshot.serverState, serverState); !syncData.isEmpty())
675+
{
676+
m_maindataSyncBuf.serverState = syncData;
677+
m_maindataSnapshot.serverState = serverState;
678+
}
673679

674680
QJsonObject syncData;
675681
syncData[KEY_RESPONSE_ID] = id;

0 commit comments

Comments
 (0)