2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2024 Jonathan Ketchker
4 * Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
5 * Copyright (C) 2006-2012 Christophe Dumez <chris@qbittorrent.org>
6 * Copyright (C) 2006-2012 Ishan Arora <ishan@qbittorrent.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * In addition, as a special exception, the copyright holders give permission to
23 * link this program with the OpenSSL project's "OpenSSL" library (or with
24 * modified versions of it that use the same license as the "OpenSSL" library),
25 * and distribute the linked executables. You must obey the GNU General Public
26 * License in all respects for all of the code used other than "OpenSSL". If you
27 * modify file(s), you may extend this exception to your version of the file(s),
28 * but you are not obligated to do so. If you do not wish to do so, delete this
29 * exception statement from your version.
32 #include "appcontroller.h"
37 #include <QCoreApplication>
40 #include <QDirIterator>
42 #include <QJsonDocument>
43 #include <QJsonObject>
44 #include <QNetworkCookie>
45 #include <QNetworkInterface>
46 #include <QRegularExpression>
47 #include <QStringList>
49 #include <QTranslator>
51 #include "base/bittorrent/session.h"
52 #include "base/global.h"
53 #include "base/interfaces/iapplication.h"
54 #include "base/net/downloadmanager.h"
55 #include "base/net/portforwarder.h"
56 #include "base/net/proxyconfigurationmanager.h"
57 #include "base/path.h"
58 #include "base/preferences.h"
59 #include "base/rss/rss_autodownloader.h"
60 #include "base/rss/rss_session.h"
61 #include "base/torrentfileguard.h"
62 #include "base/torrentfileswatcher.h"
63 #include "base/utils/datetime.h"
64 #include "base/utils/fs.h"
65 #include "base/utils/misc.h"
66 #include "base/utils/net.h"
67 #include "base/utils/password.h"
68 #include "base/utils/string.h"
69 #include "base/version.h"
71 #include "../webapplication.h"
73 using namespace std::chrono_literals
;
75 const QString KEY_COOKIE_NAME
= u
"name"_s
;
76 const QString KEY_COOKIE_DOMAIN
= u
"domain"_s
;
77 const QString KEY_COOKIE_PATH
= u
"path"_s
;
78 const QString KEY_COOKIE_VALUE
= u
"value"_s
;
79 const QString KEY_COOKIE_EXPIRATION_DATE
= u
"expirationDate"_s
;
81 void AppController::webapiVersionAction()
83 setResult(API_VERSION
.toString());
86 void AppController::versionAction()
88 setResult(QStringLiteral(QBT_VERSION
));
91 void AppController::buildInfoAction()
93 const QString platformName
=
96 #elif defined(Q_OS_MACOS)
98 #elif defined(Q_OS_WIN)
104 const QJsonObject versions
=
106 {u
"qt"_s
, QStringLiteral(QT_VERSION_STR
)},
107 {u
"libtorrent"_s
, Utils::Misc::libtorrentVersionString()},
108 {u
"boost"_s
, Utils::Misc::boostVersionString()},
109 {u
"openssl"_s
, Utils::Misc::opensslVersionString()},
110 {u
"zlib"_s
, Utils::Misc::zlibVersionString()},
111 {u
"bitness"_s
, (QT_POINTER_SIZE
* 8)},
112 {u
"platform"_s
, platformName
}
117 void AppController::shutdownAction()
119 // Special handling for shutdown, we
120 // need to reply to the WebUI before
121 // actually shutting down.
122 QTimer::singleShot(100ms
, Qt::CoarseTimer
, qApp
, []
124 QCoreApplication::exit();
128 void AppController::preferencesAction()
130 const auto *pref
= Preferences::instance();
131 const auto *session
= BitTorrent::Session::instance();
137 data
[u
"locale"_s
] = pref
->getLocale();
138 data
[u
"performance_warning"_s
] = session
->isPerformanceWarningEnabled();
139 data
[u
"status_bar_external_ip"_s
] = pref
->isStatusbarExternalIPDisplayed();
141 data
[u
"confirm_torrent_deletion"_s
] = pref
->confirmTorrentDeletion();
143 data
[u
"file_log_enabled"_s
] = app()->isFileLoggerEnabled();
144 data
[u
"file_log_path"_s
] = app()->fileLoggerPath().toString();
145 data
[u
"file_log_backup_enabled"_s
] = app()->isFileLoggerBackup();
146 data
[u
"file_log_max_size"_s
] = app()->fileLoggerMaxSize() / 1024;
147 data
[u
"file_log_delete_old"_s
] = app()->isFileLoggerDeleteOld();
148 data
[u
"file_log_age"_s
] = app()->fileLoggerAge();
149 data
[u
"file_log_age_type"_s
] = app()->fileLoggerAgeType();
150 // Delete torrent contents files on torrent removal
151 data
[u
"delete_torrent_content_files"_s
] = pref
->removeTorrentContent();
154 // When adding a torrent
155 data
[u
"torrent_content_layout"_s
] = Utils::String::fromEnum(session
->torrentContentLayout());
156 data
[u
"add_to_top_of_queue"_s
] = session
->isAddTorrentToQueueTop();
157 data
[u
"add_stopped_enabled"_s
] = session
->isAddTorrentStopped();
158 data
[u
"torrent_stop_condition"_s
] = Utils::String::fromEnum(session
->torrentStopCondition());
159 data
[u
"merge_trackers"_s
] = session
->isMergeTrackersEnabled();
160 data
[u
"auto_delete_mode"_s
] = static_cast<int>(TorrentFileGuard::autoDeleteMode());
161 data
[u
"preallocate_all"_s
] = session
->isPreallocationEnabled();
162 data
[u
"incomplete_files_ext"_s
] = session
->isAppendExtensionEnabled();
163 data
[u
"use_unwanted_folder"_s
] = session
->isUnwantedFolderEnabled();
165 data
[u
"auto_tmm_enabled"_s
] = !session
->isAutoTMMDisabledByDefault();
166 data
[u
"torrent_changed_tmm_enabled"_s
] = !session
->isDisableAutoTMMWhenCategoryChanged();
167 data
[u
"save_path_changed_tmm_enabled"_s
] = !session
->isDisableAutoTMMWhenDefaultSavePathChanged();
168 data
[u
"category_changed_tmm_enabled"_s
] = !session
->isDisableAutoTMMWhenCategorySavePathChanged();
169 data
[u
"use_subcategories"] = session
->isSubcategoriesEnabled();
170 data
[u
"save_path"_s
] = session
->savePath().toString();
171 data
[u
"temp_path_enabled"_s
] = session
->isDownloadPathEnabled();
172 data
[u
"temp_path"_s
] = session
->downloadPath().toString();
173 data
[u
"use_category_paths_in_manual_mode"_s
] = session
->useCategoryPathsInManualMode();
174 data
[u
"export_dir"_s
] = session
->torrentExportDirectory().toString();
175 data
[u
"export_dir_fin"_s
] = session
->finishedTorrentExportDirectory().toString();
177 // TODO: The following code is deprecated. Delete it once replaced by updated API method.
178 // === BEGIN DEPRECATED CODE === //
179 TorrentFilesWatcher
*fsWatcher
= TorrentFilesWatcher::instance();
180 const QHash
<Path
, TorrentFilesWatcher::WatchedFolderOptions
> watchedFolders
= fsWatcher
->folders();
181 QJsonObject nativeDirs
;
182 for (auto i
= watchedFolders
.cbegin(); i
!= watchedFolders
.cend(); ++i
)
184 const Path
&watchedFolder
= i
.key();
185 const BitTorrent::AddTorrentParams params
= i
.value().addTorrentParams
;
186 if (params
.savePath
.isEmpty())
187 nativeDirs
.insert(watchedFolder
.toString(), 1);
188 else if (params
.savePath
== watchedFolder
)
189 nativeDirs
.insert(watchedFolder
.toString(), 0);
191 nativeDirs
.insert(watchedFolder
.toString(), params
.savePath
.toString());
193 data
[u
"scan_dirs"_s
] = nativeDirs
;
194 // === END DEPRECATED CODE === //
196 // Excluded file names
197 data
[u
"excluded_file_names_enabled"_s
] = session
->isExcludedFileNamesEnabled();
198 data
[u
"excluded_file_names"_s
] = session
->excludedFileNames().join(u
'\n');
200 // Email notification upon download completion
201 data
[u
"mail_notification_enabled"_s
] = pref
->isMailNotificationEnabled();
202 data
[u
"mail_notification_sender"_s
] = pref
->getMailNotificationSender();
203 data
[u
"mail_notification_email"_s
] = pref
->getMailNotificationEmail();
204 data
[u
"mail_notification_smtp"_s
] = pref
->getMailNotificationSMTP();
205 data
[u
"mail_notification_ssl_enabled"_s
] = pref
->getMailNotificationSMTPSSL();
206 data
[u
"mail_notification_auth_enabled"_s
] = pref
->getMailNotificationSMTPAuth();
207 data
[u
"mail_notification_username"_s
] = pref
->getMailNotificationSMTPUsername();
208 data
[u
"mail_notification_password"_s
] = pref
->getMailNotificationSMTPPassword();
209 // Run an external program on torrent added
210 data
[u
"autorun_on_torrent_added_enabled"_s
] = pref
->isAutoRunOnTorrentAddedEnabled();
211 data
[u
"autorun_on_torrent_added_program"_s
] = pref
->getAutoRunOnTorrentAddedProgram();
212 // Run an external program on torrent finished
213 data
[u
"autorun_enabled"_s
] = pref
->isAutoRunOnTorrentFinishedEnabled();
214 data
[u
"autorun_program"_s
] = pref
->getAutoRunOnTorrentFinishedProgram();
218 data
[u
"listen_port"_s
] = session
->port();
219 data
[u
"ssl_enabled"_s
] = session
->isSSLEnabled();
220 data
[u
"ssl_listen_port"_s
] = session
->sslPort();
221 data
[u
"random_port"_s
] = (session
->port() == 0); // deprecated
222 data
[u
"upnp"_s
] = Net::PortForwarder::instance()->isEnabled();
223 // Connections Limits
224 data
[u
"max_connec"_s
] = session
->maxConnections();
225 data
[u
"max_connec_per_torrent"_s
] = session
->maxConnectionsPerTorrent();
226 data
[u
"max_uploads"_s
] = session
->maxUploads();
227 data
[u
"max_uploads_per_torrent"_s
] = session
->maxUploadsPerTorrent();
230 data
[u
"i2p_enabled"_s
] = session
->isI2PEnabled();
231 data
[u
"i2p_address"_s
] = session
->I2PAddress();
232 data
[u
"i2p_port"_s
] = session
->I2PPort();
233 data
[u
"i2p_mixed_mode"_s
] = session
->I2PMixedMode();
234 data
[u
"i2p_inbound_quantity"_s
] = session
->I2PInboundQuantity();
235 data
[u
"i2p_outbound_quantity"_s
] = session
->I2POutboundQuantity();
236 data
[u
"i2p_inbound_length"_s
] = session
->I2PInboundLength();
237 data
[u
"i2p_outbound_length"_s
] = session
->I2POutboundLength();
240 const auto *proxyManager
= Net::ProxyConfigurationManager::instance();
241 Net::ProxyConfiguration proxyConf
= proxyManager
->proxyConfiguration();
242 data
[u
"proxy_type"_s
] = Utils::String::fromEnum(proxyConf
.type
);
243 data
[u
"proxy_ip"_s
] = proxyConf
.ip
;
244 data
[u
"proxy_port"_s
] = proxyConf
.port
;
245 data
[u
"proxy_auth_enabled"_s
] = proxyConf
.authEnabled
;
246 data
[u
"proxy_username"_s
] = proxyConf
.username
;
247 data
[u
"proxy_password"_s
] = proxyConf
.password
;
248 data
[u
"proxy_hostname_lookup"_s
] = proxyConf
.hostnameLookupEnabled
;
250 data
[u
"proxy_bittorrent"_s
] = pref
->useProxyForBT();
251 data
[u
"proxy_peer_connections"_s
] = session
->isProxyPeerConnectionsEnabled();
252 data
[u
"proxy_rss"_s
] = pref
->useProxyForRSS();
253 data
[u
"proxy_misc"_s
] = pref
->useProxyForGeneralPurposes();
256 data
[u
"ip_filter_enabled"_s
] = session
->isIPFilteringEnabled();
257 data
[u
"ip_filter_path"_s
] = session
->IPFilterFile().toString();
258 data
[u
"ip_filter_trackers"_s
] = session
->isTrackerFilteringEnabled();
259 data
[u
"banned_IPs"_s
] = session
->bannedIPs().join(u
'\n');
262 // Global Rate Limits
263 data
[u
"dl_limit"_s
] = session
->globalDownloadSpeedLimit();
264 data
[u
"up_limit"_s
] = session
->globalUploadSpeedLimit();
265 data
[u
"alt_dl_limit"_s
] = session
->altGlobalDownloadSpeedLimit();
266 data
[u
"alt_up_limit"_s
] = session
->altGlobalUploadSpeedLimit();
267 data
[u
"bittorrent_protocol"_s
] = static_cast<int>(session
->btProtocol());
268 data
[u
"limit_utp_rate"_s
] = session
->isUTPRateLimited();
269 data
[u
"limit_tcp_overhead"_s
] = session
->includeOverheadInLimits();
270 data
[u
"limit_lan_peers"_s
] = !session
->ignoreLimitsOnLAN();
272 data
[u
"scheduler_enabled"_s
] = session
->isBandwidthSchedulerEnabled();
273 const QTime start_time
= pref
->getSchedulerStartTime();
274 data
[u
"schedule_from_hour"_s
] = start_time
.hour();
275 data
[u
"schedule_from_min"_s
] = start_time
.minute();
276 const QTime end_time
= pref
->getSchedulerEndTime();
277 data
[u
"schedule_to_hour"_s
] = end_time
.hour();
278 data
[u
"schedule_to_min"_s
] = end_time
.minute();
279 data
[u
"scheduler_days"_s
] = static_cast<int>(pref
->getSchedulerDays());
283 data
[u
"dht"_s
] = session
->isDHTEnabled();
284 data
[u
"pex"_s
] = session
->isPeXEnabled();
285 data
[u
"lsd"_s
] = session
->isLSDEnabled();
286 data
[u
"encryption"_s
] = session
->encryption();
287 data
[u
"anonymous_mode"_s
] = session
->isAnonymousModeEnabled();
288 // Max active checking torrents
289 data
[u
"max_active_checking_torrents"_s
] = session
->maxActiveCheckingTorrents();
291 data
[u
"queueing_enabled"_s
] = session
->isQueueingSystemEnabled();
292 data
[u
"max_active_downloads"_s
] = session
->maxActiveDownloads();
293 data
[u
"max_active_torrents"_s
] = session
->maxActiveTorrents();
294 data
[u
"max_active_uploads"_s
] = session
->maxActiveUploads();
295 data
[u
"dont_count_slow_torrents"_s
] = session
->ignoreSlowTorrentsForQueueing();
296 data
[u
"slow_torrent_dl_rate_threshold"_s
] = session
->downloadRateForSlowTorrents();
297 data
[u
"slow_torrent_ul_rate_threshold"_s
] = session
->uploadRateForSlowTorrents();
298 data
[u
"slow_torrent_inactive_timer"_s
] = session
->slowTorrentsInactivityTimer();
299 // Share Ratio Limiting
300 data
[u
"max_ratio_enabled"_s
] = (session
->globalMaxRatio() >= 0.);
301 data
[u
"max_ratio"_s
] = session
->globalMaxRatio();
302 data
[u
"max_seeding_time_enabled"_s
] = (session
->globalMaxSeedingMinutes() >= 0.);
303 data
[u
"max_seeding_time"_s
] = session
->globalMaxSeedingMinutes();
304 data
[u
"max_inactive_seeding_time_enabled"_s
] = (session
->globalMaxInactiveSeedingMinutes() >= 0.);
305 data
[u
"max_inactive_seeding_time"_s
] = session
->globalMaxInactiveSeedingMinutes();
306 data
[u
"max_ratio_act"_s
] = static_cast<int>(session
->shareLimitAction());
308 data
[u
"add_trackers_enabled"_s
] = session
->isAddTrackersEnabled();
309 data
[u
"add_trackers"_s
] = session
->additionalTrackers();
313 data
[u
"web_ui_domain_list"_s
] = pref
->getServerDomains();
314 data
[u
"web_ui_address"_s
] = pref
->getWebUIAddress();
315 data
[u
"web_ui_port"_s
] = pref
->getWebUIPort();
316 data
[u
"web_ui_upnp"_s
] = pref
->useUPnPForWebUIPort();
317 data
[u
"use_https"_s
] = pref
->isWebUIHttpsEnabled();
318 data
[u
"web_ui_https_cert_path"_s
] = pref
->getWebUIHttpsCertificatePath().toString();
319 data
[u
"web_ui_https_key_path"_s
] = pref
->getWebUIHttpsKeyPath().toString();
321 data
[u
"web_ui_username"_s
] = pref
->getWebUIUsername();
322 data
[u
"bypass_local_auth"_s
] = !pref
->isWebUILocalAuthEnabled();
323 data
[u
"bypass_auth_subnet_whitelist_enabled"_s
] = pref
->isWebUIAuthSubnetWhitelistEnabled();
324 QStringList authSubnetWhitelistStringList
;
325 for (const Utils::Net::Subnet
&subnet
: asConst(pref
->getWebUIAuthSubnetWhitelist()))
326 authSubnetWhitelistStringList
<< Utils::Net::subnetToString(subnet
);
327 data
[u
"bypass_auth_subnet_whitelist"_s
] = authSubnetWhitelistStringList
.join(u
'\n');
328 data
[u
"web_ui_max_auth_fail_count"_s
] = pref
->getWebUIMaxAuthFailCount();
329 data
[u
"web_ui_ban_duration"_s
] = static_cast<int>(pref
->getWebUIBanDuration().count());
330 data
[u
"web_ui_session_timeout"_s
] = pref
->getWebUISessionTimeout();
331 // Use alternative WebUI
332 data
[u
"alternative_webui_enabled"_s
] = pref
->isAltWebUIEnabled();
333 data
[u
"alternative_webui_path"_s
] = pref
->getWebUIRootFolder().toString();
335 data
[u
"web_ui_clickjacking_protection_enabled"_s
] = pref
->isWebUIClickjackingProtectionEnabled();
336 data
[u
"web_ui_csrf_protection_enabled"_s
] = pref
->isWebUICSRFProtectionEnabled();
337 data
[u
"web_ui_secure_cookie_enabled"_s
] = pref
->isWebUISecureCookieEnabled();
338 data
[u
"web_ui_host_header_validation_enabled"_s
] = pref
->isWebUIHostHeaderValidationEnabled();
339 // Custom HTTP headers
340 data
[u
"web_ui_use_custom_http_headers_enabled"_s
] = pref
->isWebUICustomHTTPHeadersEnabled();
341 data
[u
"web_ui_custom_http_headers"_s
] = pref
->getWebUICustomHTTPHeaders();
343 data
[u
"web_ui_reverse_proxy_enabled"_s
] = pref
->isWebUIReverseProxySupportEnabled();
344 data
[u
"web_ui_reverse_proxies_list"_s
] = pref
->getWebUITrustedReverseProxiesList();
345 // Update my dynamic domain name
346 data
[u
"dyndns_enabled"_s
] = pref
->isDynDNSEnabled();
347 data
[u
"dyndns_service"_s
] = static_cast<int>(pref
->getDynDNSService());
348 data
[u
"dyndns_username"_s
] = pref
->getDynDNSUsername();
349 data
[u
"dyndns_password"_s
] = pref
->getDynDNSPassword();
350 data
[u
"dyndns_domain"_s
] = pref
->getDynDomainName();
353 data
[u
"rss_refresh_interval"_s
] = RSS::Session::instance()->refreshInterval();
354 data
[u
"rss_fetch_delay"_s
] = static_cast<qlonglong
>(RSS::Session::instance()->fetchDelay().count());
355 data
[u
"rss_max_articles_per_feed"_s
] = RSS::Session::instance()->maxArticlesPerFeed();
356 data
[u
"rss_processing_enabled"_s
] = RSS::Session::instance()->isProcessingEnabled();
357 data
[u
"rss_auto_downloading_enabled"_s
] = RSS::AutoDownloader::instance()->isProcessingEnabled();
358 data
[u
"rss_download_repack_proper_episodes"_s
] = RSS::AutoDownloader::instance()->downloadRepacks();
359 data
[u
"rss_smart_episode_filters"_s
] = RSS::AutoDownloader::instance()->smartEpisodeFilters().join(u
'\n');
362 // qBitorrent preferences
363 // Resume data storage type
364 data
[u
"resume_data_storage_type"_s
] = Utils::String::fromEnum(session
->resumeDataStorageType());
365 // Torrent content removing mode
366 data
[u
"torrent_content_remove_option"_s
] = Utils::String::fromEnum(session
->torrentContentRemoveOption());
367 // Physical memory (RAM) usage limit
368 data
[u
"memory_working_set_limit"_s
] = app()->memoryWorkingSetLimit();
369 // Current network interface
370 data
[u
"current_network_interface"_s
] = session
->networkInterface();
371 // Current network interface name
372 data
[u
"current_interface_name"_s
] = session
->networkInterfaceName();
373 // Current network interface address
374 data
[u
"current_interface_address"_s
] = session
->networkInterfaceAddress();
375 // Save resume data interval
376 data
[u
"save_resume_data_interval"_s
] = session
->saveResumeDataInterval();
377 // Save statistics interval
378 data
[u
"save_statistics_interval"_s
] = static_cast<int>(session
->saveStatisticsInterval().count());
379 // .torrent file size limit
380 data
[u
"torrent_file_size_limit"_s
] = pref
->getTorrentFileSizeLimit();
381 // Confirm torrent recheck
382 data
[u
"confirm_torrent_recheck"_s
] = pref
->confirmTorrentRecheck();
383 // Recheck completed torrents
384 data
[u
"recheck_completed_torrents"_s
] = pref
->recheckTorrentsOnCompletion();
385 // Customize application instance name
386 data
[u
"app_instance_name"_s
] = app()->instanceName();
388 data
[u
"refresh_interval"_s
] = session
->refreshInterval();
389 // Resolve peer countries
390 data
[u
"resolve_peer_countries"_s
] = pref
->resolvePeerCountries();
391 // Reannounce to all trackers when ip/port changed
392 data
[u
"reannounce_when_address_changed"_s
] = session
->isReannounceWhenAddressChangedEnabled();
394 data
[u
"enable_embedded_tracker"_s
] = session
->isTrackerEnabled();
395 data
[u
"embedded_tracker_port"_s
] = pref
->getTrackerPort();
396 data
[u
"embedded_tracker_port_forwarding"_s
] = pref
->isTrackerPortForwardingEnabled();
398 data
[u
"mark_of_the_web"_s
] = pref
->isMarkOfTheWebEnabled();
400 data
[u
"ignore_ssl_errors"_s
] = pref
->isIgnoreSSLErrors();
401 // Python executable path
402 data
[u
"python_executable_path"_s
] = pref
->getPythonExecutablePath().toString();
404 // libtorrent preferences
405 // Bdecode depth limit
406 data
[u
"bdecode_depth_limit"_s
] = pref
->getBdecodeDepthLimit();
407 // Bdecode token limit
408 data
[u
"bdecode_token_limit"_s
] = pref
->getBdecodeTokenLimit();
410 data
[u
"async_io_threads"_s
] = session
->asyncIOThreads();
412 data
[u
"hashing_threads"_s
] = session
->hashingThreads();
414 data
[u
"file_pool_size"_s
] = session
->filePoolSize();
415 // Checking memory usage
416 data
[u
"checking_memory_use"_s
] = session
->checkingMemUsage();
418 data
[u
"disk_cache"_s
] = session
->diskCacheSize();
419 data
[u
"disk_cache_ttl"_s
] = session
->diskCacheTTL();
421 data
[u
"disk_queue_size"_s
] = session
->diskQueueSize();
423 data
[u
"disk_io_type"_s
] = static_cast<int>(session
->diskIOType());
425 data
[u
"disk_io_read_mode"_s
] = static_cast<int>(session
->diskIOReadMode());
426 // Disk IO write mode
427 data
[u
"disk_io_write_mode"_s
] = static_cast<int>(session
->diskIOWriteMode());
428 // Coalesce reads & writes
429 data
[u
"enable_coalesce_read_write"_s
] = session
->isCoalesceReadWriteEnabled();
430 // Piece Extent Affinity
431 data
[u
"enable_piece_extent_affinity"_s
] = session
->usePieceExtentAffinity();
433 data
[u
"enable_upload_suggestions"_s
] = session
->isSuggestModeEnabled();
434 // Send buffer watermark
435 data
[u
"send_buffer_watermark"_s
] = session
->sendBufferWatermark();
436 data
[u
"send_buffer_low_watermark"_s
] = session
->sendBufferLowWatermark();
437 data
[u
"send_buffer_watermark_factor"_s
] = session
->sendBufferWatermarkFactor();
438 // Outgoing connections per second
439 data
[u
"connection_speed"_s
] = session
->connectionSpeed();
440 // Socket send buffer size
441 data
[u
"socket_send_buffer_size"_s
] = session
->socketSendBufferSize();
442 // Socket receive buffer size
443 data
[u
"socket_receive_buffer_size"_s
] = session
->socketReceiveBufferSize();
444 // Socket listen backlog size
445 data
[u
"socket_backlog_size"_s
] = session
->socketBacklogSize();
447 data
[u
"outgoing_ports_min"_s
] = session
->outgoingPortsMin();
448 data
[u
"outgoing_ports_max"_s
] = session
->outgoingPortsMax();
449 // UPnP lease duration
450 data
[u
"upnp_lease_duration"_s
] = session
->UPnPLeaseDuration();
452 data
[u
"peer_tos"_s
] = session
->peerToS();
453 // uTP-TCP mixed mode
454 data
[u
"utp_tcp_mixed_mode"_s
] = static_cast<int>(session
->utpMixedMode());
455 // Support internationalized domain name (IDN)
456 data
[u
"idn_support_enabled"_s
] = session
->isIDNSupportEnabled();
457 // Multiple connections per IP
458 data
[u
"enable_multi_connections_from_same_ip"_s
] = session
->multiConnectionsPerIpEnabled();
459 // Validate HTTPS tracker certificate
460 data
[u
"validate_https_tracker_certificate"_s
] = session
->validateHTTPSTrackerCertificate();
462 data
[u
"ssrf_mitigation"_s
] = session
->isSSRFMitigationEnabled();
463 // Disallow connection to peers on privileged ports
464 data
[u
"block_peers_on_privileged_ports"_s
] = session
->blockPeersOnPrivilegedPorts();
466 data
[u
"upload_slots_behavior"_s
] = static_cast<int>(session
->chokingAlgorithm());
467 // Seed choking algorithm
468 data
[u
"upload_choking_algorithm"_s
] = static_cast<int>(session
->seedChokingAlgorithm());
470 data
[u
"announce_to_all_trackers"_s
] = session
->announceToAllTrackers();
471 data
[u
"announce_to_all_tiers"_s
] = session
->announceToAllTiers();
472 data
[u
"announce_ip"_s
] = session
->announceIP();
473 data
[u
"max_concurrent_http_announces"_s
] = session
->maxConcurrentHTTPAnnounces();
474 data
[u
"stop_tracker_timeout"_s
] = session
->stopTrackerTimeout();
476 data
[u
"peer_turnover"_s
] = session
->peerTurnover();
477 data
[u
"peer_turnover_cutoff"_s
] = session
->peerTurnoverCutoff();
478 data
[u
"peer_turnover_interval"_s
] = session
->peerTurnoverInterval();
479 // Maximum outstanding requests to a single peer
480 data
[u
"request_queue_size"_s
] = session
->requestQueueSize();
481 // DHT bootstrap nodes
482 data
[u
"dht_bootstrap_nodes"_s
] = session
->getDHTBootstrapNodes();
487 void AppController::setPreferencesAction()
489 requireParams({u
"json"_s
});
491 auto *pref
= Preferences::instance();
492 auto *session
= BitTorrent::Session::instance();
493 const QVariantHash m
= QJsonDocument::fromJson(params()[u
"json"_s
].toUtf8()).toVariant().toHash();
495 QVariantHash::ConstIterator it
;
496 const auto hasKey
= [&it
, &m
](const QString
&key
) -> bool
499 return (it
!= m
.constEnd());
504 if (hasKey(u
"locale"_s
))
506 QString locale
= it
.value().toString();
507 if (pref
->getLocale() != locale
)
509 auto *translator
= new QTranslator
;
510 if (translator
->load(u
":/lang/qbittorrent_"_s
+ locale
))
512 qDebug("%s locale recognized, using translation.", qUtf8Printable(locale
));
516 qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale
));
518 qApp
->installTranslator(translator
);
520 pref
->setLocale(locale
);
523 if (hasKey(u
"status_bar_external_ip"_s
))
524 pref
->setStatusbarExternalIPDisplayed(it
.value().toBool());
525 if (hasKey(u
"performance_warning"_s
))
526 session
->setPerformanceWarningEnabled(it
.value().toBool());
528 if (hasKey(u
"confirm_torrent_deletion"_s
))
529 pref
->setConfirmTorrentDeletion(it
.value().toBool());
531 if (hasKey(u
"file_log_enabled"_s
))
532 app()->setFileLoggerEnabled(it
.value().toBool());
533 if (hasKey(u
"file_log_path"_s
))
534 app()->setFileLoggerPath(Path(it
.value().toString()));
535 if (hasKey(u
"file_log_backup_enabled"_s
))
536 app()->setFileLoggerBackup(it
.value().toBool());
537 if (hasKey(u
"file_log_max_size"_s
))
538 app()->setFileLoggerMaxSize(it
.value().toInt() * 1024);
539 if (hasKey(u
"file_log_delete_old"_s
))
540 app()->setFileLoggerDeleteOld(it
.value().toBool());
541 if (hasKey(u
"file_log_age"_s
))
542 app()->setFileLoggerAge(it
.value().toInt());
543 if (hasKey(u
"file_log_age_type"_s
))
544 app()->setFileLoggerAgeType(it
.value().toInt());
545 // Delete torrent content files on torrent removal
546 if (hasKey(u
"delete_torrent_content_files"_s
))
547 pref
->setRemoveTorrentContent(it
.value().toBool());
550 // When adding a torrent
551 if (hasKey(u
"torrent_content_layout"_s
))
552 session
->setTorrentContentLayout(Utils::String::toEnum(it
.value().toString(), BitTorrent::TorrentContentLayout::Original
));
553 if (hasKey(u
"add_to_top_of_queue"_s
))
554 session
->setAddTorrentToQueueTop(it
.value().toBool());
555 if (hasKey(u
"add_stopped_enabled"_s
))
556 session
->setAddTorrentStopped(it
.value().toBool());
557 if (hasKey(u
"torrent_stop_condition"_s
))
558 session
->setTorrentStopCondition(Utils::String::toEnum(it
.value().toString(), BitTorrent::Torrent::StopCondition::None
));
559 if (hasKey(u
"merge_trackers"_s
))
560 session
->setMergeTrackersEnabled(it
.value().toBool());
561 if (hasKey(u
"auto_delete_mode"_s
))
562 TorrentFileGuard::setAutoDeleteMode(static_cast<TorrentFileGuard::AutoDeleteMode
>(it
.value().toInt()));
564 if (hasKey(u
"preallocate_all"_s
))
565 session
->setPreallocationEnabled(it
.value().toBool());
566 if (hasKey(u
"incomplete_files_ext"_s
))
567 session
->setAppendExtensionEnabled(it
.value().toBool());
568 if (hasKey(u
"use_unwanted_folder"_s
))
569 session
->setUnwantedFolderEnabled(it
.value().toBool());
572 if (hasKey(u
"auto_tmm_enabled"_s
))
573 session
->setAutoTMMDisabledByDefault(!it
.value().toBool());
574 if (hasKey(u
"torrent_changed_tmm_enabled"_s
))
575 session
->setDisableAutoTMMWhenCategoryChanged(!it
.value().toBool());
576 if (hasKey(u
"save_path_changed_tmm_enabled"_s
))
577 session
->setDisableAutoTMMWhenDefaultSavePathChanged(!it
.value().toBool());
578 if (hasKey(u
"category_changed_tmm_enabled"_s
))
579 session
->setDisableAutoTMMWhenCategorySavePathChanged(!it
.value().toBool());
580 if (hasKey(u
"use_subcategories"_s
))
581 session
->setSubcategoriesEnabled(it
.value().toBool());
582 if (hasKey(u
"save_path"_s
))
583 session
->setSavePath(Path(it
.value().toString()));
584 if (hasKey(u
"temp_path_enabled"_s
))
585 session
->setDownloadPathEnabled(it
.value().toBool());
586 if (hasKey(u
"temp_path"_s
))
587 session
->setDownloadPath(Path(it
.value().toString()));
588 if (hasKey(u
"use_category_paths_in_manual_mode"_s
))
589 session
->setUseCategoryPathsInManualMode(it
.value().toBool());
590 if (hasKey(u
"export_dir"_s
))
591 session
->setTorrentExportDirectory(Path(it
.value().toString()));
592 if (hasKey(u
"export_dir_fin"_s
))
593 session
->setFinishedTorrentExportDirectory(Path(it
.value().toString()));
595 // TODO: The following code is deprecated. Delete it once replaced by updated API method.
596 // === BEGIN DEPRECATED CODE === //
597 if (hasKey(u
"scan_dirs"_s
))
600 TorrentFilesWatcher
*fsWatcher
= TorrentFilesWatcher::instance();
601 const PathList oldScanDirs
= fsWatcher
->folders().keys();
602 const QVariantHash nativeDirs
= it
.value().toHash();
603 for (auto i
= nativeDirs
.cbegin(); i
!= nativeDirs
.cend(); ++i
)
607 const Path watchedFolder
{i
.key()};
608 TorrentFilesWatcher::WatchedFolderOptions options
= fsWatcher
->folders().value(watchedFolder
);
609 BitTorrent::AddTorrentParams
¶ms
= options
.addTorrentParams
;
612 const int intVal
= i
.value().toInt(&isInt
);
617 params
.savePath
= watchedFolder
;
618 params
.useAutoTMM
= false;
623 const Path customSavePath
{i
.value().toString()};
624 params
.savePath
= customSavePath
;
625 params
.useAutoTMM
= false;
628 fsWatcher
->setWatchedFolder(watchedFolder
, options
);
629 scanDirs
.append(watchedFolder
);
636 // Update deleted folders
637 for (const Path
&path
: oldScanDirs
)
639 if (!scanDirs
.contains(path
))
640 fsWatcher
->removeWatchedFolder(path
);
643 // === END DEPRECATED CODE === //
645 // Excluded file names
646 if (hasKey(u
"excluded_file_names_enabled"_s
))
647 session
->setExcludedFileNamesEnabled(it
.value().toBool());
648 if (hasKey(u
"excluded_file_names"_s
))
649 session
->setExcludedFileNames(it
.value().toString().split(u
'\n'));
651 // Email notification upon download completion
652 if (hasKey(u
"mail_notification_enabled"_s
))
653 pref
->setMailNotificationEnabled(it
.value().toBool());
654 if (hasKey(u
"mail_notification_sender"_s
))
655 pref
->setMailNotificationSender(it
.value().toString());
656 if (hasKey(u
"mail_notification_email"_s
))
657 pref
->setMailNotificationEmail(it
.value().toString());
658 if (hasKey(u
"mail_notification_smtp"_s
))
659 pref
->setMailNotificationSMTP(it
.value().toString());
660 if (hasKey(u
"mail_notification_ssl_enabled"_s
))
661 pref
->setMailNotificationSMTPSSL(it
.value().toBool());
662 if (hasKey(u
"mail_notification_auth_enabled"_s
))
663 pref
->setMailNotificationSMTPAuth(it
.value().toBool());
664 if (hasKey(u
"mail_notification_username"_s
))
665 pref
->setMailNotificationSMTPUsername(it
.value().toString());
666 if (hasKey(u
"mail_notification_password"_s
))
667 pref
->setMailNotificationSMTPPassword(it
.value().toString());
668 // Run an external program on torrent added
669 if (hasKey(u
"autorun_on_torrent_added_enabled"_s
))
670 pref
->setAutoRunOnTorrentAddedEnabled(it
.value().toBool());
671 if (hasKey(u
"autorun_on_torrent_added_program"_s
))
672 pref
->setAutoRunOnTorrentAddedProgram(it
.value().toString());
673 // Run an external program on torrent finished
674 if (hasKey(u
"autorun_enabled"_s
))
675 pref
->setAutoRunOnTorrentFinishedEnabled(it
.value().toBool());
676 if (hasKey(u
"autorun_program"_s
))
677 pref
->setAutoRunOnTorrentFinishedProgram(it
.value().toString());
681 if (hasKey(u
"random_port"_s
) && it
.value().toBool()) // deprecated
685 else if (hasKey(u
"listen_port"_s
))
687 session
->setPort(it
.value().toInt());
690 if (hasKey(u
"ssl_enabled"_s
))
691 session
->setSSLEnabled(it
.value().toBool());
692 if (hasKey(u
"ssl_listen_port"_s
))
693 session
->setSSLPort(it
.value().toInt());
694 if (hasKey(u
"upnp"_s
))
695 Net::PortForwarder::instance()->setEnabled(it
.value().toBool());
696 // Connections Limits
697 if (hasKey(u
"max_connec"_s
))
698 session
->setMaxConnections(it
.value().toInt());
699 if (hasKey(u
"max_connec_per_torrent"_s
))
700 session
->setMaxConnectionsPerTorrent(it
.value().toInt());
701 if (hasKey(u
"max_uploads"_s
))
702 session
->setMaxUploads(it
.value().toInt());
703 if (hasKey(u
"max_uploads_per_torrent"_s
))
704 session
->setMaxUploadsPerTorrent(it
.value().toInt());
707 if (hasKey(u
"i2p_enabled"_s
))
708 session
->setI2PEnabled(it
.value().toBool());
709 if (hasKey(u
"i2p_address"_s
))
710 session
->setI2PAddress(it
.value().toString());
711 if (hasKey(u
"i2p_port"_s
))
712 session
->setI2PPort(it
.value().toInt());
713 if (hasKey(u
"i2p_mixed_mode"_s
))
714 session
->setI2PMixedMode(it
.value().toBool());
715 if (hasKey(u
"i2p_inbound_quantity"_s
))
716 session
->setI2PInboundQuantity(it
.value().toInt());
717 if (hasKey(u
"i2p_outbound_quantity"_s
))
718 session
->setI2POutboundQuantity(it
.value().toInt());
719 if (hasKey(u
"i2p_inbound_length"_s
))
720 session
->setI2PInboundLength(it
.value().toInt());
721 if (hasKey(u
"i2p_outbound_length"_s
))
722 session
->setI2POutboundLength(it
.value().toInt());
725 auto *proxyManager
= Net::ProxyConfigurationManager::instance();
726 Net::ProxyConfiguration proxyConf
= proxyManager
->proxyConfiguration();
727 if (hasKey(u
"proxy_type"_s
))
728 proxyConf
.type
= Utils::String::toEnum(it
.value().toString(), Net::ProxyType::None
);
729 if (hasKey(u
"proxy_ip"_s
))
730 proxyConf
.ip
= it
.value().toString();
731 if (hasKey(u
"proxy_port"_s
))
732 proxyConf
.port
= it
.value().toUInt();
733 if (hasKey(u
"proxy_auth_enabled"_s
))
734 proxyConf
.authEnabled
= it
.value().toBool();
735 if (hasKey(u
"proxy_username"_s
))
736 proxyConf
.username
= it
.value().toString();
737 if (hasKey(u
"proxy_password"_s
))
738 proxyConf
.password
= it
.value().toString();
739 if (hasKey(u
"proxy_hostname_lookup"_s
))
740 proxyConf
.hostnameLookupEnabled
= it
.value().toBool();
741 proxyManager
->setProxyConfiguration(proxyConf
);
743 if (hasKey(u
"proxy_bittorrent"_s
))
744 pref
->setUseProxyForBT(it
.value().toBool());
745 if (hasKey(u
"proxy_peer_connections"_s
))
746 session
->setProxyPeerConnectionsEnabled(it
.value().toBool());
747 if (hasKey(u
"proxy_rss"_s
))
748 pref
->setUseProxyForRSS(it
.value().toBool());
749 if (hasKey(u
"proxy_misc"_s
))
750 pref
->setUseProxyForGeneralPurposes(it
.value().toBool());
753 if (hasKey(u
"ip_filter_enabled"_s
))
754 session
->setIPFilteringEnabled(it
.value().toBool());
755 if (hasKey(u
"ip_filter_path"_s
))
756 session
->setIPFilterFile(Path(it
.value().toString()));
757 if (hasKey(u
"ip_filter_trackers"_s
))
758 session
->setTrackerFilteringEnabled(it
.value().toBool());
759 if (hasKey(u
"banned_IPs"_s
))
760 session
->setBannedIPs(it
.value().toString().split(u
'\n', Qt::SkipEmptyParts
));
763 // Global Rate Limits
764 if (hasKey(u
"dl_limit"_s
))
765 session
->setGlobalDownloadSpeedLimit(it
.value().toInt());
766 if (hasKey(u
"up_limit"_s
))
767 session
->setGlobalUploadSpeedLimit(it
.value().toInt());
768 if (hasKey(u
"alt_dl_limit"_s
))
769 session
->setAltGlobalDownloadSpeedLimit(it
.value().toInt());
770 if (hasKey(u
"alt_up_limit"_s
))
771 session
->setAltGlobalUploadSpeedLimit(it
.value().toInt());
772 if (hasKey(u
"bittorrent_protocol"_s
))
773 session
->setBTProtocol(static_cast<BitTorrent::BTProtocol
>(it
.value().toInt()));
774 if (hasKey(u
"limit_utp_rate"_s
))
775 session
->setUTPRateLimited(it
.value().toBool());
776 if (hasKey(u
"limit_tcp_overhead"_s
))
777 session
->setIncludeOverheadInLimits(it
.value().toBool());
778 if (hasKey(u
"limit_lan_peers"_s
))
779 session
->setIgnoreLimitsOnLAN(!it
.value().toBool());
781 if (hasKey(u
"scheduler_enabled"_s
))
782 session
->setBandwidthSchedulerEnabled(it
.value().toBool());
783 if (m
.contains(u
"schedule_from_hour"_s
) && m
.contains(u
"schedule_from_min"_s
))
784 pref
->setSchedulerStartTime(QTime(m
[u
"schedule_from_hour"_s
].toInt(), m
[u
"schedule_from_min"_s
].toInt()));
785 if (m
.contains(u
"schedule_to_hour"_s
) && m
.contains(u
"schedule_to_min"_s
))
786 pref
->setSchedulerEndTime(QTime(m
[u
"schedule_to_hour"_s
].toInt(), m
[u
"schedule_to_min"_s
].toInt()));
787 if (hasKey(u
"scheduler_days"_s
))
788 pref
->setSchedulerDays(static_cast<Scheduler::Days
>(it
.value().toInt()));
792 if (hasKey(u
"dht"_s
))
793 session
->setDHTEnabled(it
.value().toBool());
794 if (hasKey(u
"pex"_s
))
795 session
->setPeXEnabled(it
.value().toBool());
796 if (hasKey(u
"lsd"_s
))
797 session
->setLSDEnabled(it
.value().toBool());
798 if (hasKey(u
"encryption"_s
))
799 session
->setEncryption(it
.value().toInt());
800 if (hasKey(u
"anonymous_mode"_s
))
801 session
->setAnonymousModeEnabled(it
.value().toBool());
802 // Max active checking torrents
803 if (hasKey(u
"max_active_checking_torrents"_s
))
804 session
->setMaxActiveCheckingTorrents(it
.value().toInt());
806 if (hasKey(u
"queueing_enabled"_s
))
807 session
->setQueueingSystemEnabled(it
.value().toBool());
808 if (hasKey(u
"max_active_downloads"_s
))
809 session
->setMaxActiveDownloads(it
.value().toInt());
810 if (hasKey(u
"max_active_torrents"_s
))
811 session
->setMaxActiveTorrents(it
.value().toInt());
812 if (hasKey(u
"max_active_uploads"_s
))
813 session
->setMaxActiveUploads(it
.value().toInt());
814 if (hasKey(u
"dont_count_slow_torrents"_s
))
815 session
->setIgnoreSlowTorrentsForQueueing(it
.value().toBool());
816 if (hasKey(u
"slow_torrent_dl_rate_threshold"_s
))
817 session
->setDownloadRateForSlowTorrents(it
.value().toInt());
818 if (hasKey(u
"slow_torrent_ul_rate_threshold"_s
))
819 session
->setUploadRateForSlowTorrents(it
.value().toInt());
820 if (hasKey(u
"slow_torrent_inactive_timer"_s
))
821 session
->setSlowTorrentsInactivityTimer(it
.value().toInt());
822 // Share Ratio Limiting
823 if (hasKey(u
"max_ratio_enabled"_s
))
825 if (it
.value().toBool())
826 session
->setGlobalMaxRatio(m
[u
"max_ratio"_s
].toReal());
828 session
->setGlobalMaxRatio(-1);
830 if (hasKey(u
"max_seeding_time_enabled"_s
))
832 if (it
.value().toBool())
833 session
->setGlobalMaxSeedingMinutes(m
[u
"max_seeding_time"_s
].toInt());
835 session
->setGlobalMaxSeedingMinutes(-1);
837 if (hasKey(u
"max_inactive_seeding_time_enabled"_s
))
839 session
->setGlobalMaxInactiveSeedingMinutes(it
.value().toBool()
840 ? m
[u
"max_inactive_seeding_time"_s
].toInt() : -1);
842 if (hasKey(u
"max_ratio_act"_s
))
844 switch (it
.value().toInt())
848 session
->setShareLimitAction(BitTorrent::ShareLimitAction::Stop
);
851 session
->setShareLimitAction(BitTorrent::ShareLimitAction::Remove
);
854 session
->setShareLimitAction(BitTorrent::ShareLimitAction::EnableSuperSeeding
);
857 session
->setShareLimitAction(BitTorrent::ShareLimitAction::RemoveWithContent
);
862 if (hasKey(u
"add_trackers_enabled"_s
))
863 session
->setAddTrackersEnabled(it
.value().toBool());
864 if (hasKey(u
"add_trackers"_s
))
865 session
->setAdditionalTrackers(it
.value().toString());
869 if (hasKey(u
"web_ui_domain_list"_s
))
870 pref
->setServerDomains(it
.value().toString());
871 if (hasKey(u
"web_ui_address"_s
))
872 pref
->setWebUIAddress(it
.value().toString());
873 if (hasKey(u
"web_ui_port"_s
))
874 pref
->setWebUIPort(it
.value().value
<quint16
>());
875 if (hasKey(u
"web_ui_upnp"_s
))
876 pref
->setUPnPForWebUIPort(it
.value().toBool());
877 if (hasKey(u
"use_https"_s
))
878 pref
->setWebUIHttpsEnabled(it
.value().toBool());
879 if (hasKey(u
"web_ui_https_cert_path"_s
))
880 pref
->setWebUIHttpsCertificatePath(Path(it
.value().toString()));
881 if (hasKey(u
"web_ui_https_key_path"_s
))
882 pref
->setWebUIHttpsKeyPath(Path(it
.value().toString()));
884 if (hasKey(u
"web_ui_username"_s
))
885 pref
->setWebUIUsername(it
.value().toString());
886 if (hasKey(u
"web_ui_password"_s
))
887 pref
->setWebUIPassword(Utils::Password::PBKDF2::generate(it
.value().toByteArray()));
888 if (hasKey(u
"bypass_local_auth"_s
))
889 pref
->setWebUILocalAuthEnabled(!it
.value().toBool());
890 if (hasKey(u
"bypass_auth_subnet_whitelist_enabled"_s
))
891 pref
->setWebUIAuthSubnetWhitelistEnabled(it
.value().toBool());
892 if (hasKey(u
"bypass_auth_subnet_whitelist"_s
))
894 // recognize new lines and commas as delimiters
895 pref
->setWebUIAuthSubnetWhitelist(it
.value().toString().split(QRegularExpression(u
"\n|,"_s
), Qt::SkipEmptyParts
));
897 if (hasKey(u
"web_ui_max_auth_fail_count"_s
))
898 pref
->setWebUIMaxAuthFailCount(it
.value().toInt());
899 if (hasKey(u
"web_ui_ban_duration"_s
))
900 pref
->setWebUIBanDuration(std::chrono::seconds
{it
.value().toInt()});
901 if (hasKey(u
"web_ui_session_timeout"_s
))
902 pref
->setWebUISessionTimeout(it
.value().toInt());
903 // Use alternative WebUI
904 if (hasKey(u
"alternative_webui_enabled"_s
))
905 pref
->setAltWebUIEnabled(it
.value().toBool());
906 if (hasKey(u
"alternative_webui_path"_s
))
907 pref
->setWebUIRootFolder(Path(it
.value().toString()));
909 if (hasKey(u
"web_ui_clickjacking_protection_enabled"_s
))
910 pref
->setWebUIClickjackingProtectionEnabled(it
.value().toBool());
911 if (hasKey(u
"web_ui_csrf_protection_enabled"_s
))
912 pref
->setWebUICSRFProtectionEnabled(it
.value().toBool());
913 if (hasKey(u
"web_ui_secure_cookie_enabled"_s
))
914 pref
->setWebUISecureCookieEnabled(it
.value().toBool());
915 if (hasKey(u
"web_ui_host_header_validation_enabled"_s
))
916 pref
->setWebUIHostHeaderValidationEnabled(it
.value().toBool());
917 // Custom HTTP headers
918 if (hasKey(u
"web_ui_use_custom_http_headers_enabled"_s
))
919 pref
->setWebUICustomHTTPHeadersEnabled(it
.value().toBool());
920 if (hasKey(u
"web_ui_custom_http_headers"_s
))
921 pref
->setWebUICustomHTTPHeaders(it
.value().toString());
923 if (hasKey(u
"web_ui_reverse_proxy_enabled"_s
))
924 pref
->setWebUIReverseProxySupportEnabled(it
.value().toBool());
925 if (hasKey(u
"web_ui_reverse_proxies_list"_s
))
926 pref
->setWebUITrustedReverseProxiesList(it
.value().toString());
927 // Update my dynamic domain name
928 if (hasKey(u
"dyndns_enabled"_s
))
929 pref
->setDynDNSEnabled(it
.value().toBool());
930 if (hasKey(u
"dyndns_service"_s
))
931 pref
->setDynDNSService(static_cast<DNS::Service
>(it
.value().toInt()));
932 if (hasKey(u
"dyndns_username"_s
))
933 pref
->setDynDNSUsername(it
.value().toString());
934 if (hasKey(u
"dyndns_password"_s
))
935 pref
->setDynDNSPassword(it
.value().toString());
936 if (hasKey(u
"dyndns_domain"_s
))
937 pref
->setDynDomainName(it
.value().toString());
939 if (hasKey(u
"rss_refresh_interval"_s
))
940 RSS::Session::instance()->setRefreshInterval(it
.value().toInt());
941 if (hasKey(u
"rss_fetch_delay"_s
))
942 RSS::Session::instance()->setFetchDelay(std::chrono::seconds(it
.value().toLongLong()));
943 if (hasKey(u
"rss_max_articles_per_feed"_s
))
944 RSS::Session::instance()->setMaxArticlesPerFeed(it
.value().toInt());
945 if (hasKey(u
"rss_processing_enabled"_s
))
946 RSS::Session::instance()->setProcessingEnabled(it
.value().toBool());
947 if (hasKey(u
"rss_auto_downloading_enabled"_s
))
948 RSS::AutoDownloader::instance()->setProcessingEnabled(it
.value().toBool());
949 if (hasKey(u
"rss_download_repack_proper_episodes"_s
))
950 RSS::AutoDownloader::instance()->setDownloadRepacks(it
.value().toBool());
951 if (hasKey(u
"rss_smart_episode_filters"_s
))
952 RSS::AutoDownloader::instance()->setSmartEpisodeFilters(it
.value().toString().split(u
'\n'));
955 // qBittorrent preferences
956 // Resume data storage type
957 if (hasKey(u
"resume_data_storage_type"_s
))
958 session
->setResumeDataStorageType(Utils::String::toEnum(it
.value().toString(), BitTorrent::ResumeDataStorageType::Legacy
));
959 // Torrent content removing mode
960 if (hasKey(u
"torrent_content_remove_option"_s
))
961 session
->setTorrentContentRemoveOption(Utils::String::toEnum(it
.value().toString(), BitTorrent::TorrentContentRemoveOption::MoveToTrash
));
962 // Physical memory (RAM) usage limit
963 if (hasKey(u
"memory_working_set_limit"_s
))
964 app()->setMemoryWorkingSetLimit(it
.value().toInt());
965 // Current network interface
966 if (hasKey(u
"current_network_interface"_s
))
968 const QString ifaceValue
{it
.value().toString()};
970 const QList
<QNetworkInterface
> ifaces
= QNetworkInterface::allInterfaces();
971 const auto ifacesIter
= std::find_if(ifaces
.cbegin(), ifaces
.cend(), [&ifaceValue
](const QNetworkInterface
&iface
)
973 return (!iface
.addressEntries().isEmpty()) && (iface
.name() == ifaceValue
);
975 const QString ifaceName
= (ifacesIter
!= ifaces
.cend()) ? ifacesIter
->humanReadableName() : QString
{};
977 session
->setNetworkInterface(ifaceValue
);
978 if (!ifaceName
.isEmpty() || ifaceValue
.isEmpty())
979 session
->setNetworkInterfaceName(ifaceName
);
981 // Current network interface address
982 if (hasKey(u
"current_interface_address"_s
))
984 const QHostAddress ifaceAddress
{it
.value().toString().trimmed()};
985 session
->setNetworkInterfaceAddress(ifaceAddress
.isNull() ? QString
{} : ifaceAddress
.toString());
987 // Save resume data interval
988 if (hasKey(u
"save_resume_data_interval"_s
))
989 session
->setSaveResumeDataInterval(it
.value().toInt());
990 // Save statistics interval
991 if (hasKey(u
"save_statistics_interval"_s
))
992 session
->setSaveStatisticsInterval(std::chrono::minutes(it
.value().toInt()));
993 // .torrent file size limit
994 if (hasKey(u
"torrent_file_size_limit"_s
))
995 pref
->setTorrentFileSizeLimit(it
.value().toLongLong());
996 // Confirm torrent recheck
997 if (hasKey(u
"confirm_torrent_recheck"_s
))
998 pref
->setConfirmTorrentRecheck(it
.value().toBool());
999 // Recheck completed torrents
1000 if (hasKey(u
"recheck_completed_torrents"_s
))
1001 pref
->recheckTorrentsOnCompletion(it
.value().toBool());
1002 // Customize application instance name
1003 if (hasKey(u
"app_instance_name"_s
))
1004 app()->setInstanceName(it
.value().toString());
1006 if (hasKey(u
"refresh_interval"_s
))
1007 session
->setRefreshInterval(it
.value().toInt());
1008 // Resolve peer countries
1009 if (hasKey(u
"resolve_peer_countries"_s
))
1010 pref
->resolvePeerCountries(it
.value().toBool());
1011 // Reannounce to all trackers when ip/port changed
1012 if (hasKey(u
"reannounce_when_address_changed"_s
))
1013 session
->setReannounceWhenAddressChangedEnabled(it
.value().toBool());
1015 if (hasKey(u
"embedded_tracker_port"_s
))
1016 pref
->setTrackerPort(it
.value().toInt());
1017 if (hasKey(u
"embedded_tracker_port_forwarding"_s
))
1018 pref
->setTrackerPortForwardingEnabled(it
.value().toBool());
1019 if (hasKey(u
"enable_embedded_tracker"_s
))
1020 session
->setTrackerEnabled(it
.value().toBool());
1022 if (hasKey(u
"mark_of_the_web"_s
))
1023 pref
->setMarkOfTheWebEnabled(it
.value().toBool());
1024 // Ignore SLL errors
1025 if (hasKey(u
"ignore_ssl_errors"_s
))
1026 pref
->setIgnoreSSLErrors(it
.value().toBool());
1027 // Python executable path
1028 if (hasKey(u
"python_executable_path"_s
))
1029 pref
->setPythonExecutablePath(Path(it
.value().toString()));
1031 // libtorrent preferences
1032 // Bdecode depth limit
1033 if (hasKey(u
"bdecode_depth_limit"_s
))
1034 pref
->setBdecodeDepthLimit(it
.value().toInt());
1035 // Bdecode token limit
1036 if (hasKey(u
"bdecode_token_limit"_s
))
1037 pref
->setBdecodeTokenLimit(it
.value().toInt());
1039 if (hasKey(u
"async_io_threads"_s
))
1040 session
->setAsyncIOThreads(it
.value().toInt());
1042 if (hasKey(u
"hashing_threads"_s
))
1043 session
->setHashingThreads(it
.value().toInt());
1045 if (hasKey(u
"file_pool_size"_s
))
1046 session
->setFilePoolSize(it
.value().toInt());
1047 // Checking Memory Usage
1048 if (hasKey(u
"checking_memory_use"_s
))
1049 session
->setCheckingMemUsage(it
.value().toInt());
1051 if (hasKey(u
"disk_cache"_s
))
1052 session
->setDiskCacheSize(it
.value().toInt());
1053 if (hasKey(u
"disk_cache_ttl"_s
))
1054 session
->setDiskCacheTTL(it
.value().toInt());
1056 if (hasKey(u
"disk_queue_size"_s
))
1057 session
->setDiskQueueSize(it
.value().toLongLong());
1059 if (hasKey(u
"disk_io_type"_s
))
1060 session
->setDiskIOType(static_cast<BitTorrent::DiskIOType
>(it
.value().toInt()));
1061 // Disk IO read mode
1062 if (hasKey(u
"disk_io_read_mode"_s
))
1063 session
->setDiskIOReadMode(static_cast<BitTorrent::DiskIOReadMode
>(it
.value().toInt()));
1064 // Disk IO write mode
1065 if (hasKey(u
"disk_io_write_mode"_s
))
1066 session
->setDiskIOWriteMode(static_cast<BitTorrent::DiskIOWriteMode
>(it
.value().toInt()));
1067 // Coalesce reads & writes
1068 if (hasKey(u
"enable_coalesce_read_write"_s
))
1069 session
->setCoalesceReadWriteEnabled(it
.value().toBool());
1070 // Piece extent affinity
1071 if (hasKey(u
"enable_piece_extent_affinity"_s
))
1072 session
->setPieceExtentAffinity(it
.value().toBool());
1074 if (hasKey(u
"enable_upload_suggestions"_s
))
1075 session
->setSuggestMode(it
.value().toBool());
1076 // Send buffer watermark
1077 if (hasKey(u
"send_buffer_watermark"_s
))
1078 session
->setSendBufferWatermark(it
.value().toInt());
1079 if (hasKey(u
"send_buffer_low_watermark"_s
))
1080 session
->setSendBufferLowWatermark(it
.value().toInt());
1081 if (hasKey(u
"send_buffer_watermark_factor"_s
))
1082 session
->setSendBufferWatermarkFactor(it
.value().toInt());
1083 // Outgoing connections per second
1084 if (hasKey(u
"connection_speed"_s
))
1085 session
->setConnectionSpeed(it
.value().toInt());
1086 // Socket send buffer size
1087 if (hasKey(u
"socket_send_buffer_size"_s
))
1088 session
->setSocketSendBufferSize(it
.value().toInt());
1089 // Socket receive buffer size
1090 if (hasKey(u
"socket_receive_buffer_size"_s
))
1091 session
->setSocketReceiveBufferSize(it
.value().toInt());
1092 // Socket listen backlog size
1093 if (hasKey(u
"socket_backlog_size"_s
))
1094 session
->setSocketBacklogSize(it
.value().toInt());
1096 if (hasKey(u
"outgoing_ports_min"_s
))
1097 session
->setOutgoingPortsMin(it
.value().toInt());
1098 if (hasKey(u
"outgoing_ports_max"_s
))
1099 session
->setOutgoingPortsMax(it
.value().toInt());
1100 // UPnP lease duration
1101 if (hasKey(u
"upnp_lease_duration"_s
))
1102 session
->setUPnPLeaseDuration(it
.value().toInt());
1104 if (hasKey(u
"peer_tos"_s
))
1105 session
->setPeerToS(it
.value().toInt());
1106 // uTP-TCP mixed mode
1107 if (hasKey(u
"utp_tcp_mixed_mode"_s
))
1108 session
->setUtpMixedMode(static_cast<BitTorrent::MixedModeAlgorithm
>(it
.value().toInt()));
1109 // Support internationalized domain name (IDN)
1110 if (hasKey(u
"idn_support_enabled"_s
))
1111 session
->setIDNSupportEnabled(it
.value().toBool());
1112 // Multiple connections per IP
1113 if (hasKey(u
"enable_multi_connections_from_same_ip"_s
))
1114 session
->setMultiConnectionsPerIpEnabled(it
.value().toBool());
1115 // Validate HTTPS tracker certificate
1116 if (hasKey(u
"validate_https_tracker_certificate"_s
))
1117 session
->setValidateHTTPSTrackerCertificate(it
.value().toBool());
1119 if (hasKey(u
"ssrf_mitigation"_s
))
1120 session
->setSSRFMitigationEnabled(it
.value().toBool());
1121 // Disallow connection to peers on privileged ports
1122 if (hasKey(u
"block_peers_on_privileged_ports"_s
))
1123 session
->setBlockPeersOnPrivilegedPorts(it
.value().toBool());
1124 // Choking algorithm
1125 if (hasKey(u
"upload_slots_behavior"_s
))
1126 session
->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm
>(it
.value().toInt()));
1127 // Seed choking algorithm
1128 if (hasKey(u
"upload_choking_algorithm"_s
))
1129 session
->setSeedChokingAlgorithm(static_cast<BitTorrent::SeedChokingAlgorithm
>(it
.value().toInt()));
1131 if (hasKey(u
"announce_to_all_trackers"_s
))
1132 session
->setAnnounceToAllTrackers(it
.value().toBool());
1133 if (hasKey(u
"announce_to_all_tiers"_s
))
1134 session
->setAnnounceToAllTiers(it
.value().toBool());
1135 if (hasKey(u
"announce_ip"_s
))
1137 const QHostAddress announceAddr
{it
.value().toString().trimmed()};
1138 session
->setAnnounceIP(announceAddr
.isNull() ? QString
{} : announceAddr
.toString());
1140 if (hasKey(u
"max_concurrent_http_announces"_s
))
1141 session
->setMaxConcurrentHTTPAnnounces(it
.value().toInt());
1142 if (hasKey(u
"stop_tracker_timeout"_s
))
1143 session
->setStopTrackerTimeout(it
.value().toInt());
1145 if (hasKey(u
"peer_turnover"_s
))
1146 session
->setPeerTurnover(it
.value().toInt());
1147 if (hasKey(u
"peer_turnover_cutoff"_s
))
1148 session
->setPeerTurnoverCutoff(it
.value().toInt());
1149 if (hasKey(u
"peer_turnover_interval"_s
))
1150 session
->setPeerTurnoverInterval(it
.value().toInt());
1151 // Maximum outstanding requests to a single peer
1152 if (hasKey(u
"request_queue_size"_s
))
1153 session
->setRequestQueueSize(it
.value().toInt());
1154 // DHT bootstrap nodes
1155 if (hasKey(u
"dht_bootstrap_nodes"_s
))
1156 session
->setDHTBootstrapNodes(it
.value().toString());
1162 void AppController::defaultSavePathAction()
1164 setResult(BitTorrent::Session::instance()->savePath().toString());
1167 void AppController::sendTestEmailAction()
1169 app()->sendTestEmail();
1173 void AppController::getDirectoryContentAction()
1175 requireParams({u
"dirPath"_s
});
1177 const QString dirPath
= params().value(u
"dirPath"_s
);
1178 if (dirPath
.isEmpty() || dirPath
.startsWith(u
':'))
1179 throw APIError(APIErrorType::BadParams
, tr("Invalid directory path"));
1181 const QDir dir
{dirPath
};
1182 if (!dir
.isAbsolute())
1183 throw APIError(APIErrorType::BadParams
, tr("Invalid directory path"));
1185 throw APIError(APIErrorType::NotFound
, tr("Directory does not exist"));
1187 const QString visibility
= params().value(u
"mode"_s
, u
"all"_s
);
1189 const auto parseDirectoryContentMode
= [](const QString
&visibility
) -> QDir::Filters
1191 if (visibility
== u
"dirs")
1193 if (visibility
== u
"files")
1195 if (visibility
== u
"all")
1196 return (QDir::Dirs
| QDir::Files
);
1197 throw APIError(APIErrorType::BadParams
, tr("Invalid mode, allowed values: %1").arg(u
"all, dirs, files"_s
));
1201 QDirIterator it
{dirPath
, (QDir::NoDotAndDotDot
| parseDirectoryContentMode(visibility
))};
1202 while (it
.hasNext())
1203 ret
.append(it
.next());
1207 void AppController::cookiesAction()
1209 const QList
<QNetworkCookie
> cookies
= Net::DownloadManager::instance()->allCookies();
1211 for (const QNetworkCookie
&cookie
: cookies
)
1213 ret
<< QJsonObject
{
1214 {KEY_COOKIE_NAME
, QString::fromLatin1(cookie
.name())},
1215 {KEY_COOKIE_DOMAIN
, cookie
.domain()},
1216 {KEY_COOKIE_PATH
, cookie
.path()},
1217 {KEY_COOKIE_VALUE
, QString::fromLatin1(cookie
.value())},
1218 {KEY_COOKIE_EXPIRATION_DATE
, Utils::DateTime::toSecsSinceEpoch(cookie
.expirationDate())},
1225 void AppController::setCookiesAction()
1227 requireParams({u
"cookies"_s
});
1228 const QString cookiesParam
{params()[u
"cookies"_s
].trimmed()};
1230 QJsonParseError jsonError
;
1231 const auto cookiesJsonDocument
= QJsonDocument::fromJson(cookiesParam
.toUtf8(), &jsonError
);
1232 if (jsonError
.error
!= QJsonParseError::NoError
)
1233 throw APIError(APIErrorType::BadParams
, jsonError
.errorString());
1234 if (!cookiesJsonDocument
.isArray())
1235 throw APIError(APIErrorType::BadParams
, tr("cookies must be array"));
1237 const QJsonArray cookiesJsonArr
= cookiesJsonDocument
.array();
1238 QList
<QNetworkCookie
> cookies
;
1239 cookies
.reserve(cookiesJsonArr
.size());
1240 for (const QJsonValue
&jsonVal
: cookiesJsonArr
)
1242 if (!jsonVal
.isObject())
1243 throw APIError(APIErrorType::BadParams
);
1245 QNetworkCookie cookie
;
1246 const QJsonObject jsonObj
= jsonVal
.toObject();
1247 if (jsonObj
.contains(KEY_COOKIE_NAME
))
1248 cookie
.setName(jsonObj
.value(KEY_COOKIE_NAME
).toString().toLatin1());
1249 if (jsonObj
.contains(KEY_COOKIE_DOMAIN
))
1250 cookie
.setDomain(jsonObj
.value(KEY_COOKIE_DOMAIN
).toString());
1251 if (jsonObj
.contains(KEY_COOKIE_PATH
))
1252 cookie
.setPath(jsonObj
.value(KEY_COOKIE_PATH
).toString());
1253 if (jsonObj
.contains(KEY_COOKIE_VALUE
))
1254 cookie
.setValue(jsonObj
.value(KEY_COOKIE_VALUE
).toString().toUtf8());
1255 if (jsonObj
.contains(KEY_COOKIE_EXPIRATION_DATE
))
1256 cookie
.setExpirationDate(QDateTime::fromSecsSinceEpoch(jsonObj
.value(KEY_COOKIE_EXPIRATION_DATE
).toInteger()));
1261 Net::DownloadManager::instance()->setAllCookies(cookies
);
1264 void AppController::networkInterfaceListAction()
1266 QJsonArray ifaceList
;
1267 for (const QNetworkInterface
&iface
: asConst(QNetworkInterface::allInterfaces()))
1269 if (!iface
.addressEntries().isEmpty())
1271 ifaceList
.append(QJsonObject
1273 {u
"name"_s
, iface
.humanReadableName()},
1274 {u
"value"_s
, iface
.name()}
1279 setResult(ifaceList
);
1282 void AppController::networkInterfaceAddressListAction()
1284 requireParams({u
"iface"_s
});
1286 const QString ifaceName
= params().value(u
"iface"_s
);
1287 QJsonArray addressList
;
1289 const auto appendAddress
= [&addressList
](const QHostAddress
&addr
)
1291 if (addr
.protocol() == QAbstractSocket::IPv6Protocol
)
1292 addressList
.append(Utils::Net::canonicalIPv6Addr(addr
).toString());
1294 addressList
.append(addr
.toString());
1297 if (ifaceName
.isEmpty())
1299 for (const QHostAddress
&addr
: asConst(QNetworkInterface::allAddresses()))
1300 appendAddress(addr
);
1304 const QNetworkInterface iface
= QNetworkInterface::interfaceFromName(ifaceName
);
1305 for (const QNetworkAddressEntry
&entry
: asConst(iface
.addressEntries()))
1306 appendAddress(entry
.ip());
1309 setResult(addressList
);