Improve coding style

This commit is contained in:
Vladimir Golovnev (Glassez)
2020-11-16 10:02:11 +03:00
parent acad35c5bc
commit c41df9ffbd
147 changed files with 4454 additions and 2227 deletions

View File

@@ -70,7 +70,8 @@ void AppController::versionAction()
void AppController::buildInfoAction()
{
const QJsonObject versions = {
const QJsonObject versions =
{
{"qt", QT_VERSION_STR},
{"libtorrent", Utils::Misc::libtorrentVersionString()},
{"boost", Utils::Misc::boostVersionString()},
@@ -117,7 +118,8 @@ void AppController::preferencesAction()
// Automatically add torrents from
const QVariantHash dirs = pref->getScanDirs();
QJsonObject nativeDirs;
for (auto i = dirs.cbegin(); i != dirs.cend(); ++i) {
for (auto i = dirs.cbegin(); i != dirs.cend(); ++i)
{
if (i.value().type() == QVariant::Int)
nativeDirs.insert(Utils::Fs::toNativePath(i.key()), i.value().toInt());
else
@@ -383,21 +385,25 @@ void AppController::setPreferencesAction()
if (hasKey("export_dir_fin"))
session->setFinishedTorrentExportDirectory(it.value().toString());
// Automatically add torrents from
if (hasKey("scan_dirs")) {
if (hasKey("scan_dirs"))
{
const QVariantHash nativeDirs = it.value().toHash();
QVariantHash oldScanDirs = pref->getScanDirs();
QVariantHash scanDirs;
ScanFoldersModel *model = ScanFoldersModel::instance();
for (auto i = nativeDirs.cbegin(); i != nativeDirs.cend(); ++i) {
for (auto i = nativeDirs.cbegin(); i != nativeDirs.cend(); ++i)
{
QString folder = Utils::Fs::toUniformPath(i.key());
int downloadType;
QString downloadPath;
ScanFoldersModel::PathStatus ec;
if (i.value().type() == QVariant::String) {
if (i.value().type() == QVariant::String)
{
downloadType = ScanFoldersModel::CUSTOM_LOCATION;
downloadPath = Utils::Fs::toUniformPath(i.value().toString());
}
else {
else
{
downloadType = i.value().toInt();
downloadPath = (downloadType == ScanFoldersModel::DEFAULT_LOCATION) ? "Default folder" : "Watch folder";
}
@@ -407,19 +413,23 @@ void AppController::setPreferencesAction()
else
ec = model->updatePath(folder, static_cast<ScanFoldersModel::PathType>(downloadType), downloadPath);
if (ec == ScanFoldersModel::Ok) {
if (ec == ScanFoldersModel::Ok)
{
scanDirs.insert(folder, (downloadType == ScanFoldersModel::CUSTOM_LOCATION) ? QVariant(downloadPath) : QVariant(downloadType));
qDebug("New watched folder: %s to %s", qUtf8Printable(folder), qUtf8Printable(downloadPath));
}
else {
else
{
qDebug("Watched folder %s failed with error %d", qUtf8Printable(folder), ec);
}
}
// Update deleted folders
for (auto i = oldScanDirs.cbegin(); i != oldScanDirs.cend(); ++i) {
for (auto i = oldScanDirs.cbegin(); i != oldScanDirs.cend(); ++i)
{
const QString &folder = i.key();
if (!scanDirs.contains(folder)) {
if (!scanDirs.contains(folder))
{
model->removePath(folder);
qDebug("Removed watched folder %s", qUtf8Printable(folder));
}
@@ -555,13 +565,15 @@ void AppController::setPreferencesAction()
if (hasKey("slow_torrent_inactive_timer"))
session->setSlowTorrentsInactivityTimer(it.value().toInt());
// Share Ratio Limiting
if (hasKey("max_ratio_enabled")) {
if (hasKey("max_ratio_enabled"))
{
if (it.value().toBool())
session->setGlobalMaxRatio(m["max_ratio"].toReal());
else
session->setGlobalMaxRatio(-1);
}
if (hasKey("max_seeding_time_enabled")) {
if (hasKey("max_seeding_time_enabled"))
{
if (it.value().toBool())
session->setGlobalMaxSeedingMinutes(m["max_seeding_time"].toInt());
else
@@ -577,14 +589,18 @@ void AppController::setPreferencesAction()
// Web UI
// Language
if (hasKey("locale")) {
if (hasKey("locale"))
{
QString locale = it.value().toString();
if (pref->getLocale() != locale) {
if (pref->getLocale() != locale)
{
auto *translator = new QTranslator;
if (translator->load(QLatin1String(":/lang/qbittorrent_") + locale)) {
if (translator->load(QLatin1String(":/lang/qbittorrent_") + locale))
{
qDebug("%s locale recognized, using translation.", qUtf8Printable(locale));
}
else {
else
{
qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale));
}
qApp->installTranslator(translator);
@@ -616,7 +632,8 @@ void AppController::setPreferencesAction()
pref->setWebUiLocalAuthEnabled(!it.value().toBool());
if (hasKey("bypass_auth_subnet_whitelist_enabled"))
pref->setWebUiAuthSubnetWhitelistEnabled(it.value().toBool());
if (hasKey("bypass_auth_subnet_whitelist")) {
if (hasKey("bypass_auth_subnet_whitelist"))
{
// recognize new lines and commas as delimiters
pref->setWebUiAuthSubnetWhitelist(it.value().toString().split(QRegularExpression("\n|,"), QString::SkipEmptyParts));
}
@@ -673,7 +690,8 @@ void AppController::setPreferencesAction()
// Advanced settings
// qBittorrent preferences
// Current network interface
if (hasKey("current_network_interface")) {
if (hasKey("current_network_interface"))
{
const QString ifaceValue {it.value().toString()};
const QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
@@ -687,7 +705,8 @@ void AppController::setPreferencesAction()
session->setNetworkInterfaceName(ifaceName);
}
// Current network interface address
if (hasKey("current_interface_address")) {
if (hasKey("current_interface_address"))
{
const QHostAddress ifaceAddress {it.value().toString().trimmed()};
session->setNetworkInterfaceAddress(ifaceAddress.isNull() ? QString {} : ifaceAddress.toString());
}
@@ -777,7 +796,8 @@ void AppController::setPreferencesAction()
session->setAnnounceToAllTrackers(it.value().toBool());
if (hasKey("announce_to_all_tiers"))
session->setAnnounceToAllTiers(it.value().toBool());
if (hasKey("announce_ip")) {
if (hasKey("announce_ip"))
{
const QHostAddress announceAddr {it.value().toString().trimmed()};
session->setAnnounceIP(announceAddr.isNull() ? QString {} : announceAddr.toString());
}
@@ -805,9 +825,12 @@ void AppController::defaultSavePathAction()
void AppController::networkInterfaceListAction()
{
QJsonArray ifaceList;
for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces())) {
if (!iface.addressEntries().isEmpty()) {
ifaceList.append(QJsonObject {
for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces()))
{
if (!iface.addressEntries().isEmpty())
{
ifaceList.append(QJsonObject
{
{"name", iface.humanReadableName()},
{"value", iface.name()}
});
@@ -832,11 +855,13 @@ void AppController::networkInterfaceAddressListAction()
addressList.append(addr.toString());
};
if (ifaceName.isEmpty()) {
if (ifaceName.isEmpty())
{
for (const QHostAddress &addr : asConst(QNetworkInterface::allAddresses()))
appendAddress(addr);
}
else {
else
{
const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
for (const QNetworkAddressEntry &entry : asConst(iface.addressEntries()))
appendAddress(entry.ip());

View File

@@ -38,7 +38,8 @@
void AuthController::loginAction()
{
if (sessionManager()->session()) {
if (sessionManager()->session())
{
setResult(QLatin1String("Ok."));
return;
}
@@ -47,7 +48,8 @@ void AuthController::loginAction()
const QString usernameFromWeb {params()["username"]};
const QString passwordFromWeb {params()["password"]};
if (isBanned()) {
if (isBanned())
{
LogMsg(tr("WebAPI login failure. Reason: IP has been banned, IP: %1, username: %2")
.arg(clientAddr, usernameFromWeb)
, Log::WARNING);
@@ -62,14 +64,16 @@ void AuthController::loginAction()
const bool usernameEqual = Utils::Password::slowEquals(usernameFromWeb.toUtf8(), username.toUtf8());
const bool passwordEqual = Utils::Password::PBKDF2::verify(secret, passwordFromWeb);
if (usernameEqual && passwordEqual) {
if (usernameEqual && passwordEqual)
{
m_clientFailedLogins.remove(clientAddr);
sessionManager()->sessionStart();
setResult(QLatin1String("Ok."));
LogMsg(tr("WebAPI login success. IP: %1").arg(clientAddr));
}
else {
else
{
if (Preferences::instance()->getWebUIMaxAuthFailCount() > 0)
increaseFailedAttempts();
setResult(QLatin1String("Fails."));
@@ -91,7 +95,8 @@ bool AuthController::isBanned() const
return false;
bool isBanned = (failedLoginIter->banTimer.remainingTime() >= 0);
if (isBanned && failedLoginIter->banTimer.hasExpired()) {
if (isBanned && failedLoginIter->banTimer.hasExpired())
{
m_clientFailedLogins.erase(failedLoginIter);
isBanned = false;
}
@@ -111,7 +116,8 @@ void AuthController::increaseFailedAttempts()
FailedLogin &failedLogin = m_clientFailedLogins[sessionManager()->clientId()];
++failedLogin.failedAttemptsCount;
if (failedLogin.failedAttemptsCount >= Preferences::instance()->getWebUIMaxAuthFailCount()) {
if (failedLogin.failedAttemptsCount >= Preferences::instance()->getWebUIMaxAuthFailCount())
{
// Max number of failed attempts reached
// Start ban period
failedLogin.banTimer.setRemainingTime(Preferences::instance()->getWebUIBanDuration());

View File

@@ -40,7 +40,8 @@ struct ISession
virtual void setData(const QString &id, const QVariant &data) = 0;
template <class T>
T getData(const QString &id) const {
T getData(const QString &id) const
{
return this->getData(id).value<T>();
}
};

View File

@@ -73,14 +73,16 @@ void LogController::mainAction()
Logger *const logger = Logger::instance();
QJsonArray msgList;
for (const Log::Msg &msg : asConst(logger->getMessages(lastKnownId))) {
for (const Log::Msg &msg : asConst(logger->getMessages(lastKnownId)))
{
if (!((msg.type == Log::NORMAL && isNormal)
|| (msg.type == Log::INFO && isInfo)
|| (msg.type == Log::WARNING && isWarning)
|| (msg.type == Log::CRITICAL && isCritical)))
continue;
msgList.append(QJsonObject {
msgList.append(QJsonObject
{
{KEY_LOG_ID, msg.id},
{KEY_LOG_TIMESTAMP, msg.timestamp},
{KEY_LOG_MSG_TYPE, msg.type},
@@ -113,8 +115,10 @@ void LogController::peersAction()
Logger *const logger = Logger::instance();
QJsonArray peerList;
for (const Log::Peer &peer : asConst(logger->getPeers(lastKnownId))) {
peerList.append(QJsonObject {
for (const Log::Peer &peer : asConst(logger->getPeers(lastKnownId)))
{
peerList.append(QJsonObject
{
{KEY_LOG_ID, peer.id},
{KEY_LOG_TIMESTAMP, peer.timestamp},
{KEY_LOG_PEER_IP, peer.ip},

View File

@@ -104,15 +104,18 @@ void RSSController::markAsReadAction()
RSS::Item *item = RSS::Session::instance()->itemByPath(itemPath);
if (!item) return;
if (!articleId.isNull()) {
if (!articleId.isNull())
{
RSS::Feed *feed = qobject_cast<RSS::Feed *>(item);
if (feed) {
if (feed)
{
RSS::Article *article = feed->articleByGUID(articleId);
if (article)
article->markAsRead();
}
}
else {
else
{
item->markAsRead();
}
}
@@ -174,12 +177,14 @@ void RSSController::matchingArticlesAction()
const RSS::AutoDownloadRule rule = RSS::AutoDownloader::instance()->ruleByName(ruleName);
QJsonObject jsonObj;
for (const QString &feedURL : rule.feedURLs()) {
for (const QString &feedURL : rule.feedURLs())
{
const RSS::Feed *feed = RSS::Session::instance()->feedByURL(feedURL);
if (!feed) continue; // feed doesn't exist
QJsonArray matchingArticles;
for (const RSS::Article *article : feed->articles()) {
for (const RSS::Article *article : feed->articles())
{
if (rule.matches(article->data()))
matchingArticles << article->title();
}

View File

@@ -68,14 +68,17 @@ namespace
*/
QJsonArray getPluginCategories(QStringList categories)
{
QJsonArray categoriesInfo {QJsonObject {
QJsonArray categoriesInfo
{QJsonObject {
{QLatin1String("id"), "all"},
{QLatin1String("name"), SearchPluginManager::categoryFullName("all")}
}};
categories.sort(Qt::CaseInsensitive);
for (const QString &category : categories) {
categoriesInfo << QJsonObject {
for (const QString &category : categories)
{
categoriesInfo << QJsonObject
{
{QLatin1String("id"), category},
{QLatin1String("name"), SearchPluginManager::categoryFullName(category)}
};
@@ -97,7 +100,8 @@ void SearchController::startAction()
const QStringList plugins = params()["plugins"].split('|');
QStringList pluginsToUse;
if (plugins.size() == 1) {
if (plugins.size() == 1)
{
const QString pluginsLower = plugins[0].toLower();
if (pluginsLower == "all")
pluginsToUse = SearchPluginManager::instance()->allPlugins();
@@ -106,7 +110,8 @@ void SearchController::startAction()
else
pluginsToUse << plugins;
}
else {
else
{
pluginsToUse << plugins;
}
@@ -144,7 +149,8 @@ void SearchController::stopAction()
const SearchHandlerPtr searchHandler = searchHandlers[id];
if (searchHandler->isActive()) {
if (searchHandler->isActive())
{
searchHandler->cancelSearch();
removeActiveSearch(session, id);
}
@@ -161,9 +167,11 @@ void SearchController::statusAction()
QJsonArray statusArray;
const QList<int> searchIds {(id == 0) ? searchHandlers.keys() : QList<int> {id}};
for (const int searchId : searchIds) {
for (const int searchId : searchIds)
{
const SearchHandlerPtr searchHandler = searchHandlers[searchId];
statusArray << QJsonObject {
statusArray << QJsonObject
{
{"id", searchId},
{"status", searchHandler->isActive() ? "Running" : "Stopped"},
{"total", searchHandler->results().size()}
@@ -271,7 +279,8 @@ void SearchController::updatePluginsAction()
void SearchController::checkForUpdatesFinished(const QHash<QString, PluginVersion> &updateInfo)
{
if (updateInfo.isEmpty()) {
if (updateInfo.isEmpty())
{
LogMsg(tr("All plugins are already up to date."), Log::INFO);
return;
}
@@ -279,7 +288,8 @@ void SearchController::checkForUpdatesFinished(const QHash<QString, PluginVersio
LogMsg(tr("Updating %1 plugins").arg(updateInfo.size()), Log::INFO);
SearchPluginManager *const pluginManager = SearchPluginManager::instance();
for (const QString &pluginName : asConst(updateInfo.keys())) {
for (const QString &pluginName : asConst(updateInfo.keys()))
{
LogMsg(tr("Updating plugin %1").arg(pluginName), Log::INFO);
pluginManager->updatePlugin(pluginName);
}
@@ -328,8 +338,10 @@ int SearchController::generateSearchId() const
QJsonObject SearchController::getResults(const QList<SearchResult> &searchResults, const bool isSearchActive, const int totalResults) const
{
QJsonArray searchResultsArray;
for (const SearchResult &searchResult : searchResults) {
searchResultsArray << QJsonObject {
for (const SearchResult &searchResult : searchResults)
{
searchResultsArray << QJsonObject
{
{"fileName", searchResult.fileName},
{"fileUrl", searchResult.fileUrl},
{"fileSize", searchResult.fileSize},
@@ -340,7 +352,8 @@ QJsonObject SearchController::getResults(const QList<SearchResult> &searchResult
};
}
const QJsonObject result = {
const QJsonObject result =
{
{"status", isSearchActive ? "Running" : "Stopped"},
{"results", searchResultsArray},
{"total", totalResults}
@@ -366,10 +379,12 @@ QJsonArray SearchController::getPluginsInfo(const QStringList &plugins) const
{
QJsonArray pluginsArray;
for (const QString &plugin : plugins) {
for (const QString &plugin : plugins)
{
const PluginInfo *const pluginInfo = SearchPluginManager::instance()->pluginInfo(plugin);
pluginsArray << QJsonObject {
pluginsArray << QJsonObject
{
{"name", pluginInfo->name},
{"version", QString(pluginInfo->version)},
{"fullName", pluginInfo->fullName},

View File

@@ -39,7 +39,8 @@ namespace
{
QString torrentStateToString(const BitTorrent::TorrentState state)
{
switch (state) {
switch (state)
{
case BitTorrent::TorrentState::Error:
return QLatin1String("error");
case BitTorrent::TorrentState::MissingFiles:
@@ -82,7 +83,8 @@ namespace
QVariantMap serialize(const BitTorrent::TorrentHandle &torrent)
{
QVariantMap ret = {
QVariantMap ret =
{
{KEY_TORRENT_HASH, QString(torrent.hash())},
{KEY_TORRENT_NAME, torrent.name()},
{KEY_TORRENT_MAGNET_URI, torrent.createMagnetURI()},
@@ -134,10 +136,12 @@ QVariantMap serialize(const BitTorrent::TorrentHandle &torrent)
const qreal ratio = torrent.realRatio();
ret[KEY_TORRENT_RATIO] = (ratio > BitTorrent::TorrentHandle::MAX_RATIO) ? -1 : ratio;
if (torrent.isPaused() || torrent.isChecking()) {
if (torrent.isPaused() || torrent.isChecking())
{
ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = 0;
}
else {
else
{
const qint64 dt = (QDateTime::currentDateTime().toSecsSinceEpoch()
- torrent.timeSinceActivity());
ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = dt;

View File

@@ -164,20 +164,24 @@ namespace
// initialize output variable
syncData.clear();
for (auto i = data.cbegin(); i != data.cend(); ++i) {
for (auto i = data.cbegin(); i != data.cend(); ++i)
{
const QString &key = i.key();
const QVariant &value = i.value();
QVariantList removedItems;
switch (static_cast<QMetaType::Type>(value.type())) {
case QMetaType::QVariantMap: {
switch (static_cast<QMetaType::Type>(value.type()))
{
case QMetaType::QVariantMap:
{
QVariantMap map;
processMap(prevData[key].toMap(), value.toMap(), map);
if (!map.isEmpty())
syncData[key] = map;
}
break;
case QMetaType::QVariantHash: {
case QMetaType::QVariantHash:
{
QVariantMap map;
processHash(prevData[key].toHash(), value.toHash(), map, removedItems);
if (!map.isEmpty())
@@ -186,7 +190,8 @@ namespace
syncData[key + KEY_SUFFIX_REMOVED] = removedItems;
}
break;
case QMetaType::QVariantList: {
case QMetaType::QVariantList:
{
QVariantList list;
processList(prevData[key].toList(), value.toList(), list, removedItems);
if (!list.isEmpty())
@@ -225,42 +230,52 @@ namespace
syncData.clear();
removedItems.clear();
if (prevData.isEmpty()) {
if (prevData.isEmpty())
{
// If list was empty before, then difference is a whole new list.
for (auto i = data.cbegin(); i != data.cend(); ++i)
syncData[i.key()] = i.value();
}
else {
for (auto i = data.cbegin(); i != data.cend(); ++i) {
switch (i.value().type()) {
else
{
for (auto i = data.cbegin(); i != data.cend(); ++i)
{
switch (i.value().type())
{
case QVariant::Map:
if (!prevData.contains(i.key())) {
if (!prevData.contains(i.key()))
{
// new list item found - append it to syncData
syncData[i.key()] = i.value();
}
else {
else
{
QVariantMap map;
processMap(prevData[i.key()].toMap(), i.value().toMap(), map);
// existing list item found - remove it from prevData
prevData.remove(i.key());
if (!map.isEmpty()) {
if (!map.isEmpty())
{
// changed list item found - append its changes to syncData
syncData[i.key()] = map;
}
}
break;
case QVariant::StringList:
if (!prevData.contains(i.key())) {
if (!prevData.contains(i.key()))
{
// new list item found - append it to syncData
syncData[i.key()] = i.value();
}
else {
else
{
QVariantList list;
QVariantList removedList;
processList(prevData[i.key()].toList(), i.value().toList(), list, removedList);
// existing list item found - remove it from prevData
prevData.remove(i.key());
if (!list.isEmpty() || !removedList.isEmpty()) {
if (!list.isEmpty() || !removedList.isEmpty())
{
// changed list item found - append entire list to syncData
syncData[i.key()] = i.value();
}
@@ -271,7 +286,8 @@ namespace
}
}
if (!prevData.isEmpty()) {
if (!prevData.isEmpty())
{
// prevData contains only items that are missing now -
// put them in removedItems
for (auto i = prevData.cbegin(); i != prevData.cend(); ++i)
@@ -287,12 +303,15 @@ namespace
syncData.clear();
removedItems.clear();
if (prevData.isEmpty()) {
if (prevData.isEmpty())
{
// If list was empty before, then difference is a whole new list.
syncData = data;
}
else {
for (const QVariant &item : data) {
else
{
for (const QVariant &item : data)
{
if (!prevData.contains(item))
// new list item found - append it to syncData
syncData.append(item);
@@ -313,7 +332,8 @@ namespace
QVariantMap syncData;
bool fullUpdate = true;
int lastResponseId = 0;
if (acceptedResponseId > 0) {
if (acceptedResponseId > 0)
{
lastResponseId = lastData[KEY_RESPONSE_ID].toInt();
if (lastResponseId == acceptedResponseId)
@@ -321,13 +341,15 @@ namespace
int lastAcceptedResponseId = lastAcceptedData[KEY_RESPONSE_ID].toInt();
if (lastAcceptedResponseId == acceptedResponseId) {
if (lastAcceptedResponseId == acceptedResponseId)
{
processMap(lastAcceptedData, data, syncData);
fullUpdate = false;
}
}
if (fullUpdate) {
if (fullUpdate)
{
lastAcceptedData.clear();
syncData = data;
syncData[KEY_FULL_UPDATE] = true;
@@ -436,7 +458,8 @@ void SyncController::maindataAction()
QVariantHash torrents;
QHash<QString, QStringList> trackers;
for (const BitTorrent::TorrentHandle *torrent : asConst(session->torrents())) {
for (const BitTorrent::TorrentHandle *torrent : asConst(session->torrents()))
{
const BitTorrent::InfoHash torrentHash = torrent->hash();
QVariantMap map = serialize(*torrent);
@@ -445,15 +468,18 @@ void SyncController::maindataAction()
// Calculated last activity time can differ from actual value by up to 10 seconds (this is a libtorrent issue).
// So we don't need unnecessary updates of last activity time in response.
const auto iterTorrents = lastResponse.find("torrents");
if (iterTorrents != lastResponse.end()) {
if (iterTorrents != lastResponse.end())
{
const QVariantHash lastResponseTorrents = iterTorrents->toHash();
const auto iterHash = lastResponseTorrents.find(torrentHash);
if (iterHash != lastResponseTorrents.end()) {
if (iterHash != lastResponseTorrents.end())
{
const QVariantMap torrentData = iterHash->toMap();
const auto iterLastActivity = torrentData.find(KEY_TORRENT_LAST_ACTIVITY_TIME);
if (iterLastActivity != torrentData.end()) {
if (iterLastActivity != torrentData.end())
{
const int lastValue = iterLastActivity->toInt();
if (qAbs(lastValue - map[KEY_TORRENT_LAST_ACTIVITY_TIME].toInt()) < 15)
map[KEY_TORRENT_LAST_ACTIVITY_TIME] = lastValue;
@@ -470,9 +496,11 @@ void SyncController::maindataAction()
QVariantHash categories;
const QStringMap categoriesList = session->categories();
for (auto it = categoriesList.cbegin(); it != categoriesList.cend(); ++it) {
for (auto it = categoriesList.cbegin(); it != categoriesList.cend(); ++it)
{
const QString &key = it.key();
categories[key] = QVariantMap {
categories[key] = QVariantMap
{
{"name", key},
{"savePath", it.value()}
};
@@ -485,7 +513,8 @@ void SyncController::maindataAction()
data["tags"] = tags;
QVariantHash trackersHash;
for (auto i = trackers.constBegin(); i != trackers.constEnd(); ++i) {
for (auto i = trackers.constBegin(); i != trackers.constEnd(); ++i)
{
trackersHash[i.key()] = i.value();
}
data["trackers"] = trackersHash;
@@ -526,10 +555,12 @@ void SyncController::torrentPeersAction()
data[KEY_SYNC_TORRENT_PEERS_SHOW_FLAGS] = resolvePeerCountries;
for (const BitTorrent::PeerInfo &pi : peersList) {
for (const BitTorrent::PeerInfo &pi : peersList)
{
if (pi.address().ip.isNull()) continue;
QVariantMap peer = {
QVariantMap peer =
{
{KEY_PEER_IP, pi.address().ip.toString()},
{KEY_PEER_PORT, pi.address().port},
{KEY_PEER_CLIENT, pi.client()},
@@ -545,7 +576,8 @@ void SyncController::torrentPeersAction()
{KEY_PEER_FILES, torrent->info().filesForPiece(pi.downloadingPieceIndex()).join('\n')}
};
if (resolvePeerCountries) {
if (resolvePeerCountries)
{
peer[KEY_PEER_COUNTRY_CODE] = pi.country().toLower();
peer[KEY_PEER_COUNTRY] = Net::GeoIPManager::CountryName(pi.country());
}
@@ -563,7 +595,8 @@ void SyncController::torrentPeersAction()
qint64 SyncController::getFreeDiskSpace()
{
if (m_freeDiskSpaceElapsedTimer.hasExpired(FREEDISKSPACE_CHECK_TIMEOUT)) {
if (m_freeDiskSpaceElapsedTimer.hasExpired(FREEDISKSPACE_CHECK_TIMEOUT))
{
invokeChecker();
m_freeDiskSpaceElapsedTimer.restart();
}

View File

@@ -121,12 +121,15 @@ namespace
void applyToTorrents(const QStringList &hashes, const std::function<void (BitTorrent::TorrentHandle *torrent)> &func)
{
if ((hashes.size() == 1) && (hashes[0] == QLatin1String("all"))) {
if ((hashes.size() == 1) && (hashes[0] == QLatin1String("all")))
{
for (BitTorrent::TorrentHandle *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
func(torrent);
}
else {
for (const QString &hash : hashes) {
else
{
for (const QString &hash : hashes)
{
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
if (torrent)
func(torrent);
@@ -137,10 +140,12 @@ namespace
QJsonArray getStickyTrackers(const BitTorrent::TorrentHandle *const torrent)
{
int seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0;
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers())) {
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers()))
{
if (peer.isConnecting()) continue;
if (peer.isSeed()) {
if (peer.isSeed())
{
if (peer.fromDHT())
++seedsDHT;
if (peer.fromPeX())
@@ -148,7 +153,8 @@ namespace
if (peer.fromLSD())
++seedsLSD;
}
else {
else
{
if (peer.fromDHT())
++leechesDHT;
if (peer.fromPeX())
@@ -164,7 +170,8 @@ namespace
const QString privateMsg {QCoreApplication::translate("TrackerListWidget", "This torrent is private")};
const bool isTorrentPrivate = torrent->isPrivate();
const QJsonObject dht {
const QJsonObject dht
{
{KEY_TRACKER_URL, "** [DHT] **"},
{KEY_TRACKER_TIER, ""},
{KEY_TRACKER_MSG, (isTorrentPrivate ? privateMsg : "")},
@@ -175,7 +182,8 @@ namespace
{KEY_TRACKER_LEECHES_COUNT, leechesDHT}
};
const QJsonObject pex {
const QJsonObject pex
{
{KEY_TRACKER_URL, "** [PeX] **"},
{KEY_TRACKER_TIER, ""},
{KEY_TRACKER_MSG, (isTorrentPrivate ? privateMsg : "")},
@@ -186,7 +194,8 @@ namespace
{KEY_TRACKER_LEECHES_COUNT, leechesPeX}
};
const QJsonObject lsd {
const QJsonObject lsd
{
{KEY_TRACKER_URL, "** [LSD] **"},
{KEY_TRACKER_TIER, ""},
{KEY_TRACKER_MSG, (isTorrentPrivate ? privateMsg : "")},
@@ -251,7 +260,8 @@ void TorrentsController::infoAction()
QVariantList torrentList;
TorrentFilter torrentFilter(filter, (hashSet.isEmpty() ? TorrentFilter::AnyHash : hashSet), category);
for (BitTorrent::TorrentHandle *const torrent : asConst(BitTorrent::Session::instance()->torrents())) {
for (BitTorrent::TorrentHandle *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
{
if (torrentFilter.match(torrent))
torrentList.append(serialize(*torrent));
}
@@ -358,12 +368,14 @@ void TorrentsController::propertiesAction()
dataDict[KEY_PROP_PIECES_HAVE] = torrent->piecesHave();
dataDict[KEY_PROP_CREATED_BY] = torrent->creator();
dataDict[KEY_PROP_ADDITION_DATE] = static_cast<double>(torrent->addedTime().toSecsSinceEpoch());
if (torrent->hasMetadata()) {
if (torrent->hasMetadata())
{
dataDict[KEY_PROP_LAST_SEEN] = torrent->lastSeenComplete().isValid() ? torrent->lastSeenComplete().toSecsSinceEpoch() : -1;
dataDict[KEY_PROP_COMPLETION_DATE] = torrent->completedTime().isValid() ? torrent->completedTime().toSecsSinceEpoch() : -1;
dataDict[KEY_PROP_CREATION_DATE] = static_cast<double>(torrent->creationDate().toSecsSinceEpoch());
}
else {
else
{
dataDict[KEY_PROP_LAST_SEEN] = -1;
dataDict[KEY_PROP_COMPLETION_DATE] = -1;
dataDict[KEY_PROP_CREATION_DATE] = -1;
@@ -397,10 +409,12 @@ void TorrentsController::trackersAction()
QJsonArray trackerList = getStickyTrackers(torrent);
QHash<QString, BitTorrent::TrackerInfo> trackersData = torrent->trackerInfos();
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) {
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers()))
{
const BitTorrent::TrackerInfo data = trackersData.value(tracker.url());
trackerList << QJsonObject {
trackerList << QJsonObject
{
{KEY_TRACKER_URL, tracker.url()},
{KEY_TRACKER_TIER, tracker.tier()},
{KEY_TRACKER_STATUS, static_cast<int>(tracker.status())},
@@ -429,8 +443,10 @@ void TorrentsController::webseedsAction()
throw APIError(APIErrorType::NotFound);
QJsonArray webSeedList;
for (const QUrl &webseed : asConst(torrent->urlSeeds())) {
webSeedList.append(QJsonObject {
for (const QUrl &webseed : asConst(torrent->urlSeeds()))
{
webSeedList.append(QJsonObject
{
{KEY_WEBSEED_URL, webseed.toString()}
});
}
@@ -458,13 +474,16 @@ void TorrentsController::filesAction()
throw APIError(APIErrorType::NotFound);
QJsonArray fileList;
if (torrent->hasMetadata()) {
if (torrent->hasMetadata())
{
const QVector<BitTorrent::DownloadPriority> priorities = torrent->filePriorities();
const QVector<qreal> fp = torrent->filesProgress();
const QVector<qreal> fileAvailability = torrent->availableFileFractions();
const BitTorrent::TorrentInfo info = torrent->info();
for (int i = 0; i < torrent->filesCount(); ++i) {
QJsonObject fileDict = {
for (int i = 0; i < torrent->filesCount(); ++i)
{
QJsonObject fileDict =
{
{KEY_FILE_PROGRESS, fp[i]},
{KEY_FILE_PRIORITY, static_cast<int>(priorities[i])},
{KEY_FILE_SIZE, torrent->fileSize(i)},
@@ -528,7 +547,8 @@ void TorrentsController::pieceStatesAction()
pieceStates.append(static_cast<int>(states[i]) * 2);
const QBitArray dlstates = torrent->downloadingPieces();
for (int i = 0; i < states.size(); ++i) {
for (int i = 0; i < states.size(); ++i)
{
if (dlstates[i])
pieceStates[i] = 1;
}
@@ -554,12 +574,15 @@ void TorrentsController::addAction()
const TriStateBool autoTMM = parseTriStateBool(params()["autoTMM"]);
QList<QNetworkCookie> cookies;
if (!cookie.isEmpty()) {
if (!cookie.isEmpty())
{
const QStringList cookiesStr = cookie.split("; ");
for (QString cookieStr : cookiesStr) {
for (QString cookieStr : cookiesStr)
{
cookieStr = cookieStr.trimmed();
int index = cookieStr.indexOf('=');
if (index > 1) {
if (index > 1)
{
QByteArray name = cookieStr.left(index).toLatin1();
QByteArray value = cookieStr.right(cookieStr.length() - index - 1).toLatin1();
cookies += QNetworkCookie(name, value);
@@ -582,17 +605,21 @@ void TorrentsController::addAction()
params.useAutoTMM = autoTMM;
bool partialSuccess = false;
for (QString url : asConst(urls.split('\n'))) {
for (QString url : asConst(urls.split('\n')))
{
url = url.trimmed();
if (!url.isEmpty()) {
if (!url.isEmpty())
{
Net::DownloadManager::instance()->setCookiesFromUrl(cookies, QUrl::fromEncoded(url.toUtf8()));
partialSuccess |= BitTorrent::Session::instance()->addTorrent(url, params);
}
}
for (auto it = data().constBegin(); it != data().constEnd(); ++it) {
for (auto it = data().constBegin(); it != data().constEnd(); ++it)
{
const BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::load(it.value());
if (!torrentInfo.isValid()) {
if (!torrentInfo.isValid())
{
throw APIError(APIErrorType::BadData
, tr("Error: '%1' is not a valid torrent file.").arg(it.key()));
}
@@ -616,7 +643,8 @@ void TorrentsController::addTrackersAction()
throw APIError(APIErrorType::NotFound);
QVector<BitTorrent::TrackerEntry> trackers;
for (const QString &urlStr : asConst(params()["urls"].split('\n'))) {
for (const QString &urlStr : asConst(params()["urls"].split('\n')))
{
const QUrl url {urlStr.trimmed()};
if (url.isValid())
trackers << url.toString();
@@ -645,11 +673,13 @@ void TorrentsController::editTrackerAction()
QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
bool match = false;
for (BitTorrent::TrackerEntry &tracker : trackers) {
for (BitTorrent::TrackerEntry &tracker : trackers)
{
const QUrl trackerUrl(tracker.url());
if (trackerUrl == newTrackerUrl)
throw APIError(APIErrorType::Conflict, "New tracker URL already exists");
if (trackerUrl == origTrackerUrl) {
if (trackerUrl == origTrackerUrl)
{
match = true;
BitTorrent::TrackerEntry newTracker(newTrackerUrl.toString());
newTracker.setTier(tracker.tier());
@@ -679,7 +709,8 @@ void TorrentsController::removeTrackersAction()
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
QVector<BitTorrent::TrackerEntry> remainingTrackers;
remainingTrackers.reserve(trackers.size());
for (const BitTorrent::TrackerEntry &entry : trackers) {
for (const BitTorrent::TrackerEntry &entry : trackers)
{
if (!urls.contains(entry.url()))
remainingTrackers.push_back(entry);
}
@@ -702,7 +733,8 @@ void TorrentsController::addPeersAction()
QVector<BitTorrent::PeerAddress> peerList;
peerList.reserve(peers.size());
for (const QString &peer : peers) {
for (const QString &peer : peers)
{
const BitTorrent::PeerAddress addr = BitTorrent::PeerAddress::parse(peer.trimmed());
if (!addr.ip.isNull())
peerList.append(addr);
@@ -720,7 +752,8 @@ void TorrentsController::addPeersAction()
return torrent->connectPeer(peer);
});
results[torrent->hash()] = QJsonObject {
results[torrent->hash()] = QJsonObject
{
{"added", peersAdded},
{"failed", (peers.size() - peersAdded)}
};
@@ -767,14 +800,16 @@ void TorrentsController::filePrioAction()
const int filesCount = torrent->filesCount();
QVector<BitTorrent::DownloadPriority> priorities = torrent->filePriorities();
bool priorityChanged = false;
for (const QString &fileID : params()["id"].split('|')) {
for (const QString &fileID : params()["id"].split('|'))
{
const int id = fileID.toInt(&ok);
if (!ok)
throw APIError(APIErrorType::BadParams, tr("File IDs must be integers"));
if ((id < 0) || (id >= filesCount))
throw APIError(APIErrorType::Conflict, tr("File ID is not valid"));
if (priorities[id] != priority) {
if (priorities[id] != priority)
{
priorities[id] = priority;
priorityChanged = true;
}
@@ -790,7 +825,8 @@ void TorrentsController::uploadLimitAction()
const QStringList hashes {params()["hashes"].split('|')};
QJsonObject map;
for (const QString &hash : hashes) {
for (const QString &hash : hashes)
{
int limit = -1;
const BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
if (torrent)
@@ -807,7 +843,8 @@ void TorrentsController::downloadLimitAction()
const QStringList hashes {params()["hashes"].split('|')};
QJsonObject map;
for (const QString &hash : hashes) {
for (const QString &hash : hashes)
{
int limit = -1;
const BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
if (torrent)
@@ -1082,9 +1119,11 @@ void TorrentsController::categoriesAction()
{
QJsonObject categories;
const QStringMap categoriesMap = BitTorrent::Session::instance()->categories();
for (auto it = categoriesMap.cbegin(); it != categoriesMap.cend(); ++it) {
for (auto it = categoriesMap.cbegin(); it != categoriesMap.cend(); ++it)
{
const auto &key = it.key();
categories[key] = QJsonObject {
categories[key] = QJsonObject
{
{"name", key},
{"savePath", it.value()}
};
@@ -1100,7 +1139,8 @@ void TorrentsController::addTagsAction()
const QStringList hashes {params()["hashes"].split('|')};
const QStringList tags {params()["tags"].split(',', QString::SkipEmptyParts)};
for (const QString &tag : tags) {
for (const QString &tag : tags)
{
const QString tagTrimmed {tag.trimmed()};
applyToTorrents(hashes, [&tagTrimmed](BitTorrent::TorrentHandle *const torrent)
{
@@ -1116,7 +1156,8 @@ void TorrentsController::removeTagsAction()
const QStringList hashes {params()["hashes"].split('|')};
const QStringList tags {params()["tags"].split(',', QString::SkipEmptyParts)};
for (const QString &tag : tags) {
for (const QString &tag : tags)
{
const QString tagTrimmed {tag.trimmed()};
applyToTorrents(hashes, [&tagTrimmed](BitTorrent::TorrentHandle *const torrent)
{
@@ -1124,7 +1165,8 @@ void TorrentsController::removeTagsAction()
});
}
if (tags.isEmpty()) {
if (tags.isEmpty())
{
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent)
{
torrent->removeAllTags();
@@ -1193,7 +1235,8 @@ void TorrentsController::renameFileAction()
return;
// check if new name is already used
for (int i = 0; i < torrent->filesCount(); ++i) {
for (int i = 0; i < torrent->filesCount(); ++i)
{
if (i == fileIndex) continue;
if (Utils::Fs::sameFileNames(torrent->filePath(i), newFilePath))
throw APIError(APIErrorType::Conflict, tr("Name is already in use"));

View File

@@ -122,7 +122,8 @@ void TransferController::banPeersAction()
requireParams({"peers"});
const QStringList peers = params()["peers"].split('|');
for (const QString &peer : peers) {
for (const QString &peer : peers)
{
const BitTorrent::PeerAddress addr = BitTorrent::PeerAddress::parse(peer.trimmed());
if (!addr.ip.isNull())
BitTorrent::Session::instance()->banIP(addr.ip.toString());

View File

@@ -77,7 +77,8 @@ namespace
QStringMap ret;
const QVector<QStringRef> cookies = cookieStr.splitRef(';', QString::SkipEmptyParts);
for (const auto &cookie : cookies) {
for (const auto &cookie : cookies)
{
const int idx = cookie.indexOf('=');
if (idx < 0)
continue;
@@ -104,7 +105,8 @@ namespace
return QLatin1String("private, max-age=604800"); // 1 week
if ((contentType == Http::CONTENT_TYPE_CSS)
|| (contentType == Http::CONTENT_TYPE_JS)) {
|| (contentType == Http::CONTENT_TYPE_JS))
{
// short interval in case of program update
return QLatin1String("private, max-age=43200"); // 12 hrs
}
@@ -144,21 +146,25 @@ void WebApplication::sendWebUIFile()
if (pathItems.contains(".") || pathItems.contains(".."))
throw InternalServerErrorHTTPError();
if (!m_isAltUIUsed) {
if (request().path.startsWith(PATH_PREFIX_ICONS)) {
if (!m_isAltUIUsed)
{
if (request().path.startsWith(PATH_PREFIX_ICONS))
{
const QString imageFilename {request().path.mid(PATH_PREFIX_ICONS.size())};
sendFile(QLatin1String(":/icons/") + imageFilename);
return;
}
}
const QString path {
const QString path
{
(request().path != QLatin1String("/")
? request().path
: QLatin1String("/index.html"))
};
QString localPath {
QString localPath
{
m_rootFolder
+ (session() ? PRIVATE_FOLDER : PUBLIC_FOLDER)
+ path
@@ -166,22 +172,26 @@ void WebApplication::sendWebUIFile()
QFileInfo fileInfo {localPath};
if (!fileInfo.exists() && session()) {
if (!fileInfo.exists() && session())
{
// try to send public file if there is no private one
localPath = m_rootFolder + PUBLIC_FOLDER + path;
fileInfo.setFile(localPath);
}
if (m_isAltUIUsed) {
if (m_isAltUIUsed)
{
#ifdef Q_OS_UNIX
if (!Utils::Fs::isRegularFile(localPath)) {
if (!Utils::Fs::isRegularFile(localPath))
{
status(500, "Internal Server Error");
print(tr("Unacceptable file type, only regular file is allowed."), Http::CONTENT_TYPE_TXT);
return;
}
#endif
while (fileInfo.filePath() != m_rootFolder) {
while (fileInfo.filePath() != m_rootFolder)
{
if (fileInfo.isSymLink())
throw InternalServerErrorHTTPError(tr("Symlinks inside alternative UI folder are forbidden."));
@@ -198,10 +208,12 @@ void WebApplication::translateDocument(QString &data) const
int i = 0;
bool found = true;
while (i < data.size() && found) {
while (i < data.size() && found)
{
QRegularExpressionMatch regexMatch;
i = data.indexOf(regex, i, &regexMatch);
if (i >= 0) {
if (i >= 0)
{
const QString sourceText = regexMatch.captured(1);
const QString context = regexMatch.captured(3);
@@ -219,7 +231,8 @@ void WebApplication::translateDocument(QString &data) const
data.replace(i, regexMatch.capturedLength(), translation);
i += translation.length();
}
else {
else
{
found = false; // no more translatable strings
}
@@ -246,7 +259,8 @@ const Http::Environment &WebApplication::env() const
void WebApplication::doProcessRequest()
{
const QRegularExpressionMatch match = m_apiPathPattern.match(request().path);
if (!match.hasMatch()) {
if (!match.hasMatch())
{
sendWebUIFile();
return;
}
@@ -265,9 +279,11 @@ void WebApplication::doProcessRequest()
for (const Http::UploadedFile &torrent : request().files)
data[torrent.filename] = torrent.data;
try {
try
{
const QVariant result = controller->run(action, m_params, data);
switch (result.userType()) {
switch (result.userType())
{
case QMetaType::QJsonDocument:
print(result.toJsonDocument().toJson(QJsonDocument::Compact), Http::CONTENT_TYPE_JSON);
break;
@@ -277,9 +293,11 @@ void WebApplication::doProcessRequest()
break;
}
}
catch (const APIError &error) {
catch (const APIError &error)
{
// re-throw as HTTPError
switch (error.type()) {
switch (error.type())
{
case APIErrorType::AccessDenied:
throw ForbiddenHTTPError(error.message());
case APIErrorType::BadData:
@@ -303,7 +321,8 @@ void WebApplication::configure()
const bool isAltUIUsed = pref->isAltWebUiEnabled();
const QString rootFolder = Utils::Fs::expandPathAbs(
!isAltUIUsed ? WWW_FOLDER : pref->getWebUiRootFolder());
if ((isAltUIUsed != m_isAltUIUsed) || (rootFolder != m_rootFolder)) {
if ((isAltUIUsed != m_isAltUIUsed) || (rootFolder != m_rootFolder))
{
m_isAltUIUsed = isAltUIUsed;
m_rootFolder = rootFolder;
m_translatedFiles.clear();
@@ -314,16 +333,19 @@ void WebApplication::configure()
}
const QString newLocale = pref->getLocale();
if (m_currentLocale != newLocale) {
if (m_currentLocale != newLocale)
{
m_currentLocale = newLocale;
m_translatedFiles.clear();
m_translationFileLoaded = m_translator.load(m_rootFolder + QLatin1String("/translations/webui_") + newLocale);
if (m_translationFileLoaded) {
if (m_translationFileLoaded)
{
LogMsg(tr("Web UI translation for selected locale (%1) has been successfully loaded.")
.arg(newLocale));
}
else {
else
{
LogMsg(tr("Couldn't load Web UI translation for selected locale (%1).").arg(newLocale), Log::WARNING);
}
}
@@ -361,13 +383,16 @@ void WebApplication::configure()
if (!contentSecurityPolicy.isEmpty())
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_CONTENT_SECURITY_POLICY), contentSecurityPolicy});
if (pref->isWebUICustomHTTPHeadersEnabled()) {
if (pref->isWebUICustomHTTPHeadersEnabled())
{
const QString customHeaders = pref->getWebUICustomHTTPHeaders().trimmed();
const QVector<QStringRef> customHeaderLines = customHeaders.splitRef('\n', QString::SkipEmptyParts);
for (const QStringRef &line : customHeaderLines) {
for (const QStringRef &line : customHeaderLines)
{
const int idx = line.indexOf(':');
if (idx < 0) {
if (idx < 0)
{
// require separator `:` to be present even if `value` field can be empty
LogMsg(tr("Missing ':' separator in WebUI custom HTTP header: \"%1\"").arg(line.toString()), Log::WARNING);
continue;
@@ -399,19 +424,22 @@ void WebApplication::sendFile(const QString &path)
// find translated file in cache
const auto it = m_translatedFiles.constFind(path);
if ((it != m_translatedFiles.constEnd()) && (lastModified <= it->lastModified)) {
if ((it != m_translatedFiles.constEnd()) && (lastModified <= it->lastModified))
{
print(it->data, it->mimeType);
setHeader({Http::HEADER_CACHE_CONTROL, getCachingInterval(it->mimeType)});
return;
}
QFile file {path};
if (!file.open(QIODevice::ReadOnly)) {
if (!file.open(QIODevice::ReadOnly))
{
qDebug("File %s was not found!", qUtf8Printable(path));
throw NotFoundHTTPError();
}
if (file.size() > MAX_ALLOWED_FILESIZE) {
if (file.size() > MAX_ALLOWED_FILESIZE)
{
qWarning("%s: exceeded the maximum allowed file size!", qUtf8Printable(path));
throw InternalServerErrorHTTPError(tr("Exceeded the maximum allowed file size (%1)!")
.arg(Utils::Misc::friendlyUnit(MAX_ALLOWED_FILESIZE)));
@@ -424,7 +452,8 @@ void WebApplication::sendFile(const QString &path)
const bool isTranslatable {mimeType.inherits(QLatin1String("text/plain"))};
// Translate the file
if (isTranslatable) {
if (isTranslatable)
{
QString dataStr {data};
translateDocument(dataStr);
data = dataStr.toUtf8();
@@ -443,28 +472,33 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons
m_env = env;
m_params.clear();
if (m_request.method == Http::METHOD_GET) {
if (m_request.method == Http::METHOD_GET)
{
for (auto iter = m_request.query.cbegin(); iter != m_request.query.cend(); ++iter)
m_params[iter.key()] = QString::fromUtf8(iter.value());
}
else {
else
{
m_params = m_request.posts;
}
// clear response
clear();
try {
try
{
// block suspicious requests
if ((m_isCSRFProtectionEnabled && isCrossSiteRequest(m_request))
|| (m_isHostHeaderValidationEnabled && !validateHostHeader(m_domainList))) {
|| (m_isHostHeaderValidationEnabled && !validateHostHeader(m_domainList)))
{
throw UnauthorizedHTTPError();
}
sessionInitialize();
doProcessRequest();
}
catch (const HTTPError &error) {
catch (const HTTPError &error)
{
status(error.statusCode(), error.statusText());
print((!error.message().isEmpty() ? error.message() : error.statusText()), Http::CONTENT_TYPE_TXT);
}
@@ -488,19 +522,24 @@ void WebApplication::sessionInitialize()
// TODO: Additional session check
if (!sessionId.isEmpty()) {
if (!sessionId.isEmpty())
{
m_currentSession = m_sessions.value(sessionId);
if (m_currentSession) {
if (m_currentSession->hasExpired(m_sessionTimeout)) {
if (m_currentSession)
{
if (m_currentSession->hasExpired(m_sessionTimeout))
{
// session is outdated - removing it
delete m_sessions.take(sessionId);
m_currentSession = nullptr;
}
else {
else
{
m_currentSession->updateTimestamp();
}
}
else {
else
{
qDebug() << Q_FUNC_INFO << "session does not exist!";
}
}
@@ -513,8 +552,10 @@ QString WebApplication::generateSid() const
{
QString sid;
do {
const quint32 tmp[] = {Utils::Random::rand(), Utils::Random::rand(), Utils::Random::rand()
do
{
const quint32 tmp[] =
{Utils::Random::rand(), Utils::Random::rand(), Utils::Random::rand()
, Utils::Random::rand(), Utils::Random::rand(), Utils::Random::rand()};
sid = QByteArray::fromRawData(reinterpret_cast<const char *>(tmp), sizeof(tmp)).toBase64();
}
@@ -544,7 +585,8 @@ void WebApplication::sessionStart()
// remove outdated sessions
Algorithm::removeIf(m_sessions, [this](const QString &, const WebSession *session)
{
if (session->hasExpired(m_sessionTimeout)) {
if (session->hasExpired(m_sessionTimeout))
{
delete session;
return true;
}
@@ -595,14 +637,16 @@ bool WebApplication::isCrossSiteRequest(const Http::Request &request) const
const QString originValue = request.headers.value(Http::HEADER_ORIGIN);
const QString refererValue = request.headers.value(Http::HEADER_REFERER);
if (originValue.isEmpty() && refererValue.isEmpty()) {
if (originValue.isEmpty() && refererValue.isEmpty())
{
// owasp.org recommends to block this request, but doing so will inevitably lead Web API users to spoof headers
// so lets be permissive here
return false;
}
// sent with CORS requests, as well as with POST requests
if (!originValue.isEmpty()) {
if (!originValue.isEmpty())
{
const bool isInvalid = !isSameOrigin(urlFromHostHeader(targetOrigin), originValue);
if (isInvalid)
LogMsg(tr("WebUI: Origin header & Target origin mismatch! Source IP: '%1'. Origin header: '%2'. Target origin: '%3'")
@@ -611,7 +655,8 @@ bool WebApplication::isCrossSiteRequest(const Http::Request &request) const
return isInvalid;
}
if (!refererValue.isEmpty()) {
if (!refererValue.isEmpty())
{
const bool isInvalid = !isSameOrigin(urlFromHostHeader(targetOrigin), refererValue);
if (isInvalid)
LogMsg(tr("WebUI: Referer header & Target origin mismatch! Source IP: '%1'. Referer header: '%2'. Target origin: '%3'")
@@ -630,7 +675,8 @@ bool WebApplication::validateHostHeader(const QStringList &domains) const
// (if present) try matching host header's port with local port
const int requestPort = hostHeader.port();
if ((requestPort != -1) && (m_env.localPort != requestPort)) {
if ((requestPort != -1) && (m_env.localPort != requestPort))
{
LogMsg(tr("WebUI: Invalid Host header, port mismatch. Request source IP: '%1'. Server port: '%2'. Received Host header: '%3'")
.arg(m_env.clientAddress.toString()).arg(m_env.localPort)
.arg(m_request.headers[Http::HEADER_HOST])
@@ -645,7 +691,8 @@ bool WebApplication::validateHostHeader(const QStringList &domains) const
return true;
// try matching host header with domain list
for (const auto &domain : domains) {
for (const auto &domain : domains)
{
QRegExp domainRegex(domain, Qt::CaseInsensitive, QRegExp::Wildcard);
if (requestHost.contains(domainRegex))
return true;

View File

@@ -56,31 +56,38 @@ void WebUI::configure()
const quint16 oldPort = m_port;
m_port = pref->getWebUiPort();
if (pref->isWebUiEnabled()) {
if (pref->isWebUiEnabled())
{
// UPnP/NAT-PMP
if (pref->useUPnPForWebUIPort()) {
if (m_port != oldPort) {
if (pref->useUPnPForWebUIPort())
{
if (m_port != oldPort)
{
Net::PortForwarder::instance()->deletePort(oldPort);
Net::PortForwarder::instance()->addPort(m_port);
}
}
else {
else
{
Net::PortForwarder::instance()->deletePort(oldPort);
}
// http server
const QString serverAddressString = pref->getWebUiAddress();
if (!m_httpServer) {
if (!m_httpServer)
{
m_webapp = new WebApplication(this);
m_httpServer = new Http::Server(m_webapp, this);
}
else {
else
{
if ((m_httpServer->serverAddress().toString() != serverAddressString)
|| (m_httpServer->serverPort() != m_port))
m_httpServer->close();
}
if (pref->isWebUiHttpsEnabled()) {
if (pref->isWebUiHttpsEnabled())
{
const auto readData = [](const QString &path) -> QByteArray
{
QFile file(path);
@@ -97,18 +104,22 @@ void WebUI::configure()
else
logger->addMessage(tr("Web UI: HTTPS setup failed, fallback to HTTP"), Log::CRITICAL);
}
else {
else
{
m_httpServer->disableHttps();
}
if (!m_httpServer->isListening()) {
if (!m_httpServer->isListening())
{
const auto address = (serverAddressString == "*" || serverAddressString.isEmpty())
? QHostAddress::Any : QHostAddress(serverAddressString);
bool success = m_httpServer->listen(address, m_port);
if (success) {
if (success)
{
logger->addMessage(tr("Web UI: Now listening on IP: %1, port: %2").arg(serverAddressString).arg(m_port));
}
else {
else
{
const QString errorMsg = tr("Web UI: Unable to bind to IP: %1, port: %2. Reason: %3")
.arg(serverAddressString).arg(m_port).arg(m_httpServer->errorString());
logger->addMessage(errorMsg, Log::CRITICAL);
@@ -120,17 +131,20 @@ void WebUI::configure()
}
// DynDNS
if (pref->isDynDNSEnabled()) {
if (pref->isDynDNSEnabled())
{
if (!m_dnsUpdater)
m_dnsUpdater = new Net::DNSUpdater(this);
else
m_dnsUpdater->updateCredentials();
}
else {
else
{
delete m_dnsUpdater;
}
}
else {
else
{
Net::PortForwarder::instance()->deletePort(oldPort);
delete m_httpServer;