Drop WebUI default credentials
[qBittorrent.git] / src / webui / api / appcontroller.cpp
bloba99c8d1813e7023cd515f291c9a607cd2fdb67f1
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2006-2012 Christophe Dumez <chris@qbittorrent.org>
5 * Copyright (C) 2006-2012 Ishan Arora <ishan@qbittorrent.org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * In addition, as a special exception, the copyright holders give permission to
22 * link this program with the OpenSSL project's "OpenSSL" library (or with
23 * modified versions of it that use the same license as the "OpenSSL" library),
24 * and distribute the linked executables. You must obey the GNU General Public
25 * License in all respects for all of the code used other than "OpenSSL". If you
26 * modify file(s), you may extend this exception to your version of the file(s),
27 * but you are not obligated to do so. If you do not wish to do so, delete this
28 * exception statement from your version.
31 #include "appcontroller.h"
33 #include <algorithm>
34 #include <chrono>
36 #include <QCoreApplication>
37 #include <QDebug>
38 #include <QJsonArray>
39 #include <QJsonDocument>
40 #include <QJsonObject>
41 #include <QNetworkInterface>
42 #include <QRegularExpression>
43 #include <QStringList>
44 #include <QTimer>
45 #include <QTranslator>
47 #include "base/bittorrent/session.h"
48 #include "base/global.h"
49 #include "base/interfaces/iapplication.h"
50 #include "base/net/portforwarder.h"
51 #include "base/net/proxyconfigurationmanager.h"
52 #include "base/path.h"
53 #include "base/preferences.h"
54 #include "base/rss/rss_autodownloader.h"
55 #include "base/rss/rss_session.h"
56 #include "base/torrentfileguard.h"
57 #include "base/torrentfileswatcher.h"
58 #include "base/utils/fs.h"
59 #include "base/utils/misc.h"
60 #include "base/utils/net.h"
61 #include "base/utils/password.h"
62 #include "base/utils/string.h"
63 #include "base/version.h"
64 #include "../webapplication.h"
66 using namespace std::chrono_literals;
68 void AppController::webapiVersionAction()
70 setResult(API_VERSION.toString());
73 void AppController::versionAction()
75 setResult(QStringLiteral(QBT_VERSION));
78 void AppController::buildInfoAction()
80 const QJsonObject versions =
82 {u"qt"_s, QStringLiteral(QT_VERSION_STR)},
83 {u"libtorrent"_s, Utils::Misc::libtorrentVersionString()},
84 {u"boost"_s, Utils::Misc::boostVersionString()},
85 {u"openssl"_s, Utils::Misc::opensslVersionString()},
86 {u"zlib"_s, Utils::Misc::zlibVersionString()},
87 {u"bitness"_s, (QT_POINTER_SIZE * 8)}
89 setResult(versions);
92 void AppController::shutdownAction()
94 // Special handling for shutdown, we
95 // need to reply to the WebUI before
96 // actually shutting down.
97 QTimer::singleShot(100ms, Qt::CoarseTimer, qApp, []
99 QCoreApplication::exit();
103 void AppController::preferencesAction()
105 const auto *pref = Preferences::instance();
106 const auto *session = BitTorrent::Session::instance();
108 QJsonObject data;
110 // Behavior
111 // Language
112 data[u"locale"_s] = pref->getLocale();
113 data[u"performance_warning"_s] = session->isPerformanceWarningEnabled();
114 // Log file
115 data[u"file_log_enabled"_s] = app()->isFileLoggerEnabled();
116 data[u"file_log_path"_s] = app()->fileLoggerPath().toString();
117 data[u"file_log_backup_enabled"_s] = app()->isFileLoggerBackup();
118 data[u"file_log_max_size"_s] = app()->fileLoggerMaxSize() / 1024;
119 data[u"file_log_delete_old"_s] = app()->isFileLoggerDeleteOld();
120 data[u"file_log_age"_s] = app()->fileLoggerAge();
121 data[u"file_log_age_type"_s] = app()->fileLoggerAgeType();
123 // Downloads
124 // When adding a torrent
125 data[u"torrent_content_layout"_s] = Utils::String::fromEnum(session->torrentContentLayout());
126 data[u"add_to_top_of_queue"_s] = session->isAddTorrentToQueueTop();
127 data[u"start_paused_enabled"_s] = session->isAddTorrentPaused();
128 data[u"torrent_stop_condition"_s] = Utils::String::fromEnum(session->torrentStopCondition());
129 data[u"merge_trackers"_s] = session->isMergeTrackersEnabled();
130 data[u"auto_delete_mode"_s] = static_cast<int>(TorrentFileGuard::autoDeleteMode());
131 data[u"preallocate_all"_s] = session->isPreallocationEnabled();
132 data[u"incomplete_files_ext"_s] = session->isAppendExtensionEnabled();
133 // Saving Management
134 data[u"auto_tmm_enabled"_s] = !session->isAutoTMMDisabledByDefault();
135 data[u"torrent_changed_tmm_enabled"_s] = !session->isDisableAutoTMMWhenCategoryChanged();
136 data[u"save_path_changed_tmm_enabled"_s] = !session->isDisableAutoTMMWhenDefaultSavePathChanged();
137 data[u"category_changed_tmm_enabled"_s] = !session->isDisableAutoTMMWhenCategorySavePathChanged();
138 data[u"use_subcategories"] = session->isSubcategoriesEnabled();
139 data[u"save_path"_s] = session->savePath().toString();
140 data[u"temp_path_enabled"_s] = session->isDownloadPathEnabled();
141 data[u"temp_path"_s] = session->downloadPath().toString();
142 data[u"use_category_paths_in_manual_mode"_s] = session->useCategoryPathsInManualMode();
143 data[u"export_dir"_s] = session->torrentExportDirectory().toString();
144 data[u"export_dir_fin"_s] = session->finishedTorrentExportDirectory().toString();
146 // TODO: The following code is deprecated. Delete it once replaced by updated API method.
147 // === BEGIN DEPRECATED CODE === //
148 TorrentFilesWatcher *fsWatcher = TorrentFilesWatcher::instance();
149 const QHash<Path, TorrentFilesWatcher::WatchedFolderOptions> watchedFolders = fsWatcher->folders();
150 QJsonObject nativeDirs;
151 for (auto i = watchedFolders.cbegin(); i != watchedFolders.cend(); ++i)
153 const Path &watchedFolder = i.key();
154 const BitTorrent::AddTorrentParams params = i.value().addTorrentParams;
155 if (params.savePath.isEmpty())
156 nativeDirs.insert(watchedFolder.toString(), 1);
157 else if (params.savePath == watchedFolder)
158 nativeDirs.insert(watchedFolder.toString(), 0);
159 else
160 nativeDirs.insert(watchedFolder.toString(), params.savePath.toString());
162 data[u"scan_dirs"_s] = nativeDirs;
163 // === END DEPRECATED CODE === //
165 // Excluded file names
166 data[u"excluded_file_names_enabled"_s] = session->isExcludedFileNamesEnabled();
167 data[u"excluded_file_names"_s] = session->excludedFileNames().join(u'\n');
169 // Email notification upon download completion
170 data[u"mail_notification_enabled"_s] = pref->isMailNotificationEnabled();
171 data[u"mail_notification_sender"_s] = pref->getMailNotificationSender();
172 data[u"mail_notification_email"_s] = pref->getMailNotificationEmail();
173 data[u"mail_notification_smtp"_s] = pref->getMailNotificationSMTP();
174 data[u"mail_notification_ssl_enabled"_s] = pref->getMailNotificationSMTPSSL();
175 data[u"mail_notification_auth_enabled"_s] = pref->getMailNotificationSMTPAuth();
176 data[u"mail_notification_username"_s] = pref->getMailNotificationSMTPUsername();
177 data[u"mail_notification_password"_s] = pref->getMailNotificationSMTPPassword();
178 // Run an external program on torrent added
179 data[u"autorun_on_torrent_added_enabled"_s] = pref->isAutoRunOnTorrentAddedEnabled();
180 data[u"autorun_on_torrent_added_program"_s] = pref->getAutoRunOnTorrentAddedProgram();
181 // Run an external program on torrent finished
182 data[u"autorun_enabled"_s] = pref->isAutoRunOnTorrentFinishedEnabled();
183 data[u"autorun_program"_s] = pref->getAutoRunOnTorrentFinishedProgram();
185 // Connection
186 // Listening Port
187 data[u"listen_port"_s] = session->port();
188 data[u"random_port"_s] = (session->port() == 0); // deprecated
189 data[u"upnp"_s] = Net::PortForwarder::instance()->isEnabled();
190 // Connections Limits
191 data[u"max_connec"_s] = session->maxConnections();
192 data[u"max_connec_per_torrent"_s] = session->maxConnectionsPerTorrent();
193 data[u"max_uploads"_s] = session->maxUploads();
194 data[u"max_uploads_per_torrent"_s] = session->maxUploadsPerTorrent();
196 // I2P
197 data[u"i2p_enabled"_s] = session->isI2PEnabled();
198 data[u"i2p_address"_s] = session->I2PAddress();
199 data[u"i2p_port"_s] = session->I2PPort();
200 data[u"i2p_mixed_mode"_s] = session->I2PMixedMode();
201 data[u"i2p_inbound_quantity"_s] = session->I2PInboundQuantity();
202 data[u"i2p_outbound_quantity"_s] = session->I2POutboundQuantity();
203 data[u"i2p_inbound_length"_s] = session->I2PInboundLength();
204 data[u"i2p_outbound_length"_s] = session->I2POutboundLength();
206 // Proxy Server
207 const auto *proxyManager = Net::ProxyConfigurationManager::instance();
208 Net::ProxyConfiguration proxyConf = proxyManager->proxyConfiguration();
209 data[u"proxy_type"_s] = Utils::String::fromEnum(proxyConf.type);
210 data[u"proxy_ip"_s] = proxyConf.ip;
211 data[u"proxy_port"_s] = proxyConf.port;
212 data[u"proxy_auth_enabled"_s] = proxyConf.authEnabled;
213 data[u"proxy_username"_s] = proxyConf.username;
214 data[u"proxy_password"_s] = proxyConf.password;
215 data[u"proxy_hostname_lookup"_s] = proxyConf.hostnameLookupEnabled;
217 data[u"proxy_bittorrent"_s] = pref->useProxyForBT();
218 data[u"proxy_peer_connections"_s] = session->isProxyPeerConnectionsEnabled();
219 data[u"proxy_rss"_s] = pref->useProxyForRSS();
220 data[u"proxy_misc"_s] = pref->useProxyForGeneralPurposes();
222 // IP Filtering
223 data[u"ip_filter_enabled"_s] = session->isIPFilteringEnabled();
224 data[u"ip_filter_path"_s] = session->IPFilterFile().toString();
225 data[u"ip_filter_trackers"_s] = session->isTrackerFilteringEnabled();
226 data[u"banned_IPs"_s] = session->bannedIPs().join(u'\n');
228 // Speed
229 // Global Rate Limits
230 data[u"dl_limit"_s] = session->globalDownloadSpeedLimit();
231 data[u"up_limit"_s] = session->globalUploadSpeedLimit();
232 data[u"alt_dl_limit"_s] = session->altGlobalDownloadSpeedLimit();
233 data[u"alt_up_limit"_s] = session->altGlobalUploadSpeedLimit();
234 data[u"bittorrent_protocol"_s] = static_cast<int>(session->btProtocol());
235 data[u"limit_utp_rate"_s] = session->isUTPRateLimited();
236 data[u"limit_tcp_overhead"_s] = session->includeOverheadInLimits();
237 data[u"limit_lan_peers"_s] = !session->ignoreLimitsOnLAN();
238 // Scheduling
239 data[u"scheduler_enabled"_s] = session->isBandwidthSchedulerEnabled();
240 const QTime start_time = pref->getSchedulerStartTime();
241 data[u"schedule_from_hour"_s] = start_time.hour();
242 data[u"schedule_from_min"_s] = start_time.minute();
243 const QTime end_time = pref->getSchedulerEndTime();
244 data[u"schedule_to_hour"_s] = end_time.hour();
245 data[u"schedule_to_min"_s] = end_time.minute();
246 data[u"scheduler_days"_s] = static_cast<int>(pref->getSchedulerDays());
248 // Bittorrent
249 // Privacy
250 data[u"dht"_s] = session->isDHTEnabled();
251 data[u"pex"_s] = session->isPeXEnabled();
252 data[u"lsd"_s] = session->isLSDEnabled();
253 data[u"encryption"_s] = session->encryption();
254 data[u"anonymous_mode"_s] = session->isAnonymousModeEnabled();
255 // Max active checking torrents
256 data[u"max_active_checking_torrents"_s] = session->maxActiveCheckingTorrents();
257 // Torrent Queueing
258 data[u"queueing_enabled"_s] = session->isQueueingSystemEnabled();
259 data[u"max_active_downloads"_s] = session->maxActiveDownloads();
260 data[u"max_active_torrents"_s] = session->maxActiveTorrents();
261 data[u"max_active_uploads"_s] = session->maxActiveUploads();
262 data[u"dont_count_slow_torrents"_s] = session->ignoreSlowTorrentsForQueueing();
263 data[u"slow_torrent_dl_rate_threshold"_s] = session->downloadRateForSlowTorrents();
264 data[u"slow_torrent_ul_rate_threshold"_s] = session->uploadRateForSlowTorrents();
265 data[u"slow_torrent_inactive_timer"_s] = session->slowTorrentsInactivityTimer();
266 // Share Ratio Limiting
267 data[u"max_ratio_enabled"_s] = (session->globalMaxRatio() >= 0.);
268 data[u"max_ratio"_s] = session->globalMaxRatio();
269 data[u"max_seeding_time_enabled"_s] = (session->globalMaxSeedingMinutes() >= 0.);
270 data[u"max_seeding_time"_s] = session->globalMaxSeedingMinutes();
271 data[u"max_inactive_seeding_time_enabled"_s] = (session->globalMaxInactiveSeedingMinutes() >= 0.);
272 data[u"max_inactive_seeding_time"_s] = session->globalMaxInactiveSeedingMinutes();
273 data[u"max_ratio_act"_s] = session->maxRatioAction();
274 // Add trackers
275 data[u"add_trackers_enabled"_s] = session->isAddTrackersEnabled();
276 data[u"add_trackers"_s] = session->additionalTrackers();
278 // WebUI
279 // HTTP Server
280 data[u"web_ui_domain_list"_s] = pref->getServerDomains();
281 data[u"web_ui_address"_s] = pref->getWebUIAddress();
282 data[u"web_ui_port"_s] = pref->getWebUIPort();
283 data[u"web_ui_upnp"_s] = pref->useUPnPForWebUIPort();
284 data[u"use_https"_s] = pref->isWebUIHttpsEnabled();
285 data[u"web_ui_https_cert_path"_s] = pref->getWebUIHttpsCertificatePath().toString();
286 data[u"web_ui_https_key_path"_s] = pref->getWebUIHttpsKeyPath().toString();
287 // Authentication
288 data[u"web_ui_username"_s] = pref->getWebUIUsername();
289 data[u"bypass_local_auth"_s] = !pref->isWebUILocalAuthEnabled();
290 data[u"bypass_auth_subnet_whitelist_enabled"_s] = pref->isWebUIAuthSubnetWhitelistEnabled();
291 QStringList authSubnetWhitelistStringList;
292 for (const Utils::Net::Subnet &subnet : asConst(pref->getWebUIAuthSubnetWhitelist()))
293 authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet);
294 data[u"bypass_auth_subnet_whitelist"_s] = authSubnetWhitelistStringList.join(u'\n');
295 data[u"web_ui_max_auth_fail_count"_s] = pref->getWebUIMaxAuthFailCount();
296 data[u"web_ui_ban_duration"_s] = static_cast<int>(pref->getWebUIBanDuration().count());
297 data[u"web_ui_session_timeout"_s] = pref->getWebUISessionTimeout();
298 // Use alternative WebUI
299 data[u"alternative_webui_enabled"_s] = pref->isAltWebUIEnabled();
300 data[u"alternative_webui_path"_s] = pref->getWebUIRootFolder().toString();
301 // Security
302 data[u"web_ui_clickjacking_protection_enabled"_s] = pref->isWebUIClickjackingProtectionEnabled();
303 data[u"web_ui_csrf_protection_enabled"_s] = pref->isWebUICSRFProtectionEnabled();
304 data[u"web_ui_secure_cookie_enabled"_s] = pref->isWebUISecureCookieEnabled();
305 data[u"web_ui_host_header_validation_enabled"_s] = pref->isWebUIHostHeaderValidationEnabled();
306 // Custom HTTP headers
307 data[u"web_ui_use_custom_http_headers_enabled"_s] = pref->isWebUICustomHTTPHeadersEnabled();
308 data[u"web_ui_custom_http_headers"_s] = pref->getWebUICustomHTTPHeaders();
309 // Reverse proxy
310 data[u"web_ui_reverse_proxy_enabled"_s] = pref->isWebUIReverseProxySupportEnabled();
311 data[u"web_ui_reverse_proxies_list"_s] = pref->getWebUITrustedReverseProxiesList();
312 // Update my dynamic domain name
313 data[u"dyndns_enabled"_s] = pref->isDynDNSEnabled();
314 data[u"dyndns_service"_s] = static_cast<int>(pref->getDynDNSService());
315 data[u"dyndns_username"_s] = pref->getDynDNSUsername();
316 data[u"dyndns_password"_s] = pref->getDynDNSPassword();
317 data[u"dyndns_domain"_s] = pref->getDynDomainName();
319 // RSS settings
320 data[u"rss_refresh_interval"_s] = RSS::Session::instance()->refreshInterval();
321 data[u"rss_max_articles_per_feed"_s] = RSS::Session::instance()->maxArticlesPerFeed();
322 data[u"rss_processing_enabled"_s] = RSS::Session::instance()->isProcessingEnabled();
323 data[u"rss_auto_downloading_enabled"_s] = RSS::AutoDownloader::instance()->isProcessingEnabled();
324 data[u"rss_download_repack_proper_episodes"_s] = RSS::AutoDownloader::instance()->downloadRepacks();
325 data[u"rss_smart_episode_filters"_s] = RSS::AutoDownloader::instance()->smartEpisodeFilters().join(u'\n');
327 // Advanced settings
328 // qBitorrent preferences
329 // Resume data storage type
330 data[u"resume_data_storage_type"_s] = Utils::String::fromEnum(session->resumeDataStorageType());
331 // Physical memory (RAM) usage limit
332 data[u"memory_working_set_limit"_s] = app()->memoryWorkingSetLimit();
333 // Current network interface
334 data[u"current_network_interface"_s] = session->networkInterface();
335 // Current network interface name
336 data[u"current_interface_name"_s] = session->networkInterfaceName();
337 // Current network interface address
338 data[u"current_interface_address"_s] = session->networkInterfaceAddress();
339 // Save resume data interval
340 data[u"save_resume_data_interval"_s] = session->saveResumeDataInterval();
341 // .torrent file size limit
342 data[u"torrent_file_size_limit"_s] = pref->getTorrentFileSizeLimit();
343 // Recheck completed torrents
344 data[u"recheck_completed_torrents"_s] = pref->recheckTorrentsOnCompletion();
345 // Refresh interval
346 data[u"refresh_interval"_s] = session->refreshInterval();
347 // Resolve peer countries
348 data[u"resolve_peer_countries"_s] = pref->resolvePeerCountries();
349 // Reannounce to all trackers when ip/port changed
350 data[u"reannounce_when_address_changed"_s] = session->isReannounceWhenAddressChangedEnabled();
352 // libtorrent preferences
353 // Bdecode depth limit
354 data[u"bdecode_depth_limit"_s] = pref->getBdecodeDepthLimit();
355 // Bdecode token limit
356 data[u"bdecode_token_limit"_s] = pref->getBdecodeTokenLimit();
357 // Async IO threads
358 data[u"async_io_threads"_s] = session->asyncIOThreads();
359 // Hashing threads
360 data[u"hashing_threads"_s] = session->hashingThreads();
361 // File pool size
362 data[u"file_pool_size"_s] = session->filePoolSize();
363 // Checking memory usage
364 data[u"checking_memory_use"_s] = session->checkingMemUsage();
365 // Disk write cache
366 data[u"disk_cache"_s] = session->diskCacheSize();
367 data[u"disk_cache_ttl"_s] = session->diskCacheTTL();
368 // Disk queue size
369 data[u"disk_queue_size"_s] = session->diskQueueSize();
370 // Disk IO Type
371 data[u"disk_io_type"_s] = static_cast<int>(session->diskIOType());
372 // Disk IO read mode
373 data[u"disk_io_read_mode"_s] = static_cast<int>(session->diskIOReadMode());
374 // Disk IO write mode
375 data[u"disk_io_write_mode"_s] = static_cast<int>(session->diskIOWriteMode());
376 // Coalesce reads & writes
377 data[u"enable_coalesce_read_write"_s] = session->isCoalesceReadWriteEnabled();
378 // Piece Extent Affinity
379 data[u"enable_piece_extent_affinity"_s] = session->usePieceExtentAffinity();
380 // Suggest mode
381 data[u"enable_upload_suggestions"_s] = session->isSuggestModeEnabled();
382 // Send buffer watermark
383 data[u"send_buffer_watermark"_s] = session->sendBufferWatermark();
384 data[u"send_buffer_low_watermark"_s] = session->sendBufferLowWatermark();
385 data[u"send_buffer_watermark_factor"_s] = session->sendBufferWatermarkFactor();
386 // Outgoing connections per second
387 data[u"connection_speed"_s] = session->connectionSpeed();
388 // Socket send buffer size
389 data[u"socket_send_buffer_size"_s] = session->socketSendBufferSize();
390 // Socket receive buffer size
391 data[u"socket_receive_buffer_size"_s] = session->socketReceiveBufferSize();
392 // Socket listen backlog size
393 data[u"socket_backlog_size"_s] = session->socketBacklogSize();
394 // Outgoing ports
395 data[u"outgoing_ports_min"_s] = session->outgoingPortsMin();
396 data[u"outgoing_ports_max"_s] = session->outgoingPortsMax();
397 // UPnP lease duration
398 data[u"upnp_lease_duration"_s] = session->UPnPLeaseDuration();
399 // Type of service
400 data[u"peer_tos"_s] = session->peerToS();
401 // uTP-TCP mixed mode
402 data[u"utp_tcp_mixed_mode"_s] = static_cast<int>(session->utpMixedMode());
403 // Support internationalized domain name (IDN)
404 data[u"idn_support_enabled"_s] = session->isIDNSupportEnabled();
405 // Multiple connections per IP
406 data[u"enable_multi_connections_from_same_ip"_s] = session->multiConnectionsPerIpEnabled();
407 // Validate HTTPS tracker certificate
408 data[u"validate_https_tracker_certificate"_s] = session->validateHTTPSTrackerCertificate();
409 // SSRF mitigation
410 data[u"ssrf_mitigation"_s] = session->isSSRFMitigationEnabled();
411 // Disallow connection to peers on privileged ports
412 data[u"block_peers_on_privileged_ports"_s] = session->blockPeersOnPrivilegedPorts();
413 // Embedded tracker
414 data[u"enable_embedded_tracker"_s] = session->isTrackerEnabled();
415 data[u"embedded_tracker_port"_s] = pref->getTrackerPort();
416 data[u"embedded_tracker_port_forwarding"_s] = pref->isTrackerPortForwardingEnabled();
417 // Mark-of-the-Web
418 data[u"mark_of_the_web"_s] = pref->isMarkOfTheWebEnabled();
419 // Python executable path
420 data[u"python_executable_path"_s] = pref->getPythonExecutablePath().toString();
421 // Choking algorithm
422 data[u"upload_slots_behavior"_s] = static_cast<int>(session->chokingAlgorithm());
423 // Seed choking algorithm
424 data[u"upload_choking_algorithm"_s] = static_cast<int>(session->seedChokingAlgorithm());
425 // Announce
426 data[u"announce_to_all_trackers"_s] = session->announceToAllTrackers();
427 data[u"announce_to_all_tiers"_s] = session->announceToAllTiers();
428 data[u"announce_ip"_s] = session->announceIP();
429 data[u"max_concurrent_http_announces"_s] = session->maxConcurrentHTTPAnnounces();
430 data[u"stop_tracker_timeout"_s] = session->stopTrackerTimeout();
431 // Peer Turnover
432 data[u"peer_turnover"_s] = session->peerTurnover();
433 data[u"peer_turnover_cutoff"_s] = session->peerTurnoverCutoff();
434 data[u"peer_turnover_interval"_s] = session->peerTurnoverInterval();
435 // Maximum outstanding requests to a single peer
436 data[u"request_queue_size"_s] = session->requestQueueSize();
437 // DHT bootstrap nodes
438 data[u"dht_bootstrap_nodes"_s] = session->getDHTBootstrapNodes();
440 setResult(data);
443 void AppController::setPreferencesAction()
445 requireParams({u"json"_s});
447 auto *pref = Preferences::instance();
448 auto *session = BitTorrent::Session::instance();
449 const QVariantHash m = QJsonDocument::fromJson(params()[u"json"_s].toUtf8()).toVariant().toHash();
451 QVariantHash::ConstIterator it;
452 const auto hasKey = [&it, &m](const QString &key) -> bool
454 it = m.find(key);
455 return (it != m.constEnd());
458 // Behavior
459 // Language
460 if (hasKey(u"locale"_s))
462 QString locale = it.value().toString();
463 if (pref->getLocale() != locale)
465 auto *translator = new QTranslator;
466 if (translator->load(u":/lang/qbittorrent_"_s + locale))
468 qDebug("%s locale recognized, using translation.", qUtf8Printable(locale));
470 else
472 qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale));
474 qApp->installTranslator(translator);
476 pref->setLocale(locale);
479 if (hasKey(u"performance_warning"_s))
480 session->setPerformanceWarningEnabled(it.value().toBool());
481 // Log file
482 if (hasKey(u"file_log_enabled"_s))
483 app()->setFileLoggerEnabled(it.value().toBool());
484 if (hasKey(u"file_log_path"_s))
485 app()->setFileLoggerPath(Path(it.value().toString()));
486 if (hasKey(u"file_log_backup_enabled"_s))
487 app()->setFileLoggerBackup(it.value().toBool());
488 if (hasKey(u"file_log_max_size"_s))
489 app()->setFileLoggerMaxSize(it.value().toInt() * 1024);
490 if (hasKey(u"file_log_delete_old"_s))
491 app()->setFileLoggerDeleteOld(it.value().toBool());
492 if (hasKey(u"file_log_age"_s))
493 app()->setFileLoggerAge(it.value().toInt());
494 if (hasKey(u"file_log_age_type"_s))
495 app()->setFileLoggerAgeType(it.value().toInt());
497 // Downloads
498 // When adding a torrent
499 if (hasKey(u"torrent_content_layout"_s))
500 session->setTorrentContentLayout(Utils::String::toEnum(it.value().toString(), BitTorrent::TorrentContentLayout::Original));
501 if (hasKey(u"add_to_top_of_queue"_s))
502 session->setAddTorrentToQueueTop(it.value().toBool());
503 if (hasKey(u"start_paused_enabled"_s))
504 session->setAddTorrentPaused(it.value().toBool());
505 if (hasKey(u"torrent_stop_condition"_s))
506 session->setTorrentStopCondition(Utils::String::toEnum(it.value().toString(), BitTorrent::Torrent::StopCondition::None));
507 if (hasKey(u"merge_trackers"_s))
508 session->setMergeTrackersEnabled(it.value().toBool());
509 if (hasKey(u"auto_delete_mode"_s))
510 TorrentFileGuard::setAutoDeleteMode(static_cast<TorrentFileGuard::AutoDeleteMode>(it.value().toInt()));
512 if (hasKey(u"preallocate_all"_s))
513 session->setPreallocationEnabled(it.value().toBool());
514 if (hasKey(u"incomplete_files_ext"_s))
515 session->setAppendExtensionEnabled(it.value().toBool());
517 // Saving Management
518 if (hasKey(u"auto_tmm_enabled"_s))
519 session->setAutoTMMDisabledByDefault(!it.value().toBool());
520 if (hasKey(u"torrent_changed_tmm_enabled"_s))
521 session->setDisableAutoTMMWhenCategoryChanged(!it.value().toBool());
522 if (hasKey(u"save_path_changed_tmm_enabled"_s))
523 session->setDisableAutoTMMWhenDefaultSavePathChanged(!it.value().toBool());
524 if (hasKey(u"category_changed_tmm_enabled"_s))
525 session->setDisableAutoTMMWhenCategorySavePathChanged(!it.value().toBool());
526 if (hasKey(u"use_subcategories"_s))
527 session->setSubcategoriesEnabled(it.value().toBool());
528 if (hasKey(u"save_path"_s))
529 session->setSavePath(Path(it.value().toString()));
530 if (hasKey(u"temp_path_enabled"_s))
531 session->setDownloadPathEnabled(it.value().toBool());
532 if (hasKey(u"temp_path"_s))
533 session->setDownloadPath(Path(it.value().toString()));
534 if (hasKey(u"use_category_paths_in_manual_mode"_s))
535 session->setUseCategoryPathsInManualMode(it.value().toBool());
536 if (hasKey(u"export_dir"_s))
537 session->setTorrentExportDirectory(Path(it.value().toString()));
538 if (hasKey(u"export_dir_fin"_s))
539 session->setFinishedTorrentExportDirectory(Path(it.value().toString()));
541 // TODO: The following code is deprecated. Delete it once replaced by updated API method.
542 // === BEGIN DEPRECATED CODE === //
543 if (hasKey(u"scan_dirs"_s))
545 PathList scanDirs;
546 TorrentFilesWatcher *fsWatcher = TorrentFilesWatcher::instance();
547 const PathList oldScanDirs = fsWatcher->folders().keys();
548 const QVariantHash nativeDirs = it.value().toHash();
549 for (auto i = nativeDirs.cbegin(); i != nativeDirs.cend(); ++i)
553 const Path watchedFolder {i.key()};
554 TorrentFilesWatcher::WatchedFolderOptions options = fsWatcher->folders().value(watchedFolder);
555 BitTorrent::AddTorrentParams &params = options.addTorrentParams;
557 bool isInt = false;
558 const int intVal = i.value().toInt(&isInt);
559 if (isInt)
561 if (intVal == 0)
563 params.savePath = watchedFolder;
564 params.useAutoTMM = false;
567 else
569 const Path customSavePath {i.value().toString()};
570 params.savePath = customSavePath;
571 params.useAutoTMM = false;
574 fsWatcher->setWatchedFolder(watchedFolder, options);
575 scanDirs.append(watchedFolder);
577 catch (...)
582 // Update deleted folders
583 for (const Path &path : oldScanDirs)
585 if (!scanDirs.contains(path))
586 fsWatcher->removeWatchedFolder(path);
589 // === END DEPRECATED CODE === //
591 // Excluded file names
592 if (hasKey(u"excluded_file_names_enabled"_s))
593 session->setExcludedFileNamesEnabled(it.value().toBool());
594 if (hasKey(u"excluded_file_names"_s))
595 session->setExcludedFileNames(it.value().toString().split(u'\n'));
597 // Email notification upon download completion
598 if (hasKey(u"mail_notification_enabled"_s))
599 pref->setMailNotificationEnabled(it.value().toBool());
600 if (hasKey(u"mail_notification_sender"_s))
601 pref->setMailNotificationSender(it.value().toString());
602 if (hasKey(u"mail_notification_email"_s))
603 pref->setMailNotificationEmail(it.value().toString());
604 if (hasKey(u"mail_notification_smtp"_s))
605 pref->setMailNotificationSMTP(it.value().toString());
606 if (hasKey(u"mail_notification_ssl_enabled"_s))
607 pref->setMailNotificationSMTPSSL(it.value().toBool());
608 if (hasKey(u"mail_notification_auth_enabled"_s))
609 pref->setMailNotificationSMTPAuth(it.value().toBool());
610 if (hasKey(u"mail_notification_username"_s))
611 pref->setMailNotificationSMTPUsername(it.value().toString());
612 if (hasKey(u"mail_notification_password"_s))
613 pref->setMailNotificationSMTPPassword(it.value().toString());
614 // Run an external program on torrent added
615 if (hasKey(u"autorun_on_torrent_added_enabled"_s))
616 pref->setAutoRunOnTorrentAddedEnabled(it.value().toBool());
617 if (hasKey(u"autorun_on_torrent_added_program"_s))
618 pref->setAutoRunOnTorrentAddedProgram(it.value().toString());
619 // Run an external program on torrent finished
620 if (hasKey(u"autorun_enabled"_s))
621 pref->setAutoRunOnTorrentFinishedEnabled(it.value().toBool());
622 if (hasKey(u"autorun_program"_s))
623 pref->setAutoRunOnTorrentFinishedProgram(it.value().toString());
625 // Connection
626 // Listening Port
627 if (hasKey(u"random_port"_s) && it.value().toBool()) // deprecated
629 session->setPort(0);
631 else if (hasKey(u"listen_port"_s))
633 session->setPort(it.value().toInt());
635 if (hasKey(u"upnp"_s))
636 Net::PortForwarder::instance()->setEnabled(it.value().toBool());
637 // Connections Limits
638 if (hasKey(u"max_connec"_s))
639 session->setMaxConnections(it.value().toInt());
640 if (hasKey(u"max_connec_per_torrent"_s))
641 session->setMaxConnectionsPerTorrent(it.value().toInt());
642 if (hasKey(u"max_uploads"_s))
643 session->setMaxUploads(it.value().toInt());
644 if (hasKey(u"max_uploads_per_torrent"_s))
645 session->setMaxUploadsPerTorrent(it.value().toInt());
647 // I2P
648 if (hasKey(u"i2p_enabled"_s))
649 session->setI2PEnabled(it.value().toBool());
650 if (hasKey(u"i2p_address"_s))
651 session->setI2PAddress(it.value().toString());
652 if (hasKey(u"i2p_port"_s))
653 session->setI2PPort(it.value().toInt());
654 if (hasKey(u"i2p_mixed_mode"_s))
655 session->setI2PMixedMode(it.value().toBool());
656 if (hasKey(u"i2p_inbound_quantity"_s))
657 session->setI2PInboundQuantity(it.value().toInt());
658 if (hasKey(u"i2p_outbound_quantity"_s))
659 session->setI2POutboundQuantity(it.value().toInt());
660 if (hasKey(u"i2p_inbound_length"_s))
661 session->setI2PInboundLength(it.value().toInt());
662 if (hasKey(u"i2p_outbound_length"_s))
663 session->setI2POutboundLength(it.value().toInt());
665 // Proxy Server
666 auto *proxyManager = Net::ProxyConfigurationManager::instance();
667 Net::ProxyConfiguration proxyConf = proxyManager->proxyConfiguration();
668 if (hasKey(u"proxy_type"_s))
669 proxyConf.type = Utils::String::toEnum(it.value().toString(), Net::ProxyType::None);
670 if (hasKey(u"proxy_ip"_s))
671 proxyConf.ip = it.value().toString();
672 if (hasKey(u"proxy_port"_s))
673 proxyConf.port = it.value().toUInt();
674 if (hasKey(u"proxy_auth_enabled"_s))
675 proxyConf.authEnabled = it.value().toBool();
676 if (hasKey(u"proxy_username"_s))
677 proxyConf.username = it.value().toString();
678 if (hasKey(u"proxy_password"_s))
679 proxyConf.password = it.value().toString();
680 if (hasKey(u"proxy_hostname_lookup"_s))
681 proxyConf.hostnameLookupEnabled = it.value().toBool();
682 proxyManager->setProxyConfiguration(proxyConf);
684 if (hasKey(u"proxy_bittorrent"_s))
685 pref->setUseProxyForBT(it.value().toBool());
686 if (hasKey(u"proxy_peer_connections"_s))
687 session->setProxyPeerConnectionsEnabled(it.value().toBool());
688 if (hasKey(u"proxy_rss"_s))
689 pref->setUseProxyForRSS(it.value().toBool());
690 if (hasKey(u"proxy_misc"_s))
691 pref->setUseProxyForGeneralPurposes(it.value().toBool());
693 // IP Filtering
694 if (hasKey(u"ip_filter_enabled"_s))
695 session->setIPFilteringEnabled(it.value().toBool());
696 if (hasKey(u"ip_filter_path"_s))
697 session->setIPFilterFile(Path(it.value().toString()));
698 if (hasKey(u"ip_filter_trackers"_s))
699 session->setTrackerFilteringEnabled(it.value().toBool());
700 if (hasKey(u"banned_IPs"_s))
701 session->setBannedIPs(it.value().toString().split(u'\n', Qt::SkipEmptyParts));
703 // Speed
704 // Global Rate Limits
705 if (hasKey(u"dl_limit"_s))
706 session->setGlobalDownloadSpeedLimit(it.value().toInt());
707 if (hasKey(u"up_limit"_s))
708 session->setGlobalUploadSpeedLimit(it.value().toInt());
709 if (hasKey(u"alt_dl_limit"_s))
710 session->setAltGlobalDownloadSpeedLimit(it.value().toInt());
711 if (hasKey(u"alt_up_limit"_s))
712 session->setAltGlobalUploadSpeedLimit(it.value().toInt());
713 if (hasKey(u"bittorrent_protocol"_s))
714 session->setBTProtocol(static_cast<BitTorrent::BTProtocol>(it.value().toInt()));
715 if (hasKey(u"limit_utp_rate"_s))
716 session->setUTPRateLimited(it.value().toBool());
717 if (hasKey(u"limit_tcp_overhead"_s))
718 session->setIncludeOverheadInLimits(it.value().toBool());
719 if (hasKey(u"limit_lan_peers"_s))
720 session->setIgnoreLimitsOnLAN(!it.value().toBool());
721 // Scheduling
722 if (hasKey(u"scheduler_enabled"_s))
723 session->setBandwidthSchedulerEnabled(it.value().toBool());
724 if (m.contains(u"schedule_from_hour"_s) && m.contains(u"schedule_from_min"_s))
725 pref->setSchedulerStartTime(QTime(m[u"schedule_from_hour"_s].toInt(), m[u"schedule_from_min"_s].toInt()));
726 if (m.contains(u"schedule_to_hour"_s) && m.contains(u"schedule_to_min"_s))
727 pref->setSchedulerEndTime(QTime(m[u"schedule_to_hour"_s].toInt(), m[u"schedule_to_min"_s].toInt()));
728 if (hasKey(u"scheduler_days"_s))
729 pref->setSchedulerDays(static_cast<Scheduler::Days>(it.value().toInt()));
731 // Bittorrent
732 // Privacy
733 if (hasKey(u"dht"_s))
734 session->setDHTEnabled(it.value().toBool());
735 if (hasKey(u"pex"_s))
736 session->setPeXEnabled(it.value().toBool());
737 if (hasKey(u"lsd"_s))
738 session->setLSDEnabled(it.value().toBool());
739 if (hasKey(u"encryption"_s))
740 session->setEncryption(it.value().toInt());
741 if (hasKey(u"anonymous_mode"_s))
742 session->setAnonymousModeEnabled(it.value().toBool());
743 // Max active checking torrents
744 if (hasKey(u"max_active_checking_torrents"_s))
745 session->setMaxActiveCheckingTorrents(it.value().toInt());
746 // Torrent Queueing
747 if (hasKey(u"queueing_enabled"_s))
748 session->setQueueingSystemEnabled(it.value().toBool());
749 if (hasKey(u"max_active_downloads"_s))
750 session->setMaxActiveDownloads(it.value().toInt());
751 if (hasKey(u"max_active_torrents"_s))
752 session->setMaxActiveTorrents(it.value().toInt());
753 if (hasKey(u"max_active_uploads"_s))
754 session->setMaxActiveUploads(it.value().toInt());
755 if (hasKey(u"dont_count_slow_torrents"_s))
756 session->setIgnoreSlowTorrentsForQueueing(it.value().toBool());
757 if (hasKey(u"slow_torrent_dl_rate_threshold"_s))
758 session->setDownloadRateForSlowTorrents(it.value().toInt());
759 if (hasKey(u"slow_torrent_ul_rate_threshold"_s))
760 session->setUploadRateForSlowTorrents(it.value().toInt());
761 if (hasKey(u"slow_torrent_inactive_timer"_s))
762 session->setSlowTorrentsInactivityTimer(it.value().toInt());
763 // Share Ratio Limiting
764 if (hasKey(u"max_ratio_enabled"_s))
766 if (it.value().toBool())
767 session->setGlobalMaxRatio(m[u"max_ratio"_s].toReal());
768 else
769 session->setGlobalMaxRatio(-1);
771 if (hasKey(u"max_seeding_time_enabled"_s))
773 if (it.value().toBool())
774 session->setGlobalMaxSeedingMinutes(m[u"max_seeding_time"_s].toInt());
775 else
776 session->setGlobalMaxSeedingMinutes(-1);
778 if (hasKey(u"max_inactive_seeding_time_enabled"_s))
780 session->setGlobalMaxInactiveSeedingMinutes(it.value().toBool()
781 ? m[u"max_inactive_seeding_time"_s].toInt() : -1);
783 if (hasKey(u"max_ratio_act"_s))
784 session->setMaxRatioAction(static_cast<MaxRatioAction>(it.value().toInt()));
785 // Add trackers
786 if (hasKey(u"add_trackers_enabled"_s))
787 session->setAddTrackersEnabled(it.value().toBool());
788 if (hasKey(u"add_trackers"_s))
789 session->setAdditionalTrackers(it.value().toString());
791 // WebUI
792 // HTTP Server
793 if (hasKey(u"web_ui_domain_list"_s))
794 pref->setServerDomains(it.value().toString());
795 if (hasKey(u"web_ui_address"_s))
796 pref->setWebUIAddress(it.value().toString());
797 if (hasKey(u"web_ui_port"_s))
798 pref->setWebUIPort(it.value().value<quint16>());
799 if (hasKey(u"web_ui_upnp"_s))
800 pref->setUPnPForWebUIPort(it.value().toBool());
801 if (hasKey(u"use_https"_s))
802 pref->setWebUIHttpsEnabled(it.value().toBool());
803 if (hasKey(u"web_ui_https_cert_path"_s))
804 pref->setWebUIHttpsCertificatePath(Path(it.value().toString()));
805 if (hasKey(u"web_ui_https_key_path"_s))
806 pref->setWebUIHttpsKeyPath(Path(it.value().toString()));
807 // Authentication
808 if (hasKey(u"web_ui_username"_s))
809 pref->setWebUIUsername(it.value().toString());
810 if (hasKey(u"web_ui_password"_s))
811 pref->setWebUIPassword(Utils::Password::PBKDF2::generate(it.value().toByteArray()));
812 if (hasKey(u"bypass_local_auth"_s))
813 pref->setWebUILocalAuthEnabled(!it.value().toBool());
814 if (hasKey(u"bypass_auth_subnet_whitelist_enabled"_s))
815 pref->setWebUIAuthSubnetWhitelistEnabled(it.value().toBool());
816 if (hasKey(u"bypass_auth_subnet_whitelist"_s))
818 // recognize new lines and commas as delimiters
819 pref->setWebUIAuthSubnetWhitelist(it.value().toString().split(QRegularExpression(u"\n|,"_s), Qt::SkipEmptyParts));
821 if (hasKey(u"web_ui_max_auth_fail_count"_s))
822 pref->setWebUIMaxAuthFailCount(it.value().toInt());
823 if (hasKey(u"web_ui_ban_duration"_s))
824 pref->setWebUIBanDuration(std::chrono::seconds {it.value().toInt()});
825 if (hasKey(u"web_ui_session_timeout"_s))
826 pref->setWebUISessionTimeout(it.value().toInt());
827 // Use alternative WebUI
828 if (hasKey(u"alternative_webui_enabled"_s))
829 pref->setAltWebUIEnabled(it.value().toBool());
830 if (hasKey(u"alternative_webui_path"_s))
831 pref->setWebUIRootFolder(Path(it.value().toString()));
832 // Security
833 if (hasKey(u"web_ui_clickjacking_protection_enabled"_s))
834 pref->setWebUIClickjackingProtectionEnabled(it.value().toBool());
835 if (hasKey(u"web_ui_csrf_protection_enabled"_s))
836 pref->setWebUICSRFProtectionEnabled(it.value().toBool());
837 if (hasKey(u"web_ui_secure_cookie_enabled"_s))
838 pref->setWebUISecureCookieEnabled(it.value().toBool());
839 if (hasKey(u"web_ui_host_header_validation_enabled"_s))
840 pref->setWebUIHostHeaderValidationEnabled(it.value().toBool());
841 // Custom HTTP headers
842 if (hasKey(u"web_ui_use_custom_http_headers_enabled"_s))
843 pref->setWebUICustomHTTPHeadersEnabled(it.value().toBool());
844 if (hasKey(u"web_ui_custom_http_headers"_s))
845 pref->setWebUICustomHTTPHeaders(it.value().toString());
846 // Reverse proxy
847 if (hasKey(u"web_ui_reverse_proxy_enabled"_s))
848 pref->setWebUIReverseProxySupportEnabled(it.value().toBool());
849 if (hasKey(u"web_ui_reverse_proxies_list"_s))
850 pref->setWebUITrustedReverseProxiesList(it.value().toString());
851 // Update my dynamic domain name
852 if (hasKey(u"dyndns_enabled"_s))
853 pref->setDynDNSEnabled(it.value().toBool());
854 if (hasKey(u"dyndns_service"_s))
855 pref->setDynDNSService(static_cast<DNS::Service>(it.value().toInt()));
856 if (hasKey(u"dyndns_username"_s))
857 pref->setDynDNSUsername(it.value().toString());
858 if (hasKey(u"dyndns_password"_s))
859 pref->setDynDNSPassword(it.value().toString());
860 if (hasKey(u"dyndns_domain"_s))
861 pref->setDynDomainName(it.value().toString());
863 if (hasKey(u"rss_refresh_interval"_s))
864 RSS::Session::instance()->setRefreshInterval(it.value().toInt());
865 if (hasKey(u"rss_max_articles_per_feed"_s))
866 RSS::Session::instance()->setMaxArticlesPerFeed(it.value().toInt());
867 if (hasKey(u"rss_processing_enabled"_s))
868 RSS::Session::instance()->setProcessingEnabled(it.value().toBool());
869 if (hasKey(u"rss_auto_downloading_enabled"_s))
870 RSS::AutoDownloader::instance()->setProcessingEnabled(it.value().toBool());
871 if (hasKey(u"rss_download_repack_proper_episodes"_s))
872 RSS::AutoDownloader::instance()->setDownloadRepacks(it.value().toBool());
873 if (hasKey(u"rss_smart_episode_filters"_s))
874 RSS::AutoDownloader::instance()->setSmartEpisodeFilters(it.value().toString().split(u'\n'));
876 // Advanced settings
877 // qBittorrent preferences
878 // Resume data storage type
879 if (hasKey(u"resume_data_storage_type"_s))
880 session->setResumeDataStorageType(Utils::String::toEnum(it.value().toString(), BitTorrent::ResumeDataStorageType::Legacy));
881 // Physical memory (RAM) usage limit
882 if (hasKey(u"memory_working_set_limit"_s))
883 app()->setMemoryWorkingSetLimit(it.value().toInt());
884 // Current network interface
885 if (hasKey(u"current_network_interface"_s))
887 const QString ifaceValue {it.value().toString()};
889 const QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
890 const auto ifacesIter = std::find_if(ifaces.cbegin(), ifaces.cend(), [&ifaceValue](const QNetworkInterface &iface)
892 return (!iface.addressEntries().isEmpty()) && (iface.name() == ifaceValue);
894 const QString ifaceName = (ifacesIter != ifaces.cend()) ? ifacesIter->humanReadableName() : QString {};
896 session->setNetworkInterface(ifaceValue);
897 if (!ifaceName.isEmpty() || ifaceValue.isEmpty())
898 session->setNetworkInterfaceName(ifaceName);
900 // Current network interface address
901 if (hasKey(u"current_interface_address"_s))
903 const QHostAddress ifaceAddress {it.value().toString().trimmed()};
904 session->setNetworkInterfaceAddress(ifaceAddress.isNull() ? QString {} : ifaceAddress.toString());
906 // Save resume data interval
907 if (hasKey(u"save_resume_data_interval"_s))
908 session->setSaveResumeDataInterval(it.value().toInt());
909 // .torrent file size limit
910 if (hasKey(u"torrent_file_size_limit"_s))
911 pref->setTorrentFileSizeLimit(it.value().toLongLong());
912 // Recheck completed torrents
913 if (hasKey(u"recheck_completed_torrents"_s))
914 pref->recheckTorrentsOnCompletion(it.value().toBool());
915 // Refresh interval
916 if (hasKey(u"refresh_interval"_s))
917 session->setRefreshInterval(it.value().toInt());
918 // Resolve peer countries
919 if (hasKey(u"resolve_peer_countries"_s))
920 pref->resolvePeerCountries(it.value().toBool());
921 // Reannounce to all trackers when ip/port changed
922 if (hasKey(u"reannounce_when_address_changed"_s))
923 session->setReannounceWhenAddressChangedEnabled(it.value().toBool());
925 // libtorrent preferences
926 // Bdecode depth limit
927 if (hasKey(u"bdecode_depth_limit"_s))
928 pref->setBdecodeDepthLimit(it.value().toInt());
929 // Bdecode token limit
930 if (hasKey(u"bdecode_token_limit"_s))
931 pref->setBdecodeTokenLimit(it.value().toInt());
932 // Async IO threads
933 if (hasKey(u"async_io_threads"_s))
934 session->setAsyncIOThreads(it.value().toInt());
935 // Hashing threads
936 if (hasKey(u"hashing_threads"_s))
937 session->setHashingThreads(it.value().toInt());
938 // File pool size
939 if (hasKey(u"file_pool_size"_s))
940 session->setFilePoolSize(it.value().toInt());
941 // Checking Memory Usage
942 if (hasKey(u"checking_memory_use"_s))
943 session->setCheckingMemUsage(it.value().toInt());
944 // Disk write cache
945 if (hasKey(u"disk_cache"_s))
946 session->setDiskCacheSize(it.value().toInt());
947 if (hasKey(u"disk_cache_ttl"_s))
948 session->setDiskCacheTTL(it.value().toInt());
949 // Disk queue size
950 if (hasKey(u"disk_queue_size"_s))
951 session->setDiskQueueSize(it.value().toLongLong());
952 // Disk IO Type
953 if (hasKey(u"disk_io_type"_s))
954 session->setDiskIOType(static_cast<BitTorrent::DiskIOType>(it.value().toInt()));
955 // Disk IO read mode
956 if (hasKey(u"disk_io_read_mode"_s))
957 session->setDiskIOReadMode(static_cast<BitTorrent::DiskIOReadMode>(it.value().toInt()));
958 // Disk IO write mode
959 if (hasKey(u"disk_io_write_mode"_s))
960 session->setDiskIOWriteMode(static_cast<BitTorrent::DiskIOWriteMode>(it.value().toInt()));
961 // Coalesce reads & writes
962 if (hasKey(u"enable_coalesce_read_write"_s))
963 session->setCoalesceReadWriteEnabled(it.value().toBool());
964 // Piece extent affinity
965 if (hasKey(u"enable_piece_extent_affinity"_s))
966 session->setPieceExtentAffinity(it.value().toBool());
967 // Suggest mode
968 if (hasKey(u"enable_upload_suggestions"_s))
969 session->setSuggestMode(it.value().toBool());
970 // Send buffer watermark
971 if (hasKey(u"send_buffer_watermark"_s))
972 session->setSendBufferWatermark(it.value().toInt());
973 if (hasKey(u"send_buffer_low_watermark"_s))
974 session->setSendBufferLowWatermark(it.value().toInt());
975 if (hasKey(u"send_buffer_watermark_factor"_s))
976 session->setSendBufferWatermarkFactor(it.value().toInt());
977 // Outgoing connections per second
978 if (hasKey(u"connection_speed"_s))
979 session->setConnectionSpeed(it.value().toInt());
980 // Socket send buffer size
981 if (hasKey(u"socket_send_buffer_size"_s))
982 session->setSocketSendBufferSize(it.value().toInt());
983 // Socket receive buffer size
984 if (hasKey(u"socket_receive_buffer_size"_s))
985 session->setSocketReceiveBufferSize(it.value().toInt());
986 // Socket listen backlog size
987 if (hasKey(u"socket_backlog_size"_s))
988 session->setSocketBacklogSize(it.value().toInt());
989 // Outgoing ports
990 if (hasKey(u"outgoing_ports_min"_s))
991 session->setOutgoingPortsMin(it.value().toInt());
992 if (hasKey(u"outgoing_ports_max"_s))
993 session->setOutgoingPortsMax(it.value().toInt());
994 // UPnP lease duration
995 if (hasKey(u"upnp_lease_duration"_s))
996 session->setUPnPLeaseDuration(it.value().toInt());
997 // Type of service
998 if (hasKey(u"peer_tos"_s))
999 session->setPeerToS(it.value().toInt());
1000 // uTP-TCP mixed mode
1001 if (hasKey(u"utp_tcp_mixed_mode"_s))
1002 session->setUtpMixedMode(static_cast<BitTorrent::MixedModeAlgorithm>(it.value().toInt()));
1003 // Support internationalized domain name (IDN)
1004 if (hasKey(u"idn_support_enabled"_s))
1005 session->setIDNSupportEnabled(it.value().toBool());
1006 // Multiple connections per IP
1007 if (hasKey(u"enable_multi_connections_from_same_ip"_s))
1008 session->setMultiConnectionsPerIpEnabled(it.value().toBool());
1009 // Validate HTTPS tracker certificate
1010 if (hasKey(u"validate_https_tracker_certificate"_s))
1011 session->setValidateHTTPSTrackerCertificate(it.value().toBool());
1012 // SSRF mitigation
1013 if (hasKey(u"ssrf_mitigation"_s))
1014 session->setSSRFMitigationEnabled(it.value().toBool());
1015 // Disallow connection to peers on privileged ports
1016 if (hasKey(u"block_peers_on_privileged_ports"_s))
1017 session->setBlockPeersOnPrivilegedPorts(it.value().toBool());
1018 // Embedded tracker
1019 if (hasKey(u"embedded_tracker_port"_s))
1020 pref->setTrackerPort(it.value().toInt());
1021 if (hasKey(u"embedded_tracker_port_forwarding"_s))
1022 pref->setTrackerPortForwardingEnabled(it.value().toBool());
1023 if (hasKey(u"enable_embedded_tracker"_s))
1024 session->setTrackerEnabled(it.value().toBool());
1025 // Mark-of-the-Web
1026 if (hasKey(u"mark_of_the_web"_s))
1027 pref->setMarkOfTheWebEnabled(it.value().toBool());
1028 // Python executable path
1029 if (hasKey(u"python_executable_path"_s))
1030 pref->setPythonExecutablePath(Path(it.value().toString()));
1031 // Choking algorithm
1032 if (hasKey(u"upload_slots_behavior"_s))
1033 session->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm>(it.value().toInt()));
1034 // Seed choking algorithm
1035 if (hasKey(u"upload_choking_algorithm"_s))
1036 session->setSeedChokingAlgorithm(static_cast<BitTorrent::SeedChokingAlgorithm>(it.value().toInt()));
1037 // Announce
1038 if (hasKey(u"announce_to_all_trackers"_s))
1039 session->setAnnounceToAllTrackers(it.value().toBool());
1040 if (hasKey(u"announce_to_all_tiers"_s))
1041 session->setAnnounceToAllTiers(it.value().toBool());
1042 if (hasKey(u"announce_ip"_s))
1044 const QHostAddress announceAddr {it.value().toString().trimmed()};
1045 session->setAnnounceIP(announceAddr.isNull() ? QString {} : announceAddr.toString());
1047 if (hasKey(u"max_concurrent_http_announces"_s))
1048 session->setMaxConcurrentHTTPAnnounces(it.value().toInt());
1049 if (hasKey(u"stop_tracker_timeout"_s))
1050 session->setStopTrackerTimeout(it.value().toInt());
1051 // Peer Turnover
1052 if (hasKey(u"peer_turnover"_s))
1053 session->setPeerTurnover(it.value().toInt());
1054 if (hasKey(u"peer_turnover_cutoff"_s))
1055 session->setPeerTurnoverCutoff(it.value().toInt());
1056 if (hasKey(u"peer_turnover_interval"_s))
1057 session->setPeerTurnoverInterval(it.value().toInt());
1058 // Maximum outstanding requests to a single peer
1059 if (hasKey(u"request_queue_size"_s))
1060 session->setRequestQueueSize(it.value().toInt());
1061 // DHT bootstrap nodes
1062 if (hasKey(u"dht_bootstrap_nodes"_s))
1063 session->setDHTBootstrapNodes(it.value().toString());
1065 // Save preferences
1066 pref->apply();
1069 void AppController::defaultSavePathAction()
1071 setResult(BitTorrent::Session::instance()->savePath().toString());
1074 void AppController::networkInterfaceListAction()
1076 QJsonArray ifaceList;
1077 for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces()))
1079 if (!iface.addressEntries().isEmpty())
1081 ifaceList.append(QJsonObject
1083 {u"name"_s, iface.humanReadableName()},
1084 {u"value"_s, iface.name()}
1089 setResult(ifaceList);
1092 void AppController::networkInterfaceAddressListAction()
1094 requireParams({u"iface"_s});
1096 const QString ifaceName = params().value(u"iface"_s);
1097 QJsonArray addressList;
1099 const auto appendAddress = [&addressList](const QHostAddress &addr)
1101 if (addr.protocol() == QAbstractSocket::IPv6Protocol)
1102 addressList.append(Utils::Net::canonicalIPv6Addr(addr).toString());
1103 else
1104 addressList.append(addr.toString());
1107 if (ifaceName.isEmpty())
1109 for (const QHostAddress &addr : asConst(QNetworkInterface::allAddresses()))
1110 appendAddress(addr);
1112 else
1114 const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
1115 for (const QNetworkAddressEntry &entry : asConst(iface.addressEntries()))
1116 appendAddress(entry.ip());
1119 setResult(addressList);