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"
36 #include <QCoreApplication>
39 #include <QJsonDocument>
40 #include <QJsonObject>
41 #include <QNetworkInterface>
42 #include <QRegularExpression>
43 #include <QStringList>
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)}
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();
112 data
[u
"locale"_s
] = pref
->getLocale();
113 data
[u
"performance_warning"_s
] = session
->isPerformanceWarningEnabled();
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();
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();
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);
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();
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();
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();
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();
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');
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();
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());
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();
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();
275 data
[u
"add_trackers_enabled"_s
] = session
->isAddTrackersEnabled();
276 data
[u
"add_trackers"_s
] = session
->additionalTrackers();
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();
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();
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();
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();
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');
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();
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();
358 data
[u
"async_io_threads"_s
] = session
->asyncIOThreads();
360 data
[u
"hashing_threads"_s
] = session
->hashingThreads();
362 data
[u
"file_pool_size"_s
] = session
->filePoolSize();
363 // Checking memory usage
364 data
[u
"checking_memory_use"_s
] = session
->checkingMemUsage();
366 data
[u
"disk_cache"_s
] = session
->diskCacheSize();
367 data
[u
"disk_cache_ttl"_s
] = session
->diskCacheTTL();
369 data
[u
"disk_queue_size"_s
] = session
->diskQueueSize();
371 data
[u
"disk_io_type"_s
] = static_cast<int>(session
->diskIOType());
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();
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();
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();
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();
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();
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();
418 data
[u
"mark_of_the_web"_s
] = pref
->isMarkOfTheWebEnabled();
419 // Python executable path
420 data
[u
"python_executable_path"_s
] = pref
->getPythonExecutablePath().toString();
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());
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();
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();
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
455 return (it
!= m
.constEnd());
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
));
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());
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());
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());
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
))
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
¶ms
= options
.addTorrentParams
;
558 const int intVal
= i
.value().toInt(&isInt
);
563 params
.savePath
= watchedFolder
;
564 params
.useAutoTMM
= false;
569 const Path customSavePath
{i
.value().toString()};
570 params
.savePath
= customSavePath
;
571 params
.useAutoTMM
= false;
574 fsWatcher
->setWatchedFolder(watchedFolder
, options
);
575 scanDirs
.append(watchedFolder
);
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());
627 if (hasKey(u
"random_port"_s
) && it
.value().toBool()) // deprecated
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());
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());
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());
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
));
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());
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()));
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());
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());
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());
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()));
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());
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()));
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()));
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());
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'));
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());
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());
933 if (hasKey(u
"async_io_threads"_s
))
934 session
->setAsyncIOThreads(it
.value().toInt());
936 if (hasKey(u
"hashing_threads"_s
))
937 session
->setHashingThreads(it
.value().toInt());
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());
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());
950 if (hasKey(u
"disk_queue_size"_s
))
951 session
->setDiskQueueSize(it
.value().toLongLong());
953 if (hasKey(u
"disk_io_type"_s
))
954 session
->setDiskIOType(static_cast<BitTorrent::DiskIOType
>(it
.value().toInt()));
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());
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());
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());
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());
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());
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());
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()));
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());
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());
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());
1104 addressList
.append(addr
.toString());
1107 if (ifaceName
.isEmpty())
1109 for (const QHostAddress
&addr
: asConst(QNetworkInterface::allAddresses()))
1110 appendAddress(addr
);
1114 const QNetworkInterface iface
= QNetworkInterface::interfaceFromName(ifaceName
);
1115 for (const QNetworkAddressEntry
&entry
: asConst(iface
.addressEntries()))
1116 appendAddress(entry
.ip());
1119 setResult(addressList
);